diff --git a/PENDING.md b/PENDING.md index aa7cca2c4a2c..02d136bbce26 100644 --- a/PENDING.md +++ b/PENDING.md @@ -37,6 +37,7 @@ IMPROVEMENTS * SDK - #2573 [x/distribution] add accum invariance + - #2610 [x/stake] Block redelegation to and from the same validator * Tendermint diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index a5d08d4892a9..97acc2404206 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -541,6 +541,10 @@ func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress, valAd func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, sharesAmount sdk.Dec) (types.Redelegation, sdk.Error) { + if bytes.Equal(valSrcAddr, valDstAddr) { + return types.Redelegation{}, types.ErrSelfRedelegation(k.Codespace()) + } + // check if there is already a redelgation in progress from src to dst // TODO quick fix, instead we should use an index, see https://github.com/cosmos/cosmos-sdk/issues/1402 _, found := k.GetRedelegation(ctx, delAddr, valSrcAddr, valDstAddr) diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index 24476ca3cc16..fcf2f42067a1 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -581,6 +581,32 @@ func TestRedelegation(t *testing.T) { require.Equal(t, 0, len(redelegations)) } +func TestRedelegateToSameValidator(t *testing.T) { + + ctx, _, keeper := CreateTestInput(t, false, 0) + pool := keeper.GetPool(ctx) + pool.LooseTokens = sdk.NewDec(30) + + // create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator = TestingUpdateValidator(keeper, ctx, validator) + pool = keeper.GetPool(ctx) + val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + selfDelegation := types.Delegation{ + DelegatorAddr: val0AccAddr, + ValidatorAddr: addrVals[0], + Shares: issuedShares, + } + keeper.SetDelegation(ctx, selfDelegation) + + _, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[0], sdk.NewDec(5)) + require.Error(t, err) + +} + func TestRedelegateSelfDelegation(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index e8c85800fe3f..d76eb39c15f0 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -155,6 +155,10 @@ func ErrNoRedelegation(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "no redelegation found") } +func ErrSelfRedelegation(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "cannot redelegate to the same validator") +} + func ErrBadRedelegationDst(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "redelegation validator not found") }