From 699792daeb57bbbe18f142a13f1e6ba579d65851 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 23 Jan 2019 06:26:20 -0500 Subject: [PATCH 01/16] state.md additions --- docs/spec/staking/state.md | 77 ++++++++++++++++++++++-- x/staking/keeper/keeper.go | 42 ------------- x/staking/keeper/key.go | 2 +- x/staking/keeper/validator.go | 108 ++++++++++++++++++++++++---------- 4 files changed, 151 insertions(+), 78 deletions(-) diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index 5e8f4d327769..069bb359f951 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -17,6 +17,13 @@ type Pool struct { } ``` +## LastTotalPower + +LastTotalPower tracks the total amounts of bonded tokens recorded during the previous +end block. + + - LastTotalPower: `0x12 -> amino(sdk.Int)` + ## Params Params is a module-wide configuration structure that stores system parameters @@ -36,27 +43,36 @@ type Params struct { Validators objects should be primarily stored and accessed by the `OperatorAddr`, an SDK validator address for the operator of the validator. Two -additional indexes are maintained in order to fulfill required lookups for -slashing and validator-set updates. +additional indexes are maintained per validator object in order to fulfill +required lookups for slashing and validator-set updates. A third special index +(`LastValidatorPower`) is also maintained which however remains constant +throughout each block, unlike the first two indexes which mirror the validator +records within a block. - Validators: `0x21 | OperatorAddr -> amino(validator)` - ValidatorsByConsAddr: `0x22 | ConsAddr -> OperatorAddr` - ValidatorsByPower: `0x23 | BigEndian(Tokens) | OperatorAddr -> OperatorAddr` +- LastValidatorsPower: `0x11 OperatorAddr -> amino(Tokens) `Validators` is the primary index - it ensures that each operator can have only one associated validator, where the public key of that validator can change in the future. Delegators can refer to the immutable operator of the validator, without concern for the changing public key. -`ValidatorByConsAddr` is a secondary index that enables lookups for slashing. +`ValidatorByConsAddr` is an additional index that enables lookups for slashing. When Tendermint reports evidence, it provides the validator address, so this map is needed to find the operator. Note that the `ConsAddr` corresponds to the address which can be derived from the validator's `ConsPubKey`. -`ValidatorsByPower` is a secondary index that provides a sorted list of +`ValidatorsByPower` is an additional index that provides a sorted list o potential validators to quickly determine the current active set. Note that all validators where `Jailed` is true are not stored within this index. +`LastValidatorsPower` is a special index that provides a historical list of the +last-block's bonded validators. This index remains constant during a block but +is updated during the validator set update process which takes place in [end +block](end_block.md). + Each validator's state is stored in a `Validator` struct: ```golang @@ -190,3 +206,56 @@ type RedelegationEntry struct { SharesDst sdk.Dec // amount of destination-validator shares created by redelegation } ``` + +## Queues + +All queues objects are sorted by timestamp. The time used within any queue is +first rounded to the nearest nanosecond then sorted. The sortable time format +used is a slight modification of the RFC3339Nano and uses the the format string +`"2006-01-02T15:04:05.000000000"`. Noteably This format: + - right pads all zeros + - drops the time zone info (uses UTC) + +In all cases, the stored timestamp represents the maturation time of the queue +element. + +### UnbondingDelegationQueue + +For the purpose of tracking progress of unbonding delegations the unbonding +delegations queue is kept. + +- UnbondingDelegation: `0x41 | format(time) -> []DVPair` + +``` +type DVPair struct { + DelegatorAddr sdk.AccAddress + ValidatorAddr sdk.ValAddress +} +``` + +### RedelegationQueue + +For the purpose of tracking progress of redelegations the redelegation queue is +kept. + +- UnbondingDelegation: `0x42 | format(time) -> []DVVTriplet` + +``` +type DVVTriplet struct { + DelegatorAddr sdk.AccAddress + ValidatorSrcAddr sdk.ValAddress + ValidatorDstAddr sdk.ValAddress +} +``` + +### ValidatorQueue + +For the purpose of tracking progress of unbonding validators the validator +queue is kept. + +- ValidatorQueueTime: `0x43 | format(time) -> []sdk.ValAddress` + +The stored object as each key is an array of validator operator addresses from +which the validator object can be accessed. Typically it is expected that only +a single validator record will operate a given timestamp however it is possible +that multiple validators exist in the queue at the same location. diff --git a/x/staking/keeper/keeper.go b/x/staking/keeper/keeper.go index ea3527d5d633..d085f1f0fdb6 100644 --- a/x/staking/keeper/keeper.go +++ b/x/staking/keeper/keeper.go @@ -98,45 +98,3 @@ func (k Keeper) SetLastTotalPower(ctx sdk.Context, power sdk.Int) { b := k.cdc.MustMarshalBinaryLengthPrefixed(power) store.Set(LastTotalPowerKey, b) } - -//_______________________________________________________________________ - -// Load the last validator power. -// Returns zero if the operator was not a validator last block. -func (k Keeper) GetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) (power sdk.Int) { - store := ctx.KVStore(k.storeKey) - bz := store.Get(GetLastValidatorPowerKey(operator)) - if bz == nil { - return sdk.ZeroInt() - } - k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &power) - return -} - -// Set the last validator power. -func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, power sdk.Int) { - store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshalBinaryLengthPrefixed(power) - store.Set(GetLastValidatorPowerKey(operator), bz) -} - -// Iterate over last validator powers. -func (k Keeper) IterateLastValidatorPowers(ctx sdk.Context, handler func(operator sdk.ValAddress, power sdk.Int) (stop bool)) { - store := ctx.KVStore(k.storeKey) - iter := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) - defer iter.Close() - for ; iter.Valid(); iter.Next() { - addr := sdk.ValAddress(iter.Key()[len(LastValidatorPowerKey):]) - var power sdk.Int - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &power) - if handler(addr, power) { - break - } - } -} - -// Delete the last validator power. -func (k Keeper) DeleteLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) { - store := ctx.KVStore(k.storeKey) - store.Delete(GetLastValidatorPowerKey(operator)) -} diff --git a/x/staking/keeper/key.go b/x/staking/keeper/key.go index 6dbb053cf688..c8a5210bcb2b 100644 --- a/x/staking/keeper/key.go +++ b/x/staking/keeper/key.go @@ -17,7 +17,7 @@ var ( // ParamKey = []byte{0x00} // key for parameters relating to stake PoolKey = []byte{0x01} // key for the staking pools - // Last* values are const during a block. + // Last* values are constant during a block. LastValidatorPowerKey = []byte{0x11} // prefix for each key to a validator index, for bonded validators LastTotalPowerKey = []byte{0x12} // prefix for the total power diff --git a/x/staking/keeper/validator.go b/x/staking/keeper/validator.go index d218ca966c75..20f82fd52004 100644 --- a/x/staking/keeper/validator.go +++ b/x/staking/keeper/validator.go @@ -240,33 +240,63 @@ func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve uint16) (validators [ return validators[:i] // trim if the array length < maxRetrieve } -// get the group of the bonded validators -func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator) { +// get the current group of bonded validators sorted by power-rank +func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator { store := ctx.KVStore(k.storeKey) - - // add the actual validator power sorted store maxValidators := k.MaxValidators(ctx) - validators = make([]types.Validator, maxValidators) + validators := make([]types.Validator, maxValidators) - iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) + iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) defer iterator.Close() i := 0 - for ; iterator.Valid(); iterator.Next() { - - // sanity check - if i >= int(maxValidators) { - panic("more validators than maxValidators found") - } - address := AddressFromLastValidatorPowerKey(iterator.Key()) + for ; iterator.Valid() && i < int(maxValidators); iterator.Next() { + address := iterator.Value() validator := k.mustGetValidator(ctx, address) - validators[i] = validator - i++ + if validator.Status == sdk.Bonded { + validators[i] = validator + i++ + } } return validators[:i] // trim } +// returns an iterator for the current validator power store +func (k Keeper) ValidatorsPowerStoreIterator(ctx sdk.Context) (iterator sdk.Iterator) { + store := ctx.KVStore(k.storeKey) + iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) + return iterator +} + +//_______________________________________________________________________ +// Last Validator Index + +// Load the last validator power. +// Returns zero if the operator was not a validator last block. +func (k Keeper) GetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) (power sdk.Int) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(GetLastValidatorPowerKey(operator)) + if bz == nil { + return sdk.ZeroInt() + } + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &power) + return +} + +// Set the last validator power. +func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, power sdk.Int) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryLengthPrefixed(power) + store.Set(GetLastValidatorPowerKey(operator), bz) +} + +// Delete the last validator power. +func (k Keeper) DeleteLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) { + store := ctx.KVStore(k.storeKey) + store.Delete(GetLastValidatorPowerKey(operator)) +} + // returns an iterator for the consensus validators in the last block func (k Keeper) LastValidatorsIterator(ctx sdk.Context) (iterator sdk.Iterator) { store := ctx.KVStore(k.storeKey) @@ -274,34 +304,50 @@ func (k Keeper) LastValidatorsIterator(ctx sdk.Context) (iterator sdk.Iterator) return iterator } -// get the current group of bonded validators sorted by power-rank -func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator { +// Iterate over last validator powers. +func (k Keeper) IterateLastValidatorPowers(ctx sdk.Context, handler func(operator sdk.ValAddress, power sdk.Int) (stop bool)) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + addr := sdk.ValAddress(iter.Key()[len(LastValidatorPowerKey):]) + var power sdk.Int + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &power) + if handler(addr, power) { + break + } + } +} + +// get the group of the bonded validators +func (k Keeper) GetLastValidators(ctx sdk.Context) (validators []types.Validator) { store := ctx.KVStore(k.storeKey) + + // add the actual validator power sorted store maxValidators := k.MaxValidators(ctx) - validators := make([]types.Validator, maxValidators) + validators = make([]types.Validator, maxValidators) - iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) + iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) defer iterator.Close() i := 0 - for ; iterator.Valid() && i < int(maxValidators); iterator.Next() { - address := iterator.Value() - validator := k.mustGetValidator(ctx, address) + for ; iterator.Valid(); iterator.Next() { - if validator.Status == sdk.Bonded { - validators[i] = validator - i++ + // sanity check + if i >= int(maxValidators) { + panic("more validators than maxValidators found") } + address := AddressFromLastValidatorPowerKey(iterator.Key()) + validator := k.mustGetValidator(ctx, address) + + validators[i] = validator + i++ } return validators[:i] // trim } -// returns an iterator for the current validator power store -func (k Keeper) ValidatorsPowerStoreIterator(ctx sdk.Context) (iterator sdk.Iterator) { - store := ctx.KVStore(k.storeKey) - iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) - return iterator -} +//_______________________________________________________________________ +// Validator Queue // gets a specific validator queue timeslice. A timeslice is a slice of ValAddresses corresponding to unbonding validators // that expire at a certain time. From 435c7e045b0e653d6e0316bcaea7172c96fd473a Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 23 Jan 2019 06:43:46 -0500 Subject: [PATCH 02/16] working state transitions --- docs/spec/SPEC-SPEC.md | 2 +- docs/spec/staking/TODO.md | 7 ------- docs/spec/staking/overview.md | 5 +++++ docs/spec/staking/state_transitions.md | 16 ++++++++++++++++ 4 files changed, 22 insertions(+), 8 deletions(-) delete mode 100644 docs/spec/staking/TODO.md create mode 100644 docs/spec/staking/state_transitions.md diff --git a/docs/spec/SPEC-SPEC.md b/docs/spec/SPEC-SPEC.md index e53fe74b3240..5b65833c796b 100644 --- a/docs/spec/SPEC-SPEC.md +++ b/docs/spec/SPEC-SPEC.md @@ -15,7 +15,7 @@ function - `overview.md` - describe module - `state.md` - specify and describe structures expected to marshalled into the store, and their keys - - `state_transitions.md` - standard state transition operations triggered by by hooks, messages, etc. + - `state_transitions.md` - standard state transition operations triggered by hooks, messages, etc. - `end_block.md` - specify any end-block operations - `begin_block.md` - specify any begin-block operations - `messages.md` - specify message structure and expected state machine behaviour diff --git a/docs/spec/staking/TODO.md b/docs/spec/staking/TODO.md deleted file mode 100644 index efef4991c07d..000000000000 --- a/docs/spec/staking/TODO.md +++ /dev/null @@ -1,7 +0,0 @@ - - - `state.md` needs updates to include - - LastValidatorPower - - LastTotalPower - - state for the queues: UnbondingDelegation, UnbondingValidator, Redelegation - - introduce `state_transitions.md` to describe validator/delegator state - transitions which are called from transactions diff --git a/docs/spec/staking/overview.md b/docs/spec/staking/overview.md index 02a0b3b02bbb..752c9b616fba 100644 --- a/docs/spec/staking/overview.md +++ b/docs/spec/staking/overview.md @@ -27,6 +27,11 @@ native staking token of the chain. - Delegation - UnbondingDelegation - Redelegation + - Queues + 2. **[State Transistions](state_transitions.md)** + - Validator + - Delegation + - Pool 2. **[Messages](messages.md)** - MsgCreateValidator - MsgEditValidator diff --git a/docs/spec/staking/state_transitions.md b/docs/spec/staking/state_transitions.md new file mode 100644 index 000000000000..9965ea5fb19c --- /dev/null +++ b/docs/spec/staking/state_transitions.md @@ -0,0 +1,16 @@ +# State Transitions + +This document describes the state transition operations for: + - Validators + - Delegators + - Pool + +## Validator + + + +## Delegator + +## Pool + + From 495f7793c2097b0bbf64b78ebddbaad218f652c7 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Thu, 24 Jan 2019 20:28:27 -0500 Subject: [PATCH 03/16] refactor and high level lists for state transitions --- docs/spec/staking/end_block.md | 11 +++--- docs/spec/staking/state_transitions.md | 48 +++++++++++++++++++------- x/staking/keeper/_store.md | 43 ----------------------- x/staking/keeper/delegation.go | 6 +--- x/staking/keeper/hooks.go | 13 +++++-- x/staking/keeper/keeper.go | 4 ++- x/staking/keeper/query_utils.go | 5 ++- x/staking/keeper/val_state_change.go | 16 +++------ x/staking/keeper/validator.go | 19 ++++------ 9 files changed, 72 insertions(+), 93 deletions(-) delete mode 100644 x/staking/keeper/_store.md diff --git a/docs/spec/staking/end_block.md b/docs/spec/staking/end_block.md index a6cb30765817..c0df0bc1346c 100644 --- a/docs/spec/staking/end_block.md +++ b/docs/spec/staking/end_block.md @@ -38,11 +38,12 @@ delegated to this validator). At this point the validator is said to be an unbonding validator, whereby it will mature to become an "unbonded validator" after the unbonding period has passed. -Each block the validator queue is to be checked for mature unbonding -validators. For all unbonding validators that have finished their unbonding -period, the validator.Status is switched from sdk.Unbonding to sdk.Unbonded. -If at this switch they do not have any delegation left the validator object -instead just deleted from state. +Each block the validator queue is to be checked for mature unbonding validators +(namely with a completion time <= current time). At this point any mature +validators which do not have any delegations remaining are delete from state. +For all other mature unbonding validators that still have remaining +delegations, the validator.Status is switched from sdk.Unbonding to +sdk.Unbonded. ### Unbonding Delegations diff --git a/docs/spec/staking/state_transitions.md b/docs/spec/staking/state_transitions.md index 9965ea5fb19c..23f918235a70 100644 --- a/docs/spec/staking/state_transitions.md +++ b/docs/spec/staking/state_transitions.md @@ -1,16 +1,40 @@ # State Transitions -This document describes the state transition operations for: +This document describes the state transition operations pertaining to: - Validators - - Delegators - - Pool - -## Validator - - - -## Delegator - -## Pool - + - Delegations + - Slashing + +## Validators +RemoveValidatorTokens +RemoveValidatorTokensAndShares +AddValidatorTokensAndShares + +completeUnbondingValidator +beginUnbondingValidator +bondValidator + +bondedToUnbonding + -> beginUnbondingValidator +unbondingToBonded + -> bondValidator +unbondedToBonded + -> bondValidator +unbondingToUnbonded + -> completeUnbondingValidator + +jailValidator / unjailValidator + +## Delegations +Delegate +unbond +Undelegate +CompleteUnbonding +BeginRedelegation +CompleteRedelegation + +## Slashing +Slash +slashUnbondingDelegation +slashRedelegation diff --git a/x/staking/keeper/_store.md b/x/staking/keeper/_store.md deleted file mode 100644 index f6430c312b90..000000000000 --- a/x/staking/keeper/_store.md +++ /dev/null @@ -1,43 +0,0 @@ -# Stores - -This document provided a bit more insight as to the purpose of several related -prefixed areas of the staking store which are accessed in `x/staking/keeper.go`. - -# IAVL Store - -## Validators - - Prefix Key Space: ValidatorsKey - - Key/Sort: Validator Operator Address - - Value: Validator Object - - Contains: All Validator records independent of being bonded or not - - Used For: Retrieve validator from operator address, general validator retrieval - -## Validators By Power - - Prefix Key Space: ValidatorsByPowerKey - - Key/Sort: Validator Power (equivalent bonded shares) then Block - Height then Transaction Order - - Value: Validator Operator Address - - Contains: All Validator records independent of being bonded or not - - Used For: Determining who the top validators are whom should be bonded - -## Validators Cliff Power - - Prefix Key Space: ValidatorCliffKey - - Key/Sort: single-record - - Value: Validator Power Key (as above store) - - Contains: The cliff validator (ex. 100th validator) power - - Used For: Efficient updates to validator status - -## Validators Bonded - - Prefix Key Space: ValidatorsBondedKey - - Key/Sort: Validator PubKey Address (NOTE same as Tendermint) - - Value: Validator Operator Address - - Contains: Only currently bonded Validators - - Used For: Retrieving the list of all currently bonded validators when updating - for a new validator entering the validator set we may want to loop - through this set to determine who we've kicked out. - retrieving validator by tendermint index - -# Transient Store - -The transient store persists between transations but not between blocks - diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go index 162d87aa4d6c..1b7671e0cf11 100644 --- a/x/staking/keeper/delegation.go +++ b/x/staking/keeper/delegation.go @@ -464,11 +464,7 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Co // Get or create the delegator delegation delegation, found := k.GetDelegation(ctx, delAddr, validator.OperatorAddr) if !found { - delegation = types.Delegation{ - DelegatorAddr: delAddr, - ValidatorAddr: validator.OperatorAddr, - Shares: sdk.ZeroDec(), - } + delegation = NewDelegation(delAddr, validator.OperatorAddrsdk.ZeroDec()) } // call the appropriate hook if present diff --git a/x/staking/keeper/hooks.go b/x/staking/keeper/hooks.go index 4ed0a724c54d..6810a8a78713 100644 --- a/x/staking/keeper/hooks.go +++ b/x/staking/keeper/hooks.go @@ -1,71 +1,80 @@ -//nolint package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// Expose the hooks if present +// AfterValidatorCreated - call hook if registered func (k Keeper) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { if k.hooks != nil { k.hooks.AfterValidatorCreated(ctx, valAddr) } } +// BeforeValidatorModified - call hook if registered func (k Keeper) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) { if k.hooks != nil { k.hooks.BeforeValidatorModified(ctx, valAddr) } } +// AfterValidatorRemoved - call hook if registered func (k Keeper) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { if k.hooks != nil { k.hooks.AfterValidatorRemoved(ctx, consAddr, valAddr) } } +// AfterValidatorBonded - call hook if registered func (k Keeper) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { if k.hooks != nil { k.hooks.AfterValidatorBonded(ctx, consAddr, valAddr) } } +// AfterValidatorPowerDidChange - call hook if registered func (k Keeper) AfterValidatorPowerDidChange(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { if k.hooks != nil { k.hooks.AfterValidatorPowerDidChange(ctx, consAddr, valAddr) } } +// AfterValidatorBeginUnbonding - call hook if registered func (k Keeper) AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { if k.hooks != nil { k.hooks.AfterValidatorBeginUnbonding(ctx, consAddr, valAddr) } } +// BeforeDelegationCreated - call hook if registered func (k Keeper) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { if k.hooks != nil { k.hooks.BeforeDelegationCreated(ctx, delAddr, valAddr) } } +// BeforeDelegationSharesModified - call hook if registered func (k Keeper) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { if k.hooks != nil { k.hooks.BeforeDelegationSharesModified(ctx, delAddr, valAddr) } } +// BeforeDelegationRemoved - call hook if registered func (k Keeper) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { if k.hooks != nil { k.hooks.BeforeDelegationRemoved(ctx, delAddr, valAddr) } } +// AfterDelegationModified - call hook if registered func (k Keeper) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { if k.hooks != nil { k.hooks.AfterDelegationModified(ctx, delAddr, valAddr) } } +// BeforeValidatorSlashed - call hook if registered func (k Keeper) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) { if k.hooks != nil { k.hooks.BeforeValidatorSlashed(ctx, valAddr, fraction) diff --git a/x/staking/keeper/keeper.go b/x/staking/keeper/keeper.go index d085f1f0fdb6..edf329c315b5 100644 --- a/x/staking/keeper/keeper.go +++ b/x/staking/keeper/keeper.go @@ -28,7 +28,9 @@ type Keeper struct { codespace sdk.CodespaceType } -func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, paramstore params.Subspace, codespace sdk.CodespaceType) Keeper { +func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, + paramstore params.Subspace, codespace sdk.CodespaceType) Keeper { + keeper := Keeper{ storeKey: key, storeTKey: tkey, diff --git a/x/staking/keeper/query_utils.go b/x/staking/keeper/query_utils.go index 872059f1108a..c4acefe705e4 100644 --- a/x/staking/keeper/query_utils.go +++ b/x/staking/keeper/query_utils.go @@ -84,7 +84,10 @@ func (k Keeper) GetAllUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAdd } // return all redelegations for a delegator -func (k Keeper) GetAllRedelegations(ctx sdk.Context, delegator sdk.AccAddress, srcValAddress, dstValAddress sdk.ValAddress) (redelegations []types.Redelegation) { +func (k Keeper) GetAllRedelegations(ctx sdk.Context, delegator sdk.AccAddress, + srcValAddress, dstValAddress sdk.ValAddress) ( + redelegations []types.Redelegation) { + store := ctx.KVStore(k.storeKey) delegatorPrefixKey := GetREDsKey(delegator) iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest diff --git a/x/staking/keeper/val_state_change.go b/x/staking/keeper/val_state_change.go index 3ddb995fc02a..f611d4e20e0e 100644 --- a/x/staking/keeper/val_state_change.go +++ b/x/staking/keeper/val_state_change.go @@ -79,9 +79,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // Assert that the validator had updated its ValidatorDistInfo.FeePoolWithdrawalHeight. // This hook is extremely useful, otherwise lazy accum bugs will be difficult to solve. - if k.hooks != nil { - k.hooks.AfterValidatorPowerDidChange(ctx, validator.ConsAddress(), valAddr) - } + k.AfterValidatorPowerDidChange(ctx, validator.ConsAddress(), valAddr) // set validator power on lookup index. k.SetLastValidatorPower(ctx, valAddr, sdk.NewInt(newPower)) @@ -194,10 +192,8 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types. // delete from queue if present k.DeleteValidatorQueue(ctx, validator) - // call the bond hook if present - if k.hooks != nil { - k.hooks.AfterValidatorBonded(ctx, validator.ConsAddress(), validator.OperatorAddr) - } + // trigger hook + k.AfterValidatorBonded(ctx, validator.ConsAddress(), validator.OperatorAddr) return validator } @@ -229,10 +225,8 @@ func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validat // Adds to unbonding validator queue k.InsertValidatorQueue(ctx, validator) - // call the unbond hook if present - if k.hooks != nil { - k.hooks.AfterValidatorBeginUnbonding(ctx, validator.ConsAddress(), validator.OperatorAddr) - } + // trigger hook + k.AfterValidatorBeginUnbonding(ctx, validator.ConsAddress(), validator.OperatorAddr) return validator } diff --git a/x/staking/keeper/validator.go b/x/staking/keeper/validator.go index 20f82fd52004..d314b44ab431 100644 --- a/x/staking/keeper/validator.go +++ b/x/staking/keeper/validator.go @@ -184,15 +184,11 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { return } if validator.Status != sdk.Unbonded { - panic("Cannot call RemoveValidator on bonded or unbonding validators") + panic("cannot call RemoveValidator on bonded or unbonding validators") + } + if validator.Tokens.IsPositive() { + panic("attempting to remove a validator which still contains tokens") } - - // if any tokens remain, remove from pool (burning the tokens). - // this happens if shares are zero but tokens are not. - // TODO: Remove once https://github.com/cosmos/cosmos-sdk/pull/2958 is merged - pool := k.GetPool(ctx) - pool.NotBondedTokens = pool.NotBondedTokens.Sub(validator.Tokens) - k.SetPool(ctx, pool) // delete the old validator record store := ctx.KVStore(k.storeKey) @@ -200,11 +196,8 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { store.Delete(GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address()))) store.Delete(GetValidatorsByPowerIndexKey(validator)) - // call hook if present - if k.hooks != nil { - k.hooks.AfterValidatorRemoved(ctx, validator.ConsAddress(), validator.OperatorAddr) - } - + // call hooks + k.AfterValidatorRemoved(ctx, validator.ConsAddress(), validator.OperatorAddr) } //___________________________________________________________________________ From e388919bd2e958c67ccbb56d2803618b2c54e463 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Fri, 25 Jan 2019 03:43:34 -0500 Subject: [PATCH 04/16] fix compile --- x/staking/keeper/delegation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go index 1b7671e0cf11..85549e69a1e8 100644 --- a/x/staking/keeper/delegation.go +++ b/x/staking/keeper/delegation.go @@ -464,7 +464,7 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Co // Get or create the delegator delegation delegation, found := k.GetDelegation(ctx, delAddr, validator.OperatorAddr) if !found { - delegation = NewDelegation(delAddr, validator.OperatorAddrsdk.ZeroDec()) + delegation = types.NewDelegation(delAddr, validator.OperatorAddr, sdk.ZeroDec()) } // call the appropriate hook if present From 058d6548a642b8079756b052450e2cefed3eaeb0 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Fri, 25 Jan 2019 04:51:30 -0500 Subject: [PATCH 05/16] fix test --- x/staking/keeper/validator_test.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/x/staking/keeper/validator_test.go b/x/staking/keeper/validator_test.go index 9f605312ae9d..cd6518a2257a 100644 --- a/x/staking/keeper/validator_test.go +++ b/x/staking/keeper/validator_test.go @@ -276,9 +276,24 @@ func TestValidatorBasics(t *testing.T) { assert.True(ValEq(t, validators[2], resVals[2])) // remove a record - validators[1].Status = sdk.Unbonded // First must set to Unbonded. - keeper.SetValidator(ctx, validators[1]) // ... - keeper.RemoveValidator(ctx, validators[1].OperatorAddr) // Now it can be removed. + + // shouldn't be able to remove if status is not unbonded + assert.PanicsWithValue(t, + "cannot call RemoveValidator on bonded or unbonding validators", + func() { keeper.RemoveValidator(ctx, validators[1].OperatorAddr) }) + + // shouldn't be able to remove if there are still tokens left + validators[1].Status = sdk.Unbonded + keeper.SetValidator(ctx, validators[1]) + assert.PanicsWithValue(t, + "attempting to remove a validator which still contains tokens", + func() { keeper.RemoveValidator(ctx, validators[1].OperatorAddr) }) + + validators[1].Tokens = sdk.ZeroInt() + keeper.SetValidator(ctx, validators[1]) + + // now it can be removed + keeper.RemoveValidator(ctx, validators[1].OperatorAddr) _, found = keeper.GetValidator(ctx, addrVals[1]) require.False(t, found) } From c3fc6a54586ca95bbc11ded7b075643a7b396120 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Fri, 25 Jan 2019 05:29:21 -0500 Subject: [PATCH 06/16] ... --- docs/spec/staking/state_transitions.md | 49 ++++++++++++++------------ x/staking/types/params.go | 6 ++-- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/docs/spec/staking/state_transitions.md b/docs/spec/staking/state_transitions.md index 23f918235a70..375456ada399 100644 --- a/docs/spec/staking/state_transitions.md +++ b/docs/spec/staking/state_transitions.md @@ -6,35 +6,38 @@ This document describes the state transition operations pertaining to: - Slashing ## Validators -RemoveValidatorTokens -RemoveValidatorTokensAndShares -AddValidatorTokensAndShares -completeUnbondingValidator -beginUnbondingValidator -bondValidator +### non-bonded to bonded + - delete record from `ValidatorByPowerIndex` + - set `Validator.BondHeight` to current height -bondedToUnbonding - -> beginUnbondingValidator -unbondingToBonded - -> bondValidator -unbondedToBonded - -> bondValidator -unbondingToUnbonded +### unbonding to unbonded -> completeUnbondingValidator -jailValidator / unjailValidator +### bonded to unbonding + -> beginUnbondingValidator + +### jail/unjail +when a validator is jailed it is effectively removed from the Tendermint set. +this process may be also be reversed. the following operations occur: + - set `Validator.Jailed` and update object + - if jailed delete record from `ValidatorByPowerIndex` + - if unjailed add record to `ValidatorByPowerIndex` ## Delegations -Delegate -unbond -Undelegate -CompleteUnbonding -BeginRedelegation -CompleteRedelegation + +### Delegate + ### AddValidatorTokensAndShares +### unbond +### Undelegate + ### RemoveValidatorTokensAndShares +### CompleteUnbonding +### BeginRedelegation +### CompleteRedelegation ## Slashing -Slash -slashUnbondingDelegation -slashRedelegation +### Slash + ### RemoveValidatorTokens +### slashUnbondingDelegation +### slashRedelegation diff --git a/x/staking/types/params.go b/x/staking/types/params.go index 1f95b160f15b..ef76b0053a9c 100644 --- a/x/staking/types/params.go +++ b/x/staking/types/params.go @@ -78,9 +78,9 @@ func DefaultParams() Params { // String returns a human readable string representation of the parameters. func (p Params) String() string { return fmt.Sprintf(`Params: - Unbonding Time: %s) - Max Validators: %d) - Max Entries: %d) + Unbonding Time: %s + Max Validators: %d + Max Entries: %d Bonded Coin Denomination: %s`, p.UnbondingTime, p.MaxValidators, p.MaxEntries, p.BondDenom) } From 51b574afbffbe1f5c9379548e815d48e1254af1e Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Fri, 25 Jan 2019 07:03:37 -0500 Subject: [PATCH 07/16] ... --- docs/spec/staking/state_transitions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/spec/staking/state_transitions.md b/docs/spec/staking/state_transitions.md index 375456ada399..cc42714d6370 100644 --- a/docs/spec/staking/state_transitions.md +++ b/docs/spec/staking/state_transitions.md @@ -10,6 +10,7 @@ This document describes the state transition operations pertaining to: ### non-bonded to bonded - delete record from `ValidatorByPowerIndex` - set `Validator.BondHeight` to current height + - update `Pool` object to reflect new bonded tokens ### unbonding to unbonded -> completeUnbondingValidator From e42ed1aa48ffaf56974ef295f138d9db067f2bd8 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 5 Feb 2019 16:33:00 -0800 Subject: [PATCH 08/16] working --- docs/spec/staking/overview.md | 2 +- docs/spec/staking/state_transitions.md | 33 +++++++++++++++++++++----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/docs/spec/staking/overview.md b/docs/spec/staking/overview.md index 752c9b616fba..14056f724ce8 100644 --- a/docs/spec/staking/overview.md +++ b/docs/spec/staking/overview.md @@ -31,7 +31,7 @@ native staking token of the chain. 2. **[State Transistions](state_transitions.md)** - Validator - Delegation - - Pool + - Slashing 2. **[Messages](messages.md)** - MsgCreateValidator - MsgEditValidator diff --git a/docs/spec/staking/state_transitions.md b/docs/spec/staking/state_transitions.md index cc42714d6370..5e7e4fad1f7c 100644 --- a/docs/spec/staking/state_transitions.md +++ b/docs/spec/staking/state_transitions.md @@ -5,18 +5,36 @@ This document describes the state transition operations pertaining to: - Delegations - Slashing + ## Validators ### non-bonded to bonded - - delete record from `ValidatorByPowerIndex` +When a validator is bonded from any other state the following operations occur: - set `Validator.BondHeight` to current height - - update `Pool` object to reflect new bonded tokens + - set `validator.Status` to `Bonded` + - update the `Pool` object with tokens moved from `NotBondedTokens` to `BondedTokens` + - delete record the existing record from `ValidatorByPowerIndex` + - add an new updated record to the `ValidatorByPowerIndex` + - update the `Validator` object for this validator + - if it exists, delete any `ValidatorQueue` record for this validator + - call the `AfterValidatorBonded` hook + +### bonded to unbonding +When a validator begins the unbonding process the following operations occur: + - update the `Pool` object with tokens moved from `BondedTokens` to `NotBondedTokens` + - set `validator.Status` to `Unbonding` + - delete record the existing record from `ValidatorByPowerIndex` + - add an new updated record to the `ValidatorByPowerIndex` + - update the `Validator` object for this validator + - insert a new record into the `ValidatorQueue` for this validator + - call the `AfterValidatorBeginUnbonding` hook ### unbonding to unbonded - -> completeUnbondingValidator +A validator moves from unbonding to unbonded when the `ValidatorQueue` object +moves from bonded to unbonded + - update the `Validator` object for this validator + - set `validator.Status` to `Unbonded` -### bonded to unbonding - -> beginUnbondingValidator ### jail/unjail when a validator is jailed it is effectively removed from the Tendermint set. @@ -25,6 +43,7 @@ this process may be also be reversed. the following operations occur: - if jailed delete record from `ValidatorByPowerIndex` - if unjailed add record to `ValidatorByPowerIndex` + ## Delegations ### Delegate @@ -36,9 +55,11 @@ this process may be also be reversed. the following operations occur: ### BeginRedelegation ### CompleteRedelegation + ## Slashing + ### Slash - ### RemoveValidatorTokens + ### RemoveValidatorTokens ### slashUnbondingDelegation ### slashRedelegation From f314803469ab1ebd5ac0318c004c05dff94611bc Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 5 Feb 2019 23:58:03 -0800 Subject: [PATCH 09/16] merge fixes --- docs/spec/staking/state_transitions.md | 10 ++++++---- x/distribution/types/keepers.go | 2 +- x/staking/keeper/validator.go | 10 +++++----- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/spec/staking/state_transitions.md b/docs/spec/staking/state_transitions.md index 5e7e4fad1f7c..15f3ae673fac 100644 --- a/docs/spec/staking/state_transitions.md +++ b/docs/spec/staking/state_transitions.md @@ -17,7 +17,6 @@ When a validator is bonded from any other state the following operations occur: - add an new updated record to the `ValidatorByPowerIndex` - update the `Validator` object for this validator - if it exists, delete any `ValidatorQueue` record for this validator - - call the `AfterValidatorBonded` hook ### bonded to unbonding When a validator begins the unbonding process the following operations occur: @@ -27,7 +26,6 @@ When a validator begins the unbonding process the following operations occur: - add an new updated record to the `ValidatorByPowerIndex` - update the `Validator` object for this validator - insert a new record into the `ValidatorQueue` for this validator - - call the `AfterValidatorBeginUnbonding` hook ### unbonding to unbonded A validator moves from unbonding to unbonded when the `ValidatorQueue` object @@ -35,7 +33,6 @@ moves from bonded to unbonded - update the `Validator` object for this validator - set `validator.Status` to `Unbonded` - ### jail/unjail when a validator is jailed it is effectively removed from the Tendermint set. this process may be also be reversed. the following operations occur: @@ -47,7 +44,12 @@ this process may be also be reversed. the following operations occur: ## Delegations ### Delegate - ### AddValidatorTokensAndShares +When a delegation occurs both the validator object are affected + + - update the `Validator` object + - delete record the existing record from `ValidatorByPowerIndex` + - add an new updated record to the `ValidatorByPowerIndex` + ### unbond ### Undelegate ### RemoveValidatorTokensAndShares diff --git a/x/distribution/types/keepers.go b/x/distribution/types/keepers.go index e734d47fcdfe..07fd2b7362e6 100644 --- a/x/distribution/types/keepers.go +++ b/x/distribution/types/keepers.go @@ -11,7 +11,7 @@ type StakingKeeper interface { ValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) sdk.Validator TotalPower(ctx sdk.Context) sdk.Int GetLastTotalPower(ctx sdk.Context) sdk.Int - GetLastValidatorPower(ctx sdk.Context, valAddr sdk.ValAddress) sdk.Int + GetLastValidatorPower(ctx sdk.Context, valAddr sdk.ValAddress) int64 } // expected coin keeper diff --git a/x/staking/keeper/validator.go b/x/staking/keeper/validator.go index 9e6889d503d4..bee2696f8fa4 100644 --- a/x/staking/keeper/validator.go +++ b/x/staking/keeper/validator.go @@ -274,18 +274,18 @@ func (k Keeper) ValidatorsPowerStoreIterator(ctx sdk.Context) (iterator sdk.Iter // Load the last validator power. // Returns zero if the operator was not a validator last block. -func (k Keeper) GetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) (power sdk.Int) { +func (k Keeper) GetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) (power int64) { store := ctx.KVStore(k.storeKey) bz := store.Get(GetLastValidatorPowerKey(operator)) if bz == nil { - return sdk.ZeroInt() + return 0 } k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &power) return } // Set the last validator power. -func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, power sdk.Int) { +func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, power int64) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinaryLengthPrefixed(power) store.Set(GetLastValidatorPowerKey(operator), bz) @@ -305,13 +305,13 @@ func (k Keeper) LastValidatorsIterator(ctx sdk.Context) (iterator sdk.Iterator) } // Iterate over last validator powers. -func (k Keeper) IterateLastValidatorPowers(ctx sdk.Context, handler func(operator sdk.ValAddress, power sdk.Int) (stop bool)) { +func (k Keeper) IterateLastValidatorPowers(ctx sdk.Context, handler func(operator sdk.ValAddress, power int64) (stop bool)) { store := ctx.KVStore(k.storeKey) iter := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) defer iter.Close() for ; iter.Valid(); iter.Next() { addr := sdk.ValAddress(iter.Key()[len(LastValidatorPowerKey):]) - var power sdk.Int + var power int64 k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &power) if handler(addr, power) { break From 5a2e0295ce4ade4bacfad53416b29b22221620f5 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 6 Feb 2019 00:22:04 -0800 Subject: [PATCH 10/16] state transition updates --- docs/spec/staking/state_transitions.md | 26 ++++++++++++++++++++------ x/staking/{staking.go => alias.go} | 0 2 files changed, 20 insertions(+), 6 deletions(-) rename x/staking/{staking.go => alias.go} (100%) diff --git a/docs/spec/staking/state_transitions.md b/docs/spec/staking/state_transitions.md index 15f3ae673fac..6e07993fff57 100644 --- a/docs/spec/staking/state_transitions.md +++ b/docs/spec/staking/state_transitions.md @@ -45,23 +45,37 @@ this process may be also be reversed. the following operations occur: ### Delegate When a delegation occurs both the validator object are affected - - - update the `Validator` object + - determine the delegators shares based on tokens delegated and the validator's exchange rate + - remove tokens from the sending account + - add shares the delegation object or add them to a created validator object + - add new delegator shares and update the `Validator` object + - update the `Pool` object appropriately if tokens have moved into a bonded validator - delete record the existing record from `ValidatorByPowerIndex` - add an new updated record to the `ValidatorByPowerIndex` -### unbond ### Undelegate - ### RemoveValidatorTokensAndShares +When a + +unbond + // subtract shares from delegator + // remove the delegation or // update the delegation + // if the delegation is the operator of the validator then + // trigger a jail validator + // remove the coins from the validator + // if not unbonded, we must instead remove validator in EndBlocker once it finishes its unbonding period + ### CompleteUnbonding + - + ### BeginRedelegation + ### CompleteRedelegation -## Slashing +TODO TODO TOFU TODO +## Slashing ### Slash - ### RemoveValidatorTokens ### slashUnbondingDelegation ### slashRedelegation diff --git a/x/staking/staking.go b/x/staking/alias.go similarity index 100% rename from x/staking/staking.go rename to x/staking/alias.go From cf6e0e161501a50d42888d51de7c69a1d189cf01 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 6 Feb 2019 16:57:37 -0800 Subject: [PATCH 11/16] state transitions --- docs/spec/staking/state_transitions.md | 69 ++++++++++++++++---------- x/staking/keeper/delegation.go | 2 +- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/docs/spec/staking/state_transitions.md b/docs/spec/staking/state_transitions.md index 6e07993fff57..75a545b5ec2f 100644 --- a/docs/spec/staking/state_transitions.md +++ b/docs/spec/staking/state_transitions.md @@ -8,7 +8,7 @@ This document describes the state transition operations pertaining to: ## Validators -### non-bonded to bonded +### Non-Bonded to Bonded When a validator is bonded from any other state the following operations occur: - set `Validator.BondHeight` to current height - set `validator.Status` to `Bonded` @@ -18,7 +18,7 @@ When a validator is bonded from any other state the following operations occur: - update the `Validator` object for this validator - if it exists, delete any `ValidatorQueue` record for this validator -### bonded to unbonding +### Bonded to Unbonding When a validator begins the unbonding process the following operations occur: - update the `Pool` object with tokens moved from `BondedTokens` to `NotBondedTokens` - set `validator.Status` to `Unbonding` @@ -27,13 +27,13 @@ When a validator begins the unbonding process the following operations occur: - update the `Validator` object for this validator - insert a new record into the `ValidatorQueue` for this validator -### unbonding to unbonded +### Unbonding to Unbonded A validator moves from unbonding to unbonded when the `ValidatorQueue` object moves from bonded to unbonded - update the `Validator` object for this validator - set `validator.Status` to `Unbonded` -### jail/unjail +### Jail/Unjail when a validator is jailed it is effectively removed from the Tendermint set. this process may be also be reversed. the following operations occur: - set `Validator.Jailed` and update object @@ -44,7 +44,7 @@ this process may be also be reversed. the following operations occur: ## Delegations ### Delegate -When a delegation occurs both the validator object are affected +When a delegation occurs both the validator and the delegtion objects are affected - determine the delegators shares based on tokens delegated and the validator's exchange rate - remove tokens from the sending account - add shares the delegation object or add them to a created validator object @@ -53,29 +53,48 @@ When a delegation occurs both the validator object are affected - delete record the existing record from `ValidatorByPowerIndex` - add an new updated record to the `ValidatorByPowerIndex` -### Undelegate -When a - -unbond - // subtract shares from delegator - // remove the delegation or // update the delegation - // if the delegation is the operator of the validator then - // trigger a jail validator - // remove the coins from the validator - // if not unbonded, we must instead remove validator in EndBlocker once it finishes its unbonding period - -### CompleteUnbonding - - - -### BeginRedelegation - -### CompleteRedelegation +#### Unbond Delegation +As a part of the Undelegate and Complete Unbonding state transitions Unbond +Delegation may be called. + - subtract the unbonded shares from delegator + - update the delegation or remove the delegation if there are no more shares + - if the delegation is the operator of the validator and no more shares exist + then trigger a jail validator + - update the validator with removed the delegator shares and associated coins, update + the pool for any shifts between bonded and non-bonded tokens. + - remove the validator if it is unbonded and there are no more delegation shares. +### Undelegate +When an delegation occurs both the validator and the delegtion objects are affected + - perform an unbond delegation + - if the validator is unbonding or bonded add the tokens to an + `UnbondingDelegation` Entry + - if the validator is unbonded send the tokens directly to the withdraw + account + +### Complete Unbonding +For undelegations which do not complete immediately, the following operations +occur when the unbonding delegation queue element matures: + - remove the entry from the `UnbondingDelegation` object + - withdraw the tokens to the delegator withdraw address + +### Begin Redelegation +Redelegations affect the delegation, source and destination validators. + - perform an unbond delegation from the source validator + - using the generated tokens perform a Delegate to the destination + validator + - record the token amount in an new entry in the relevant `Redelegation` + +### Complete Redelegation +When a redelegations complete the following occurs: + - remove the entry from the `Redelegation` object TODO TODO TOFU TODO ## Slashing -### Slash -### slashUnbondingDelegation -### slashRedelegation +### Slash Validator + +### Slash Unbonding Delegation + +### Slash Redelegation diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go index 85549e69a1e8..dd7b25637a16 100644 --- a/x/staking/keeper/delegation.go +++ b/x/staking/keeper/delegation.go @@ -534,7 +534,7 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA k.AfterDelegationModified(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr) } - // remove the coins from the validator + // remove the sharea and coins from the validator validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares) if validator.DelegatorShares.IsZero() && validator.Status == sdk.Unbonded { From 0d736067159c72d9f648ce316e34f2933b312611 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 6 Feb 2019 18:01:52 -0800 Subject: [PATCH 12/16] pending --- PENDING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PENDING.md b/PENDING.md index 334878e7bfb0..387a900aa069 100644 --- a/PENDING.md +++ b/PENDING.md @@ -83,6 +83,7 @@ IMPROVEMENTS * \#2509 Sanitize all usage of Dec.RoundInt64() * [\#556](https://github.com/cosmos/cosmos-sdk/issues/556) Increase `BaseApp` test coverage. + * \#3357 develop state-transitions.md for staking spec, missing states added to `state.md` * Tendermint From f9824a56b2e9cad4672eb639cd8aee5041074c30 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Thu, 7 Feb 2019 14:35:32 -0800 Subject: [PATCH 13/16] Apply suggestions from code review Co-Authored-By: rigelrozanski --- docs/spec/staking/state.md | 3 ++- docs/spec/staking/state_transitions.md | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index e50095019828..272b564f5606 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -211,9 +211,10 @@ type RedelegationEntry struct { ## Queues All queues objects are sorted by timestamp. The time used within any queue is -first rounded to the nearest nanosecond then sorted. The sortable time format +first rounded to the nearest nanosecond then sorted. The sortable time format used is a slight modification of the RFC3339Nano and uses the the format string `"2006-01-02T15:04:05.000000000"`. Noteably This format: + - right pads all zeros - drops the time zone info (uses UTC) diff --git a/docs/spec/staking/state_transitions.md b/docs/spec/staking/state_transitions.md index 75a545b5ec2f..3bb7d06f8002 100644 --- a/docs/spec/staking/state_transitions.md +++ b/docs/spec/staking/state_transitions.md @@ -1,6 +1,7 @@ # State Transitions This document describes the state transition operations pertaining to: + - Validators - Delegations - Slashing @@ -9,6 +10,7 @@ This document describes the state transition operations pertaining to: ## Validators ### Non-Bonded to Bonded + When a validator is bonded from any other state the following operations occur: - set `Validator.BondHeight` to current height - set `validator.Status` to `Bonded` From 2d119a2429e74c94341c94c10c56c4150c3853c1 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Thu, 7 Feb 2019 14:49:11 -0800 Subject: [PATCH 14/16] address @alexanderbez comments --- docs/spec/staking/overview.md | 8 ++++---- x/staking/keeper/delegation.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/spec/staking/overview.md b/docs/spec/staking/overview.md index 14056f724ce8..09f457589d9d 100644 --- a/docs/spec/staking/overview.md +++ b/docs/spec/staking/overview.md @@ -32,18 +32,18 @@ native staking token of the chain. - Validator - Delegation - Slashing - 2. **[Messages](messages.md)** + 3. **[Messages](messages.md)** - MsgCreateValidator - MsgEditValidator - MsgDelegate - MsgBeginUnbonding - MsgBeginRedelegate - 3. **[End-Block](end_block.md)** + 4. **[End-Block](end_block.md)** - Validator Set Changes - Queues - Unbonding Validators - Unbonding Delegations - Redelegations - 4. **[Hooks](hooks.md)** - 5. **[Tags](tags.md)** + 5. **[Hooks](hooks.md)** + 6. **[Tags](tags.md)** diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go index dd7b25637a16..8af194e03cc9 100644 --- a/x/staking/keeper/delegation.go +++ b/x/staking/keeper/delegation.go @@ -534,7 +534,7 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA k.AfterDelegationModified(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr) } - // remove the sharea and coins from the validator + // remove the shares and coins from the validator validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares) if validator.DelegatorShares.IsZero() && validator.Status == sdk.Unbonded { From cdb5eb86fa2da640b3a34df9247d001434472872 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 7 Feb 2019 15:38:31 -0800 Subject: [PATCH 15/16] Apply suggestions from code review Co-Authored-By: rigelrozanski --- docs/spec/staking/end_block.md | 4 ++-- docs/spec/staking/state.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/spec/staking/end_block.md b/docs/spec/staking/end_block.md index c0df0bc1346c..08b5fd73356f 100644 --- a/docs/spec/staking/end_block.md +++ b/docs/spec/staking/end_block.md @@ -40,8 +40,8 @@ after the unbonding period has passed. Each block the validator queue is to be checked for mature unbonding validators (namely with a completion time <= current time). At this point any mature -validators which do not have any delegations remaining are delete from state. -For all other mature unbonding validators that still have remaining +validators which do not have any delegations remaining are deleted from state. +For all other mature unbonding validators that still have remaining delegations, the validator.Status is switched from sdk.Unbonding to sdk.Unbonded. diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index 272b564f5606..228fb7f40344 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -44,10 +44,10 @@ type Params struct { Validators objects should be primarily stored and accessed by the `OperatorAddr`, an SDK validator address for the operator of the validator. Two -additional indexes are maintained per validator object in order to fulfill +additional indices are maintained per validator object in order to fulfill required lookups for slashing and validator-set updates. A third special index (`LastValidatorPower`) is also maintained which however remains constant -throughout each block, unlike the first two indexes which mirror the validator +throughout each block, unlike the first two indices which mirror the validator records within a block. - Validators: `0x21 | OperatorAddr -> amino(validator)` @@ -259,5 +259,5 @@ queue is kept. The stored object as each key is an array of validator operator addresses from which the validator object can be accessed. Typically it is expected that only -a single validator record will operate a given timestamp however it is possible +a single validator record will be associated with a given timestamp however it is possible that multiple validators exist in the queue at the same location. From d64e8c82e06db77206de46f7e026af430b987499 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Fri, 8 Feb 2019 14:42:47 -0800 Subject: [PATCH 16/16] chris comments --- docs/spec/staking/state_transitions.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/spec/staking/state_transitions.md b/docs/spec/staking/state_transitions.md index 3bb7d06f8002..0feee82f0906 100644 --- a/docs/spec/staking/state_transitions.md +++ b/docs/spec/staking/state_transitions.md @@ -12,7 +12,6 @@ This document describes the state transition operations pertaining to: ### Non-Bonded to Bonded When a validator is bonded from any other state the following operations occur: - - set `Validator.BondHeight` to current height - set `validator.Status` to `Bonded` - update the `Pool` object with tokens moved from `NotBondedTokens` to `BondedTokens` - delete record the existing record from `ValidatorByPowerIndex`