From cb0c593ce176917b02800ef80e0ad2a1ab4debb2 Mon Sep 17 00:00:00 2001 From: JoowonYun Date: Wed, 20 Dec 2023 19:10:13 +0900 Subject: [PATCH 1/5] fix: dust share --- x/staking/keeper/delegation.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go index 651cc6310b62..5ff226ec7bbd 100644 --- a/x/staking/keeper/delegation.go +++ b/x/staking/keeper/delegation.go @@ -1201,7 +1201,12 @@ func (k Keeper) ValidateUnbondAmount( // due to rounding, however we don't want to truncate the shares or take the // minimum because we want to allow for the full withdraw of shares from a // delegation. - if shares.GT(delShares) { + tolerance, err := validator.SharesFromTokens(math.OneInt()) + if err != nil { + return shares, err + } + + if delShares.Sub(shares).LT(tolerance) { shares = delShares } From b195a4be7348172c71e770f0064a7db06f2fafe2 Mon Sep 17 00:00:00 2001 From: JoowonYun Date: Thu, 21 Dec 2023 17:13:06 +0900 Subject: [PATCH 2/5] test: dust undelegtion case --- x/staking/keeper/delegation_test.go | 48 +++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/x/staking/keeper/delegation_test.go b/x/staking/keeper/delegation_test.go index f4915506e136..e5b6b27a5ca5 100644 --- a/x/staking/keeper/delegation_test.go +++ b/x/staking/keeper/delegation_test.go @@ -1170,3 +1170,51 @@ func (s *KeeperTestSuite) TestSetUnbondingDelegationEntry() { // unbondingID comes from a global counter -> gaps in unbondingIDs are OK as long as every unbondingID is unique require.Equal(uint64(2), resUnbonding.Entries[1].UnbondingId) } + +func (s *KeeperTestSuite) TestUndelegateWithDustShare() { + ctx, keeper := s.ctx, s.stakingKeeper + require := s.Require() + + addrDels, valAddrs := createValAddrs(2) + + s.accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec("cosmos")).AnyTimes() + + // construct the validators[0] & slash 1stake + amt := math.NewInt(100) + validator := testutil.NewValidator(s.T(), valAddrs[0], PKs[0]) + validator, _ = validator.AddTokensFromDel(amt) + validator = validator.RemoveTokens(math.NewInt(1)) + validator = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + + // first add a validators[0] to delegate too + bond1to1 := stakingtypes.NewDelegation(addrDels[0].String(), valAddrs[0].String(), math.LegacyNewDec(100)) + require.NoError(keeper.SetDelegation(ctx, bond1to1)) + resBond, err := keeper.Delegations.Get(ctx, collections.Join(addrDels[0], valAddrs[0])) + require.NoError(err) + require.Equal(bond1to1, resBond) + + // second delegators[1] add a validators[0] to delegate + bond2to1 := stakingtypes.NewDelegation(addrDels[1].String(), valAddrs[0].String(), math.LegacyNewDec(1)) + validator, delegatorShare := validator.AddTokensFromDel(math.NewInt(1)) + bond2to1.Shares = delegatorShare + _ = stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) + require.NoError(keeper.SetDelegation(ctx, bond2to1)) + resBond, err = keeper.Delegations.Get(ctx, collections.Join(addrDels[1], valAddrs[0])) + require.NoError(err) + require.Equal(bond2to1, resBond) + + // check delegation state + delegations, err := keeper.GetValidatorDelegations(ctx, valAddrs[0]) + require.NoError(err) + require.Equal(2, len(delegations)) + + // undelegate all delegator[0]'s delegate + _, err = s.msgServer.Undelegate(ctx, stakingtypes.NewMsgUndelegate(addrDels[0].String(), valAddrs[0].String(), sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(99)))) + require.NoError(err) + + // remain only delegator[1]'s delegate + delegations, err = keeper.GetValidatorDelegations(ctx, valAddrs[0]) + require.NoError(err) + require.Equal(1, len(delegations)) + require.Equal(delegations[0].DelegatorAddress, addrDels[1].String()) +} From b23d00f6b599f514ab0540ebe44396f0d9175514 Mon Sep 17 00:00:00 2001 From: JoowonYun Date: Thu, 28 Dec 2023 15:11:16 +0900 Subject: [PATCH 3/5] docs: dust share --- CHANGELOG.md | 2 ++ x/staking/keeper/delegation.go | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a420fb9b005..afa093be7754 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,6 +93,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (simulation) [#18196](https://github.com/cosmos/cosmos-sdk/pull/18196) Fix the problem of `validator set is empty after InitGenesis` in simulation test. * (baseapp) [#18551](https://github.com/cosmos/cosmos-sdk/pull/18551) Fix SelectTxForProposal the calculation method of tx bytes size is inconsistent with CometBFT * (baseapp) [#18895](https://github.com/cosmos/cosmos-sdk/pull/18895) Fix de-duplicating vote extensions during validation in ValidateVoteExtensions. +* (x/staking) [#18841](https://github.com/cosmos/cosmos-sdk/pull/18841) Fix delegation state when it has dust share. Change the condition of unbond to remove delegation with less than minimum share ### API Breaking Changes @@ -197,6 +198,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/upgrade) [#16244](https://github.com/cosmos/cosmos-sdk/pull/16244) Upgrade module no longer stores the app version but gets and sets the app version stored in the `ParamStore` of baseapp. * (x/staking) [#17655](https://github.com/cosmos/cosmos-sdk/pull/17655) `HistoricalInfo` was replaced with `HistoricalRecord`, it removes the validator set and comet header and only keep what is needed for IBC. +* (x/staking) [#18841](https://github.com/cosmos/cosmos-sdk/pull/18841) Delegates with less than the minimum share removed. ## [v0.50.2](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.2) - 2023-12-11 diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go index 5ff226ec7bbd..3802b373c789 100644 --- a/x/staking/keeper/delegation.go +++ b/x/staking/keeper/delegation.go @@ -1197,10 +1197,9 @@ func (k Keeper) ValidateUnbondAmount( return shares, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid shares amount") } - // Cap the shares at the delegation's shares. Shares being greater could occur - // due to rounding, however we don't want to truncate the shares or take the - // minimum because we want to allow for the full withdraw of shares from a - // delegation. + // Depending on the share, amount can be smaller than unit amount(1stake). + // If the remain amount after unbonding is smaller than the minimum share, + // it's completely unbonded to avoid leaving dust shares. tolerance, err := validator.SharesFromTokens(math.OneInt()) if err != nil { return shares, err From ff6e240ef4c97cf122c52a25c28ae4c6d75f9a59 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Thu, 28 Dec 2023 13:25:19 -0500 Subject: [PATCH 4/5] Update CHANGELOG.md Co-authored-by: atheeshp <59333759+atheeshp@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afa093be7754..c1f6bd6a68b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -198,7 +198,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/upgrade) [#16244](https://github.com/cosmos/cosmos-sdk/pull/16244) Upgrade module no longer stores the app version but gets and sets the app version stored in the `ParamStore` of baseapp. * (x/staking) [#17655](https://github.com/cosmos/cosmos-sdk/pull/17655) `HistoricalInfo` was replaced with `HistoricalRecord`, it removes the validator set and comet header and only keep what is needed for IBC. -* (x/staking) [#18841](https://github.com/cosmos/cosmos-sdk/pull/18841) Delegates with less than the minimum share removed. +* (x/staking) [#18841](https://github.com/cosmos/cosmos-sdk/pull/18841) Delegations with less than the minimum share removed. ## [v0.50.2](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.50.2) - 2023-12-11 From 82b9fa49be003e5e3552798a11d5e15db1ad87db Mon Sep 17 00:00:00 2001 From: Facundo Medica <14063057+facundomedica@users.noreply.github.com> Date: Tue, 20 Feb 2024 17:31:06 +0100 Subject: [PATCH 5/5] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 449f5b9ef465..12968844377a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,7 +96,7 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i ### State Machine Breaking -* (x/staking) [#18841](https://github.com/cosmos/cosmos-sdk/pull/18841) In a undelegation or redelegation if the shares being un/re-delegated left correspond to less than 1 token (in base denom) the entire delegation gets removed. +* (x/staking) [#18841](https://github.com/cosmos/cosmos-sdk/pull/18841) In a undelegation or redelegation if the shares being left delegated correspond to less than 1 token (in base denom) the entire delegation gets removed. ### API Breaking Changes @@ -2794,4 +2794,4 @@ sure you are aware of any relevant breaking changes. ## Previous Releases -[CHANGELOG of previous versions](https://github.com/cosmos/cosmos-sdk/blob/c17c3caab86a1426a1eef4541e8203f5f54a1a54/CHANGELOG.md#v0391---2020-08-11) (pre Stargate). \ No newline at end of file +[CHANGELOG of previous versions](https://github.com/cosmos/cosmos-sdk/blob/c17c3caab86a1426a1eef4541e8203f5f54a1a54/CHANGELOG.md#v0391---2020-08-11) (pre Stargate).