Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix simulation bugs; Incorprates #2676 from Sunny #2677

Merged
merged 26 commits into from
Nov 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4e44ddf
Print out initial update on every block
cwgoes Oct 28, 2018
38ceea1
Randomize simulation parameters
cwgoes Oct 28, 2018
4e48217
Randomize initial liveness weightings
cwgoes Oct 28, 2018
46e6b40
Randomize genesis parameters
cwgoes Oct 28, 2018
3884b13
Update PENDING.md
cwgoes Oct 28, 2018
991573e
Avoid dividing-by-zero
cwgoes Oct 28, 2018
f5918db
More simulation seeds, more blocks in CI version
cwgoes Oct 29, 2018
e2aa3e9
Update cmd/gaia/app/sim_test.go
zmanian Oct 29, 2018
01c235d
Update cmd/gaia/app/sim_test.go
zmanian Oct 29, 2018
f13f602
Update cmd/gaia/app/sim_test.go
zmanian Oct 29, 2018
1868ee3
Merge remote-tracking branch 'origin/develop' into cwgoes/simulation-…
rigelrozanski Oct 31, 2018
a84cf4f
Merge tag 'v0.25.0' into develop
cwgoes Nov 1, 2018
fb0f4a4
Merge branch 'develop' into cwgoes/simulation-transubstantiated
cwgoes Nov 1, 2018
d9907cc
Address comments
cwgoes Nov 1, 2018
f695a66
Minimize variable passing
cwgoes Nov 1, 2018
39165c9
fixed power store invariant
sunnya97 Nov 2, 2018
3cf6687
PENDING
sunnya97 Nov 2, 2018
42eb4e2
IterateValidatorsBonded -> IterateBondedValidatorsByPower
sunnya97 Nov 2, 2018
888c061
WriteValidators uses IterateLastValidators rather than IterateBondedV…
sunnya97 Nov 2, 2018
13d933f
fixed democoin interface
sunnya97 Nov 2, 2018
191a732
fixed comment
sunnya97 Nov 3, 2018
559ebb7
Merge PR #2671: Power Store Invariant
rigelrozanski Nov 3, 2018
7b10ac4
Merge branch 'develop' into cwgoes/simulation-transubstantiated
Nov 3, 2018
4068d8a
Fix simulation bugs; Incorprates #2676 from Sunny
jaekwon Nov 4, 2018
ace50be
Address review feedback; Update PENDING
jaekwon Nov 5, 2018
95cf0c3
Merge branch 'develop' into jae/simulation-transubstantiated
jaekwon Nov 5, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ IMPROVEMENTS
* Gaia

* SDK
- #2573 [x/distribution] add accum invariance
- #2556 [x/mock/simulation] Fix debugging output
- #2396 [x/mock/simulation] Change parameters to get more slashes
- #2617 [x/mock/simulation] Randomize all genesis parameters
- #2669 [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store.
- \#2573 [x/distribution] add accum invariance
- \#2556 [x/mock/simulation] Fix debugging output
- \#2396 [x/mock/simulation] Change parameters to get more slashes
- \#2617 [x/mock/simulation] Randomize all genesis parameters
- \#2669 [x/stake] Added invarant check to make sure validator's power aligns with its spot in the power store.
- \#1924 [x/mock/simulation] Use a transition matrix for block size
- \#2660 [x/mock/simulation] Staking transactions get tested far more frequently
- #2610 [x/stake] Block redelegation to and from the same validator
- #2652 [x/auth] Add benchmark for get and set account
- \#2610 [x/stake] Block redelegation to and from the same validator
- \#2652 [x/auth] Add benchmark for get and set account

* Tendermint

Expand All @@ -62,10 +62,10 @@ BUG FIXES
* Gaia CLI (`gaiacli`)

* Gaia
- #2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators`
- \#2670 [x/stake] fixed incorrent `IterateBondedValidators` and split into two functions: `IterateBondedValidators` and `IterateLastBlockConsValidators`

* SDK
- #2625 [x/gov] fix AppendTag function usage error

- \#2625 [x/gov] fix AppendTag function usage error
- \#2677 [x/stake, x/distribution] various staking/distribution fixes as found by the simulator

* Tendermint
14 changes: 8 additions & 6 deletions cmd/gaia/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,38 +109,40 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
app.cdc,
app.keyParams, app.tkeyParams,
)
app.stakeKeeper = stake.NewKeeper(
stakeKeeper := stake.NewKeeper(
app.cdc,
app.keyStake, app.tkeyStake,
app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace),
sunnya97 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we change all passed around Keepers to be pointers? Or just save that for a seperate PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets consider it in the future for a separate PR... I'm not sure. I think for new keepers we do want pointers.

app.RegisterCodespace(stake.DefaultCodespace),
)
app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint,
app.paramsKeeper.Subspace(mint.DefaultParamspace),
app.stakeKeeper, app.feeCollectionKeeper,
&stakeKeeper, app.feeCollectionKeeper,
)
app.distrKeeper = distr.NewKeeper(
app.cdc,
app.keyDistr,
app.paramsKeeper.Subspace(distr.DefaultParamspace),
app.bankKeeper, app.stakeKeeper, app.feeCollectionKeeper,
app.bankKeeper, &stakeKeeper, app.feeCollectionKeeper,
app.RegisterCodespace(stake.DefaultCodespace),
)
app.slashingKeeper = slashing.NewKeeper(
app.cdc,
app.keySlashing,
app.stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace),
&stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace),
app.RegisterCodespace(slashing.DefaultCodespace),
)
app.govKeeper = gov.NewKeeper(
app.cdc,
app.keyGov,
app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, app.stakeKeeper,
app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, &stakeKeeper,
app.RegisterCodespace(gov.DefaultCodespace),
)

// register the staking hooks
app.stakeKeeper = app.stakeKeeper.WithHooks(
// NOTE: stakeKeeper above are passed by reference,
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
// so that it can be modified like below:
app.stakeKeeper = *stakeKeeper.SetHooks(
NewHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks()))

// register message routes
Expand Down
7 changes: 5 additions & 2 deletions x/distribution/keeper/hooks.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package keeper

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
Expand Down Expand Up @@ -38,11 +40,12 @@ func (k Keeper) onValidatorBonded(ctx sdk.Context, valAddr sdk.ValAddress) {
k.onValidatorModified(ctx, valAddr)
}

// XXX Consider removing this after debugging.
// Sanity check, very useful!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to #2663?

Like maybe these sanity check so be run as an optional thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not super performance heavy, lets run it always for now.

func (k Keeper) onValidatorPowerDidChange(ctx sdk.Context, valAddr sdk.ValAddress) {
vi := k.GetValidatorDistInfo(ctx, valAddr)
if vi.FeePoolWithdrawalHeight != ctx.BlockHeight() {
panic("expected validator dist info FeePoolWithdrawalHeight to be updated, but was not.")
panic(fmt.Sprintf("expected validator (%v) dist info FeePoolWithdrawalHeight to be updated to %v, but was %v.",
valAddr.String(), ctx.BlockHeight(), vi.FeePoolWithdrawalHeight))
}
}

Expand Down
2 changes: 1 addition & 1 deletion x/distribution/keeper/test_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initCoins int64,
keeper := NewKeeper(cdc, keyDistr, pk.Subspace(DefaultParamspace), ck, sk, fck, types.DefaultCodespace)

// set the distribution hooks on staking
sk = sk.WithHooks(keeper.Hooks())
sk.SetHooks(keeper.Hooks())

// set genesis items required for distribution
keeper.SetFeePool(ctx, types.InitialFeePool())
Expand Down
6 changes: 3 additions & 3 deletions x/mock/simulation/random_simulate_blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"time"

abci "github.com/tendermint/tendermint/abci/types"
common "github.com/tendermint/tendermint/libs/common"
cmn "github.com/tendermint/tendermint/libs/common"
tmtypes "github.com/tendermint/tendermint/types"

"github.com/cosmos/cosmos-sdk/baseapp"
Expand Down Expand Up @@ -65,7 +65,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
testingMode, t, b := getTestingMode(tb)
fmt.Printf("Starting SimulateFromSeed with randomness created with seed %d\n", int(seed))
r := rand.New(rand.NewSource(seed))
params := RandomParams(r)
params := RandomParams(r) // := DefaultParams()
fmt.Printf("Randomized simulation params: %+v\n", params)
timestamp := randTimestamp(r)
fmt.Printf("Starting the simulation from time %v, unixtime %v\n", timestamp.UTC().Format(time.UnixDate), timestamp.Unix())
Expand Down Expand Up @@ -365,7 +365,7 @@ func getKeys(validators map[string]mockValidator) []string {
}

// randomProposer picks a random proposer from the current validator set
func randomProposer(r *rand.Rand, validators map[string]mockValidator) common.HexBytes {
func randomProposer(r *rand.Rand, validators map[string]mockValidator) cmn.HexBytes {
keys := getKeys(validators)
if len(keys) == 0 {
return nil
Expand Down
10 changes: 9 additions & 1 deletion x/slashing/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr))
}

// Get validator.
validator := k.validatorSet.ValidatorByConsAddr(ctx, consAddr)
if validator == nil || validator.GetStatus() == sdk.Unbonded {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm - if MaxEvidenceAge is equal to the unbonding period, shouldn't this case be impossible? Is this an off-by-one problem due to the next val set offset? If so can we add a comment about it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++

I would replace // defensive with the actual comment describing what it's being defensive of.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simulation doesn't take this into account, and Tendermint might break this assumption at some point.

// Defensive.
// Simulation doesn't take unbonding periods into account, and
// Tendermint might break this assumption at some point.
return
}

// Double sign too old
maxEvidenceAge := k.MaxEvidenceAge(ctx)
if age > maxEvidenceAge {
Expand Down Expand Up @@ -80,7 +89,6 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
k.validatorSet.Slash(ctx, consAddr, distributionHeight, power, revisedFraction)

// Jail validator if not already jailed
validator := k.validatorSet.ValidatorByConsAddr(ctx, consAddr)
if !validator.GetJailed() {
k.validatorSet.Jail(ctx, consAddr)
}
Expand Down
4 changes: 2 additions & 2 deletions x/slashing/test_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s
}
require.Nil(t, err)
paramstore := paramsKeeper.Subspace(DefaultParamspace)
keeper := NewKeeper(cdc, keySlashing, sk, paramstore, DefaultCodespace)
sk = sk.WithHooks(keeper.Hooks())
keeper := NewKeeper(cdc, keySlashing, &sk, paramstore, DefaultCodespace)
sk.SetHooks(keeper.Hooks())

require.NotPanics(t, func() {
InitGenesis(ctx, keeper, GenesisState{defaults}, genesis)
Expand Down
25 changes: 18 additions & 7 deletions x/stake/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,27 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
}

// Called every block, update validator set
func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.ValidatorUpdate) {
func EndBlocker(ctx sdk.Context, k keeper.Keeper) (validatorUpdates []abci.ValidatorUpdate) {
endBlockerTags := sdk.EmptyTags()

// Reset the intra-transaction counter.
k.SetIntraTxCounter(ctx, 0)

// Calculate validator set changes.
//
// NOTE: ApplyAndReturnValidatorSetUpdates has to come before
// UnbondAllMatureValidatorQueue.
// This fixes a bug when the unbonding period is instant (is the case in
// some of the tests). The test expected the validator to be completely
// unbonded after the Endblocker (go from Bonded -> Unbonding during
// ApplyAndReturnValidatorSetUpdates and then Unbonding -> Unbonded during
// UnbondAllMatureValidatorQueue).
validatorUpdates = k.ApplyAndReturnValidatorSetUpdates(ctx)

// Unbond all mature validators from the unbonding queue.
k.UnbondAllMatureValidatorQueue(ctx)

// Remove all mature unbonding delegations from the ubd queue.
matureUnbonds := k.DequeueAllMatureUnbondingQueue(ctx, ctx.BlockHeader().Time)
for _, dvPair := range matureUnbonds {
err := k.CompleteUnbonding(ctx, dvPair.DelegatorAddr, dvPair.ValidatorAddr)
Expand All @@ -49,6 +65,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid
))
}

// Remove all mature redelegations from the red queue.
matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time)
for _, dvvTriplet := range matureRedelegations {
err := k.CompleteRedelegation(ctx, dvvTriplet.DelegatorAddr, dvvTriplet.ValidatorSrcAddr, dvvTriplet.ValidatorDstAddr)
Expand All @@ -62,12 +79,6 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid
tags.DstValidator, []byte(dvvTriplet.ValidatorDstAddr.String()),
))
}

// reset the intra-transaction counter
k.SetIntraTxCounter(ctx, 0)

// calculate validator set changes
ValidatorUpdates = k.ApplyAndReturnValidatorSetUpdates(ctx)
return
}

Expand Down
9 changes: 5 additions & 4 deletions x/stake/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,11 +494,12 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
var finishTime time.Time
// Jump to finishTime for unbonding period and remove from unbonding queue
types.MsgCdc.MustUnmarshalBinaryLengthPrefixed(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)

//Check that the account is unbonded
// Check that the validator is deleted from state
validators := keeper.GetValidators(ctx, 100)
require.Equal(t, len(validatorAddrs)-(i+1), len(validators),
"expected %d validators got %d", len(validatorAddrs)-(i+1), len(validators))
Expand Down Expand Up @@ -1013,7 +1014,7 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
EndBlocker(ctx, keeper)

// validator power should have been reduced to zero
// ergo validator should have been removed from the store
_, found = keeper.GetValidator(ctx, valA)
require.False(t, found)
// validator should be in unbonding state
validator, _ = keeper.GetValidator(ctx, valA)
require.Equal(t, validator.GetStatus(), sdk.Unbonding)
}
7 changes: 7 additions & 0 deletions x/stake/keeper/delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,13 @@ func (k Keeper) DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time
func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Coin,
validator types.Validator, subtractAccount bool) (newShares sdk.Dec, err sdk.Error) {

// In some situations, the exchange rate becomes invalid, e.g. if
// validator loses all tokens due to slashing. In this case,
// make all future delegations invalid.
if validator.DelegatorShareExRate().IsZero() {
return sdk.ZeroDec(), types.ErrDelegatorShareExRateInvalid(k.Codespace())
}

// Get or create the delegator delegation
delegation, found := k.GetDelegation(ctx, delAddr, validator.OperatorAddr)
if !found {
Expand Down
2 changes: 1 addition & 1 deletion x/stake/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, paramst
}

// Set the validator hooks
func (k Keeper) WithHooks(sh sdk.StakingHooks) Keeper {
func (k *Keeper) SetHooks(sh sdk.StakingHooks) *Keeper {
if k.hooks != nil {
panic("cannot set validator hooks twice")
}
Expand Down
7 changes: 1 addition & 6 deletions x/stake/keeper/slash.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,6 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
k.SetPool(ctx, pool)

// remove validator if it has no more tokens
if validator.DelegatorShares.IsZero() && validator.Status == sdk.Unbonded {
// if not unbonded, we must instead remove validator in EndBlocker once it finishes its unbonding period
k.RemoveValidator(ctx, validator.OperatorAddr)
}

// Log that a slash occurred!
logger.Info(fmt.Sprintf(
"validator %s slashed by slash factor of %s; burned %v tokens",
Expand Down Expand Up @@ -236,6 +230,7 @@ func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, re
if sharesToUnbond.GT(delegation.Shares) {
sharesToUnbond = delegation.Shares
}

tokensToBurn, err := k.unbond(ctx, redelegation.DelegatorAddr, redelegation.ValidatorDstAddr, sharesToUnbond)
if err != nil {
panic(fmt.Errorf("error unbonding delegator: %v", err))
Expand Down
24 changes: 12 additions & 12 deletions x/stake/keeper/slash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,9 +348,9 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
// read updated validator
// power decreased by 1 again, validator is out of stake
// ergo validator should have been removed from the store
_, found = keeper.GetValidatorByConsAddr(ctx, consAddr)
require.False(t, found)
// validator should be in unbonding period
validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr)
require.Equal(t, validator.GetStatus(), sdk.Unbonding)
}

// tests Slash at a previous height with a redelegation
Expand Down Expand Up @@ -450,16 +450,16 @@ func TestSlashWithRedelegation(t *testing.T) {
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
// read updated validator
// validator decreased to zero power, should have been removed from the store
_, found = keeper.GetValidatorByConsAddr(ctx, consAddr)
require.False(t, found)
// validator decreased to zero power, should be in unbonding period
validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr)
require.Equal(t, validator.GetStatus(), sdk.Unbonding)

// slash the validator again, by 100%
// no stake remains to be slashed
ctx = ctx.WithBlockHeight(12)
// validator no longer in the store
_, found = keeper.GetValidatorByConsAddr(ctx, consAddr)
require.False(t, found)
// validator still in unbonding period
validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr)
require.Equal(t, validator.GetStatus(), sdk.Unbonding)
keeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec())

// read updating redelegation
Expand All @@ -472,9 +472,9 @@ func TestSlashWithRedelegation(t *testing.T) {
// no more bonded tokens burned
require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
// read updated validator
// power still zero, still not in the store
_, found = keeper.GetValidatorByConsAddr(ctx, consAddr)
require.False(t, found)
// power still zero, still in unbonding period
validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr)
require.Equal(t, validator.GetStatus(), sdk.Unbonding)
}

// tests Slash at a previous height with both an unbonding delegation and a redelegation
Expand Down
9 changes: 2 additions & 7 deletions x/stake/keeper/val_state_change.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab
if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) {
updates = append(updates, validator.ABCIValidatorUpdate())

// XXX Assert that the validator had updated its ValidatorDistInfo.FeePoolWithdrawalHeight.
// XXX This hook probably shouldn't exist. Maybe rethink the hook system.
// 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.OnValidatorPowerDidChange(ctx, validator.ConsAddress(), valAddr)
}
Expand Down Expand Up @@ -108,11 +108,6 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab
// bonded to unbonding
k.bondedToUnbonding(ctx, validator)

// remove validator if it has no more tokens
sunnya97 marked this conversation as resolved.
Show resolved Hide resolved
if validator.Tokens.IsZero() {
k.RemoveValidator(ctx, validator.OperatorAddr)
}

// delete from the bonded validator index
k.DeleteLastValidatorPower(ctx, sdk.ValAddress(valAddrBytes))

Expand Down
7 changes: 5 additions & 2 deletions x/stake/keeper/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ func (k Keeper) RemoveValidatorTokensAndShares(ctx sdk.Context, validator types.

// Update the tokens of an existing validator, update the validators power index key
func (k Keeper) RemoveValidatorTokens(ctx sdk.Context, validator types.Validator, tokensToRemove sdk.Dec) types.Validator {

pool := k.GetPool(ctx)
k.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool = validator.RemoveTokens(pool, tokensToRemove)
Expand Down Expand Up @@ -189,6 +190,9 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) {
if !found {
return
}
if validator.Status != sdk.Unbonded {
panic("Cannot call RemoveValidator on bonded or unbonding validators")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}

// delete the old validator record
store := ctx.KVStore(k.storeKey)
Expand Down Expand Up @@ -357,10 +361,9 @@ func (k Keeper) UnbondAllMatureValidatorQueue(ctx sdk.Context) {
if !found || val.GetStatus() != sdk.Unbonding {
continue
}
k.unbondingToUnbonded(ctx, val)
if val.GetDelegatorShares().IsZero() {
k.RemoveValidator(ctx, val.OperatorAddr)
} else {
k.unbondingToUnbonded(ctx, val)
}
}
store.Delete(validatorTimesliceIterator.Key())
Expand Down
Loading