diff --git a/CHANGELOG.md b/CHANGELOG.md index bafe31c5cddd..31eff0d6ec91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### API Breaking Changes +* (x/staking) [#17486](https://github.com/cosmos/cosmos-sdk/pull/17486) Use collections for `RedelegationQueueKey`: + * remove from `types`: `GetRedelegationTimeKey` + * remove from `Keeper`: `RedelegationQueueIterator` * (x/staking) [#17562](https://github.com/cosmos/cosmos-sdk/pull/17562) Use collections for `ValidatorQueue` * remove from `types`: `GetValidatorQueueKey`, `ParseValidatorQueueKey` * remove from `Keeper`: `ValidatorQueueIterator` diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go index e777641a36fb..22fe53ec75ef 100644 --- a/x/staking/keeper/delegation.go +++ b/x/staking/keeper/delegation.go @@ -10,7 +10,6 @@ import ( "cosmossdk.io/collections" errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" - storetypes "cosmossdk.io/store/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -610,20 +609,9 @@ func (k Keeper) RemoveRedelegation(ctx context.Context, red types.Redelegation) // timeslice is a slice of DVVTriplets corresponding to redelegations that // expire at a certain time. func (k Keeper) GetRedelegationQueueTimeSlice(ctx context.Context, timestamp time.Time) (dvvTriplets []types.DVVTriplet, err error) { - store := k.storeService.OpenKVStore(ctx) - bz, err := store.Get(types.GetRedelegationTimeKey(timestamp)) - if err != nil { - return nil, err - } - - if bz == nil { - return []types.DVVTriplet{}, nil - } - - triplets := types.DVVTriplets{} - err = k.cdc.Unmarshal(bz, &triplets) - if err != nil { - return nil, err + triplets, err := k.RedelegationQueue.Get(ctx, timestamp) + if err != nil && !errors.Is(err, collections.ErrNotFound) { + return []types.DVVTriplet{}, err } return triplets.Triplets, nil @@ -631,12 +619,8 @@ func (k Keeper) GetRedelegationQueueTimeSlice(ctx context.Context, timestamp tim // SetRedelegationQueueTimeSlice sets a specific redelegation queue timeslice. func (k Keeper) SetRedelegationQueueTimeSlice(ctx context.Context, timestamp time.Time, keys []types.DVVTriplet) error { - store := k.storeService.OpenKVStore(ctx) - bz, err := k.cdc.Marshal(&types.DVVTriplets{Triplets: keys}) - if err != nil { - return err - } - return store.Set(types.GetRedelegationTimeKey(timestamp), bz) + triplets := types.DVVTriplets{Triplets: keys} + return k.RedelegationQueue.Set(ctx, timestamp, triplets) } // InsertRedelegationQueue insert an redelegation delegation to the appropriate @@ -660,38 +644,28 @@ func (k Keeper) InsertRedelegationQueue(ctx context.Context, red types.Redelegat return k.SetRedelegationQueueTimeSlice(ctx, completionTime, timeSlice) } -// RedelegationQueueIterator returns all the redelegation queue timeslices from -// time 0 until endTime. -func (k Keeper) RedelegationQueueIterator(ctx context.Context, endTime time.Time) (storetypes.Iterator, error) { - store := k.storeService.OpenKVStore(ctx) - return store.Iterator(types.RedelegationQueueKey, storetypes.InclusiveEndBytes(types.GetRedelegationTimeKey(endTime))) -} - // DequeueAllMatureRedelegationQueue returns a concatenated list of all the // timeslices inclusively previous to currTime, and deletes the timeslices from // the queue. func (k Keeper) DequeueAllMatureRedelegationQueue(ctx context.Context, currTime time.Time) (matureRedelegations []types.DVVTriplet, err error) { - store := k.storeService.OpenKVStore(ctx) + var keys []time.Time - // gets an iterator for all timeslices from time 0 until the current Blockheader time sdkCtx := sdk.UnwrapSDKContext(ctx) - redelegationTimesliceIterator, err := k.RedelegationQueueIterator(ctx, sdkCtx.HeaderInfo().Time) + + // gets an iterator for all timeslices from time 0 until the current Blockheader time + rng := (&collections.Range[time.Time]{}).EndInclusive(sdkCtx.HeaderInfo().Time) + err = k.RedelegationQueue.Walk(ctx, rng, func(key time.Time, value types.DVVTriplets) (bool, error) { + keys = append(keys, key) + matureRedelegations = append(matureRedelegations, value.Triplets...) + return false, nil + }) if err != nil { - return nil, err + return matureRedelegations, err } - defer redelegationTimesliceIterator.Close() - - for ; redelegationTimesliceIterator.Valid(); redelegationTimesliceIterator.Next() { - timeslice := types.DVVTriplets{} - value := redelegationTimesliceIterator.Value() - if err = k.cdc.Unmarshal(value, ×lice); err != nil { - return nil, err - } - - matureRedelegations = append(matureRedelegations, timeslice.Triplets...) - - if err = store.Delete(redelegationTimesliceIterator.Key()); err != nil { - return nil, err + for _, key := range keys { + err := k.RedelegationQueue.Remove(ctx, key) + if err != nil { + return matureRedelegations, err } } diff --git a/x/staking/keeper/keeper.go b/x/staking/keeper/keeper.go index c84be8549af7..e6692bab5ea8 100644 --- a/x/staking/keeper/keeper.go +++ b/x/staking/keeper/keeper.go @@ -68,6 +68,8 @@ type Keeper struct { RedelegationsByValSrc collections.Map[collections.Triple[[]byte, []byte, []byte], []byte] // UnbondingDelegationByValIndex key: valAddr+delAddr | value: none used (index key for UnbondingDelegations stored by validator index) UnbondingDelegationByValIndex collections.Map[collections.Pair[[]byte, []byte], []byte] + // RedelegationQueue key: Timestamp | value: DVVTriplets [delAddr+valSrcAddr+valDstAddr] + RedelegationQueue collections.Map[time.Time, types.DVVTriplets] // ValidatorQueue key: len(timestamp bytes)+timestamp+height | value: ValAddresses ValidatorQueue collections.Map[collections.Triple[uint64, time.Time, uint64], types.ValAddresses] // LastValidatorPower key: valAddr | value: power(gogotypes.Int64Value()) @@ -180,7 +182,8 @@ func NewKeeper( ), collections.BytesValue, ), - Validators: collections.NewMap(sb, types.ValidatorsKey, "validators", sdk.LengthPrefixedBytesKey, codec.CollValue[types.Validator](cdc)), // sdk.LengthPrefixedBytesKey is needed to retain state compatibility + RedelegationQueue: collections.NewMap(sb, types.RedelegationQueueKey, "redelegation_queue", sdk.TimeKey, codec.CollValue[types.DVVTriplets](cdc)), + Validators: collections.NewMap(sb, types.ValidatorsKey, "validators", sdk.LengthPrefixedBytesKey, codec.CollValue[types.Validator](cdc)), // sdk.LengthPrefixedBytesKey is needed to retain state compatibility UnbondingDelegations: collections.NewMap( sb, types.UnbondingDelegationKey, "unbonding_delegation", diff --git a/x/staking/keeper/keeper_test.go b/x/staking/keeper/keeper_test.go index e5f1052893c3..488afc863905 100644 --- a/x/staking/keeper/keeper_test.go +++ b/x/staking/keeper/keeper_test.go @@ -168,6 +168,14 @@ func getREDsFromValSrcIndexKey(valSrcAddr sdk.ValAddress) []byte { return append(redelegationByValSrcIndexKey, addresstypes.MustLengthPrefix(valSrcAddr)...) } +// getRedelegationTimeKey returns a key prefix for indexing an unbonding +// redelegation based on a completion time. +func getRedelegationTimeKey(timestamp time.Time) []byte { + bz := sdk.FormatTimeBytes(timestamp) + redelegationQueueKey := []byte{0x42} + return append(redelegationQueueKey, bz...) +} + // getUBDKey creates the key for an unbonding delegation by delegator and validator addr // VALUE: staking/UnbondingDelegation func getUBDKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte { @@ -510,6 +518,56 @@ func (s *KeeperTestSuite) TestValidatorQueueMigrationToColls() { s.Require().NoError(err) } +func (s *KeeperTestSuite) TestRedelegationQueueMigrationToColls() { + s.SetupTest() + + addrs, valAddrs := createValAddrs(101) + err := testutil.DiffCollectionsMigration( + s.ctx, + s.key, + 100, + func(i int64) { + date := time.Unix(i, i) + dvvTriplets := stakingtypes.DVVTriplets{ + Triplets: []stakingtypes.DVVTriplet{ + { + DelegatorAddress: addrs[i].String(), + ValidatorSrcAddress: valAddrs[i].String(), + ValidatorDstAddress: valAddrs[i+1].String(), + }, + }, + } + bz, err := s.cdc.Marshal(&dvvTriplets) + s.Require().NoError(err) + s.ctx.KVStore(s.key).Set(getRedelegationTimeKey(date), bz) + }, + "de104dd19c7a72c6b0ad03d25c897313bb1473befc118952ad88e6a8726749c9", + ) + s.Require().NoError(err) + + err = testutil.DiffCollectionsMigration( + s.ctx, + s.key, + 100, + func(i int64) { + date := time.Unix(i, i) + dvvTriplets := stakingtypes.DVVTriplets{ + Triplets: []stakingtypes.DVVTriplet{ + { + DelegatorAddress: addrs[i].String(), + ValidatorSrcAddress: valAddrs[i].String(), + ValidatorDstAddress: valAddrs[i+1].String(), + }, + }, + } + err := s.stakingKeeper.SetRedelegationQueueTimeSlice(s.ctx, date, dvvTriplets.Triplets) + s.Require().NoError(err) + }, + "de104dd19c7a72c6b0ad03d25c897313bb1473befc118952ad88e6a8726749c9", + ) + s.Require().NoError(err) +} + func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) } diff --git a/x/staking/migrations/v2/store_test.go b/x/staking/migrations/v2/store_test.go index bbd865f3cde1..2e8f465a2840 100644 --- a/x/staking/migrations/v2/store_test.go +++ b/x/staking/migrations/v2/store_test.go @@ -106,7 +106,7 @@ func TestStoreMigration(t *testing.T) { { "RedelegationQueueKey", v1.GetRedelegationTimeKey(now), - types.GetRedelegationTimeKey(now), + getRedelegationTimeKey(now), }, { "ValidatorQueueKey", @@ -141,6 +141,11 @@ func TestStoreMigration(t *testing.T) { } } +func getRedelegationTimeKey(timestamp time.Time) []byte { + bz := sdk.FormatTimeBytes(timestamp) + return append(types.RedelegationQueueKey, bz...) +} + func getLastValidatorPowerKey(operator sdk.ValAddress) []byte { return append(types.LastValidatorPowerKey, sdkaddress.MustLengthPrefix(operator)...) } diff --git a/x/staking/types/keys.go b/x/staking/types/keys.go index 600cf56bb11d..0b208df770d8 100644 --- a/x/staking/types/keys.go +++ b/x/staking/types/keys.go @@ -2,7 +2,6 @@ package types import ( "encoding/binary" - "time" "cosmossdk.io/collections" addresscodec "cosmossdk.io/core/address" @@ -50,7 +49,7 @@ var ( UnbondingTypeKey = collections.NewPrefix(57) // prefix for an index containing the type of unbonding operations UnbondingQueueKey = collections.NewPrefix(65) // prefix for the timestamps in unbonding queue - RedelegationQueueKey = []byte{0x42} // prefix for the timestamps in redelegations queue + RedelegationQueueKey = collections.NewPrefix(66) // prefix for the timestamps in redelegations queue ValidatorQueueKey = collections.NewPrefix(67) // prefix for the timestamps in validator queue HistoricalInfoKey = collections.NewPrefix(80) // prefix for the historical info @@ -161,13 +160,6 @@ func GetREDKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) [] return key } -// GetRedelegationTimeKey returns a key prefix for indexing an unbonding -// redelegation based on a completion time. -func GetRedelegationTimeKey(timestamp time.Time) []byte { - bz := sdk.FormatTimeBytes(timestamp) - return append(RedelegationQueueKey, bz...) -} - // GetREDsKey returns a key prefix for indexing a redelegation from a delegator // address. func GetREDsKey(delAddr sdk.AccAddress) []byte {