diff --git a/x/stake/cross_stake/cross_stake.go b/x/stake/cross_stake/cross_stake.go index 275f7b8ff..b7357643e 100644 --- a/x/stake/cross_stake/cross_stake.go +++ b/x/stake/cross_stake/cross_stake.go @@ -227,9 +227,28 @@ func (app *CrossStakeApp) handleUndelegate(ctx sdk.Context, pack *types.CrossSta }, errCode, nil } - _, err := app.stakeKeeper.BeginUnbonding(ctx.WithCrossStake(true), delAddr, pack.Validator, shares) - if err != nil { - return sdk.ExecuteResult{}, errCode, err + validator, found := app.stakeKeeper.GetValidator(ctx, pack.Validator) + if !found { + errCode = CrossStakeErrBadDelegation + return sdk.ExecuteResult{ + Err: types.ErrNoValidatorFound(app.stakeKeeper.Codespace()), + }, errCode, nil + } + + if sdk.IsUpgrade(sdk.FirstSunsetFork) && !validator.IsSelfDelegator(delAddr) { + // unbound the delegation directly, do not wait for the breathe block + // this is to prevent too many user get the coins back in the breathe block + // but self delegation still needs to wait until the unbonding period + + _, _, err := app.stakeKeeper.UnboundDelegation(ctx.WithCrossStake(true), delAddr, pack.Validator, shares) + if err != nil { + return sdk.ExecuteResult{}, errCode, err + } + } else { + _, err := app.stakeKeeper.BeginUnbonding(ctx.WithCrossStake(true), delAddr, pack.Validator, shares, false) + if err != nil { + return sdk.ExecuteResult{}, errCode, err + } } // publish undelegate event diff --git a/x/stake/handler.go b/x/stake/handler.go index cc4aa6425..cc9e754a7 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -464,7 +464,7 @@ func handleMsgUndelegate(ctx sdk.Context, msg types.MsgUndelegate, k keeper.Keep } func handleMsgBeginUnbonding(ctx sdk.Context, msg types.MsgBeginUnbonding, k keeper.Keeper) sdk.Result { - ubd, err := k.BeginUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr, msg.SharesAmount) + ubd, err := k.BeginUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr, msg.SharesAmount, true) if err != nil { return err.Result() } diff --git a/x/stake/handler_sidechain.go b/x/stake/handler_sidechain.go index a44a91055..75fa531e1 100644 --- a/x/stake/handler_sidechain.go +++ b/x/stake/handler_sidechain.go @@ -437,9 +437,30 @@ func handleMsgSideChainUndelegate(ctx sdk.Context, msg MsgSideChainUndelegate, k return err.Result() } - ubd, err := k.BeginUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr, shares) - if err != nil { - return err.Result() + var ( + ubd types.UnbondingDelegation + events sdk.Events + ) + + validator, found := k.GetValidator(ctx, msg.ValidatorAddr) + if !found { + return types.ErrNoValidatorFound(k.Codespace()).Result() + } + + if sdk.IsUpgrade(sdk.FirstSunsetFork) && !validator.IsSelfDelegator(msg.DelegatorAddr) { + // unbound the delegation directly, do not wait for the breathe block + // this is to prevent too many user get the coins back in the breathe block + // but self delegation still needs to wait until the unbonding period + + ubd, events, err = k.UnboundDelegation(ctx, msg.DelegatorAddr, msg.ValidatorAddr, shares) + if err != nil { + return err.Result() + } + } else { + ubd, err = k.BeginUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr, shares, true) + if err != nil { + return err.Result() + } } finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(ubd.MinTime) @@ -468,7 +489,7 @@ func handleMsgSideChainUndelegate(ctx sdk.Context, msg MsgSideChainUndelegate, k k.PbsbServer.Publish(event) } - return sdk.Result{Data: finishTime, Tags: tags} + return sdk.Result{Data: finishTime, Tags: tags, Events: events} } // we allow the self-delegator delegating/redelegating to its validator. diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index fab870dc3..2a35eae22 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -658,9 +658,25 @@ func (k Keeper) getBeginInfo(ctx sdk.Context, valSrcAddr sdk.ValAddress) ( } } +func (k Keeper) UnboundDelegation(ctx sdk.Context, + delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec, +) (types.UnbondingDelegation, sdk.Events, sdk.Error) { + ubd, err := k.BeginUnbonding(ctx, delAddr, valAddr, sharesAmount, false) + if err != nil { + return ubd, nil, err + } + ubd, events, err := k.CompleteUnbonding(ctx, ubd.DelegatorAddr, ubd.ValidatorAddr) + if err != nil { + return ubd, events, err + } + + return ubd, events, nil +} + // begin unbonding an unbonding record func (k Keeper) BeginUnbonding(ctx sdk.Context, - delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) (types.UnbondingDelegation, sdk.Error) { + delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec, + enqueue bool) (types.UnbondingDelegation, sdk.Error) { // TODO quick fix, instead we should use an index, see https://github.com/cosmos/cosmos-sdk/issues/1402 _, found := k.GetUnbondingDelegation(ctx, delAddr, valAddr) @@ -677,6 +693,9 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context, balance := sdk.NewCoin(k.BondDenom(ctx), returnAmount.RawInt()) completionTime := ctx.BlockHeader().Time.Add(k.UnbondingTime(ctx)) + if !enqueue { + completionTime = ctx.BlockHeader().Time + } ubd := types.UnbondingDelegation{ DelegatorAddr: delAddr, ValidatorAddr: valAddr, @@ -687,7 +706,9 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context, CrossStake: ctx.CrossStake(), } k.SetUnbondingDelegation(ctx, ubd) - k.InsertUnbondingQueue(ctx, ubd) + if enqueue { + k.InsertUnbondingQueue(ctx, ubd) + } return ubd, nil } diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index 6a32c8a3b..687cb07b0 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -283,7 +283,7 @@ func TestUndelegateSelfDelegation(t *testing.T) { keeper.SetDelegation(ctx, delegation) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - _, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDecWithoutFra(10)) + _, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDecWithoutFra(10), true) require.NoError(t, err) // end block @@ -337,7 +337,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { // unbond the all self-delegation to put validator in unbonding state val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - _, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDecWithoutFra(10)) + _, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDecWithoutFra(10), true) require.NoError(t, err) // end block @@ -357,7 +357,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { ctx = ctx.WithBlockTime(blockTime2) // unbond some of the other delegation's shares - _, err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDecWithoutFra(6)) + _, err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDecWithoutFra(6), true) require.NoError(t, err) // retrieve the unbonding delegation @@ -409,7 +409,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) { ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDecWithoutFra(10)) + _, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDecWithoutFra(10), true) require.NoError(t, err) // end block @@ -439,7 +439,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) { require.Equal(t, 1, len(matureUbds)) // unbond all the other delegation's shares - _, err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDecWithoutFra(10)) + _, err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDecWithoutFra(10), true) require.NoError(t, err) ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) @@ -491,7 +491,7 @@ func TestUnbondingAllDelegationFromValidator(t *testing.T) { ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDecWithoutFra(10)) + _, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDecWithoutFra(10), true) require.NoError(t, err) // end block @@ -499,7 +499,7 @@ func TestUnbondingAllDelegationFromValidator(t *testing.T) { require.Equal(t, 1, len(updates)) // unbond all the remaining delegation - _, err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDecWithoutFra(10)) + _, err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDecWithoutFra(10), true) require.NoError(t, err) // validator should still be in state and still be in unbonding state @@ -713,7 +713,7 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { ctx = ctx.WithBlockHeader(header) // unbond the all self-delegation to put validator in unbonding state - _, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDecWithoutFra(10)) + _, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDecWithoutFra(10), true) require.NoError(t, err) // end block @@ -795,7 +795,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) { ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDecWithoutFra(10)) + _, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDecWithoutFra(10), true) require.NoError(t, err) // end block diff --git a/x/stake/querier/queryable_test.go b/x/stake/querier/queryable_test.go index cfad6b733..64ebb11da 100644 --- a/x/stake/querier/queryable_test.go +++ b/x/stake/querier/queryable_test.go @@ -305,7 +305,7 @@ func TestQueryDelegation(t *testing.T) { require.NotNil(t, err) // Query unbonging delegation - keeper.BeginUnbonding(ctx, addrAcc2, val1.OperatorAddr, sdk.NewDec(sdk.NewDecWithoutFra(10).RawInt())) + keeper.BeginUnbonding(ctx, addrAcc2, val1.OperatorAddr, sdk.NewDec(sdk.NewDecWithoutFra(10).RawInt()), true) query = abci.RequestQuery{ Path: "/custom/stake/unbondingDelegation",