From 7353eb4d1b00bbddf1a731bb10d1ada1cefd59b1 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 23 May 2018 16:38:50 -0400 Subject: [PATCH] updateBondedValidators only kicks the cliff validator (typical case) --- x/stake/keeper.go | 110 +++++++++++++++++++++++++++++++---------- x/stake/keeper_keys.go | 19 +++---- 2 files changed, 94 insertions(+), 35 deletions(-) diff --git a/x/stake/keeper.go b/x/stake/keeper.go index 3088341a3e..1e8697e1df 100644 --- a/x/stake/keeper.go +++ b/x/stake/keeper.go @@ -243,7 +243,7 @@ func (k Keeper) updateValidator(ctx sdk.Context, validator Validator) Validator } // update the validator set for this validator - updatedVal := k.updateBondedValidatorsNew(ctx, store, validator) + updatedVal := k.updateBondedValidators(ctx, store, validator) if updatedVal.Owner != nil { // updates to validator occured to be updated validator = updatedVal } @@ -265,31 +265,23 @@ func (k Keeper) updateValidator(ctx sdk.Context, validator Validator) Validator // GetValidators. // // Optionally also return the validator from a retrieve address if the validator has been bonded -func (k Keeper) updateBondedValidators(ctx sdk.Context, store sdk.KVStore) { - k.updateBondedValidatorsNew(ctx, store, Validator{}) -} -func (k Keeper) updateBondedValidatorsNew(ctx sdk.Context, store sdk.KVStore, +func (k Keeper) updateBondedValidators(ctx sdk.Context, store sdk.KVStore, newValidator Validator) (updatedVal Validator) { - // clear the current validators store, add to the ToKickOut temp store - toKickOut := make(map[string]byte) - iterator := store.SubspaceIterator(ValidatorsBondedKey) - for ; iterator.Valid(); iterator.Next() { - ownerAddr := iterator.Value() - toKickOut[string(ownerAddr)] = 0 // set anything - } - iterator.Close() + kickCliffValidator := false + oldCliffValidatorAddr := k.getCliffValidator(ctx) // add the actual validator power sorted store maxValidators := k.GetParams(ctx).MaxValidators - iterator = store.ReverseSubspaceIterator(ValidatorsByPowerKey) // largest to smallest + iterator := store.ReverseSubspaceIterator(ValidatorsByPowerKey) // largest to smallest i := 0 var validator Validator for ; ; i++ { if !iterator.Valid() || i > int(maxValidators-1) { + // TODO benchmark if we should read the current power and not write if it's the same if i-1 == int(maxValidators-1) { - k.setCliffValidatorPower(ctx, validator, k.GetPool(ctx)) + k.setCliffValidator(ctx, validator, k.GetPool(ctx)) } iterator.Close() break @@ -310,7 +302,70 @@ func (k Keeper) updateBondedValidatorsNew(ctx sdk.Context, store sdk.KVStore, } } - _, found := toKickOut[string(ownerAddr)] + // if not previously a validator (and unrevoked), + // kick the cliff validator / bond this new validator + if validator.Status() != sdk.Bonded && !validator.Revoked { + kickCliffValidator = true + + validator = k.bondValidator(ctx, store, validator) + if bytes.Equal(ownerAddr, newValidator.Owner) { + updatedVal = validator + } + } + + iterator.Next() + } + + // perform the actual kicks + if oldCliffValidatorAddr != nil && kickCliffValidator { + validator, found := k.getValidator(store, oldCliffValidatorAddr) + if !found { + panic(fmt.Sprintf("validator record not found for address: %v\n", oldCliffValidatorAddr)) + } + k.unbondValidator(ctx, store, validator) + } + + return +} + +// full update of the bonded validator set, many can be added/kicked +func (k Keeper) updateBondedValidatorsFull(ctx sdk.Context, store sdk.KVStore) { + // clear the current validators store, add to the ToKickOut temp store + toKickOut := make(map[string]byte) + iterator := store.SubspaceIterator(ValidatorsBondedKey) + for ; iterator.Valid(); iterator.Next() { + ownerAddr := iterator.Value() + toKickOut[string(ownerAddr)] = 0 // set anything + } + iterator.Close() + + // add the actual validator power sorted store + maxValidators := k.GetParams(ctx).MaxValidators + iterator = store.ReverseSubspaceIterator(ValidatorsByPowerKey) // largest to smallest + i := 0 + var validator Validator + for ; ; i++ { + if !iterator.Valid() || i > int(maxValidators-1) { + + if i-1 == int(maxValidators-1) { + k.setCliffValidator(ctx, validator, k.GetPool(ctx)) + } + iterator.Close() + break + } + + // either retrieve the original validator from the store, + // or under the situation that this is the "new validator" just + // use the validator provided because it has not yet been updated + // in the main validator store + ownerAddr := iterator.Value() + var found bool + validator, found = k.getValidator(store, ownerAddr) + if !found { + panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr)) + } + + _, found = toKickOut[string(ownerAddr)] if found { delete(toKickOut, string(ownerAddr)) } else { @@ -319,9 +374,6 @@ func (k Keeper) updateBondedValidatorsNew(ctx sdk.Context, store sdk.KVStore, // this wasn't a previously a validator, therefor // update the validator to enter the validator group validator = k.bondValidator(ctx, store, validator) - if bytes.Equal(ownerAddr, newValidator.Owner) { - updatedVal = validator - } } iterator.Next() @@ -336,7 +388,6 @@ func (k Keeper) updateBondedValidatorsNew(ctx sdk.Context, store sdk.KVStore, } k.unbondValidator(ctx, store, validator) } - return } @@ -517,7 +568,7 @@ func (k Keeper) setParams(ctx sdk.Context, params Params) { // if max validator count changes, must recalculate validator set if exParams.MaxValidators != params.MaxValidators { - k.updateBondedValidators(ctx, store) + k.updateBondedValidatorsFull(ctx, store) } b := k.cdc.MustMarshalBinary(params) store.Set(ParamKey, b) @@ -568,17 +619,24 @@ func (k Keeper) setIntraTxCounter(ctx sdk.Context, counter int16) { //__________________________________________________________________________ -// get the current power of the validator on the cliff -func (k Keeper) getCliffValidatorPower(ctx sdk.Context) []byte { +// get the current validator on the cliff +func (k Keeper) getCliffValidator(ctx sdk.Context) []byte { store := ctx.KVStore(k.storeKey) return store.Get(ValidatorCliffKey) } -// set the current power of the validator on the cliff -func (k Keeper) setCliffValidatorPower(ctx sdk.Context, validator Validator, pool Pool) { +// get the current power of the validator on the cliff +func (k Keeper) getCliffValidatorPower(ctx sdk.Context) []byte { + store := ctx.KVStore(k.storeKey) + return store.Get(ValidatorPowerCliffKey) +} + +// set the current validator and power of the validator on the cliff +func (k Keeper) setCliffValidator(ctx sdk.Context, validator Validator, pool Pool) { store := ctx.KVStore(k.storeKey) bz := GetValidatorsByPowerKey(validator, pool) - store.Set(ValidatorCliffKey, bz) + store.Set(ValidatorPowerCliffKey, bz) + store.Set(ValidatorCliffKey, validator.Owner) } //__________________________________________________________________________ diff --git a/x/stake/keeper_keys.go b/x/stake/keeper_keys.go index a2ad0f2520..5a84d08f29 100644 --- a/x/stake/keeper_keys.go +++ b/x/stake/keeper_keys.go @@ -13,15 +13,16 @@ import ( //nolint var ( // Keys for store prefixes - ParamKey = []byte{0x00} // key for parameters relating to staking - PoolKey = []byte{0x01} // key for the staking pools - ValidatorsKey = []byte{0x02} // prefix for each key to a validator - ValidatorsBondedKey = []byte{0x03} // prefix for each key to bonded/actively validating validators - ValidatorsByPowerKey = []byte{0x04} // prefix for each key to a validator sorted by power - ValidatorCliffKey = []byte{0x05} // key for block-local tx index - TendermintUpdatesKey = []byte{0x06} // prefix for each key to a validator which is being updated - DelegationKey = []byte{0x07} // prefix for each key to a delegator's bond - IntraTxCounterKey = []byte{0x08} // key for block-local tx index + ParamKey = []byte{0x00} // key for parameters relating to staking + PoolKey = []byte{0x01} // key for the staking pools + ValidatorsKey = []byte{0x02} // prefix for each key to a validator + ValidatorsBondedKey = []byte{0x03} // prefix for each key to bonded/actively validating validators + ValidatorsByPowerKey = []byte{0x04} // prefix for each key to a validator sorted by power + ValidatorCliffKey = []byte{0x05} // key for block-local tx index + ValidatorPowerCliffKey = []byte{0x06} // key for block-local tx index + TendermintUpdatesKey = []byte{0x07} // prefix for each key to a validator which is being updated + DelegationKey = []byte{0x08} // prefix for each key to a delegator's bond + IntraTxCounterKey = []byte{0x09} // key for block-local tx index ) const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch