diff --git a/CHANGELOG.md b/CHANGELOG.md index 39e212961353..f306fc1c13de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### API Breaking Changes +* (x/staking) [#17291](https://github.com/cosmos/cosmos-sdk/pull/17291) Use collections for `UnbondingDelegationByValIndex`: + * remove from `types`: `GetUBDKeyFromValIndexKey`, `GetUBDsByValIndexKey`, `GetUBDByValIndexKey` * (x/slashing) [#17568](https://github.com/cosmos/cosmos-sdk/pull/17568) Use collections for `ValidatorMissedBlockBitmap`: * remove from `types`: `ValidatorMissedBlockBitmapPrefixKey`, `ValidatorMissedBlockBitmapKey` * (x/staking) [#17481](https://github.com/cosmos/cosmos-sdk/pull/17481) Use collections for `UnbondingQueue`: diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go index 801a370808fb..e777641a36fb 100644 --- a/x/staking/keeper/delegation.go +++ b/x/staking/keeper/delegation.go @@ -167,26 +167,29 @@ func (k Keeper) GetUnbondingDelegation(ctx context.Context, delAddr sdk.AccAddre // particular validator. func (k Keeper) GetUnbondingDelegationsFromValidator(ctx context.Context, valAddr sdk.ValAddress) (ubds []types.UnbondingDelegation, err error) { store := k.storeService.OpenKVStore(ctx) - prefix := types.GetUBDsByValIndexKey(valAddr) - iterator, err := store.Iterator(prefix, storetypes.PrefixEndBytes(prefix)) + rng := collections.NewPrefixedPairRange[[]byte, []byte](valAddr) + err = k.UnbondingDelegationByValIndex.Walk( + ctx, + rng, + func(key collections.Pair[[]byte, []byte], value []byte) (stop bool, err error) { + valAddr := key.K1() + delAddr := key.K2() + ubdkey := types.GetUBDKey(delAddr, valAddr) + ubdValue, err := store.Get(ubdkey) + if err != nil { + return true, err + } + unbondingDelegation, err := types.UnmarshalUBD(k.cdc, ubdValue) + if err != nil { + return true, err + } + ubds = append(ubds, unbondingDelegation) + return false, nil + }, + ) if err != nil { return ubds, err } - defer iterator.Close() - - for ; iterator.Valid(); iterator.Next() { - key := types.GetUBDKeyFromValIndexKey(iterator.Key()) - value, err := store.Get(key) - if err != nil { - return ubds, err - } - ubd, err := types.UnmarshalUBD(k.cdc, value) - if err != nil { - return ubds, err - } - ubds = append(ubds, ubd) - } - return ubds, nil } @@ -263,8 +266,6 @@ func (k Keeper) HasMaxUnbondingDelegationEntries(ctx context.Context, delegatorA // SetUnbondingDelegation sets the unbonding delegation and associated index. func (k Keeper) SetUnbondingDelegation(ctx context.Context, ubd types.UnbondingDelegation) error { - store := k.storeService.OpenKVStore(ctx) - delAddr, err := k.authKeeper.AddressCodec().StringToBytes(ubd.DelegatorAddress) if err != nil { return err @@ -278,12 +279,11 @@ func (k Keeper) SetUnbondingDelegation(ctx context.Context, ubd types.UnbondingD return err } - return store.Set(types.GetUBDByValIndexKey(delAddr, valAddr), []byte{}) // index, store empty bytes + return k.UnbondingDelegationByValIndex.Set(ctx, collections.Join(valAddr, delAddr), []byte{}) } // RemoveUnbondingDelegation removes the unbonding delegation object and associated index. func (k Keeper) RemoveUnbondingDelegation(ctx context.Context, ubd types.UnbondingDelegation) error { - store := k.storeService.OpenKVStore(ctx) delAddr, err := k.authKeeper.AddressCodec().StringToBytes(ubd.DelegatorAddress) if err != nil { return err @@ -297,7 +297,7 @@ func (k Keeper) RemoveUnbondingDelegation(ctx context.Context, ubd types.Unbondi return err } - return store.Delete(types.GetUBDByValIndexKey(delAddr, valAddr)) + return k.UnbondingDelegationByValIndex.Remove(ctx, collections.Join(valAddr, delAddr)) } // SetUnbondingDelegationEntry adds an entry to the unbonding delegation at diff --git a/x/staking/keeper/grpc_query.go b/x/staking/keeper/grpc_query.go index ca87fcacc743..c7891e772546 100644 --- a/x/staking/keeper/grpc_query.go +++ b/x/staking/keeper/grpc_query.go @@ -172,7 +172,6 @@ func (k Querier) ValidatorUnbondingDelegations(ctx context.Context, req *types.Q if req.ValidatorAddr == "" { return nil, status.Error(codes.InvalidArgument, "validator address cannot be empty") } - var ubds types.UnbondingDelegations valAddr, err := k.validatorAddressCodec.StringToBytes(req.ValidatorAddr) if err != nil { @@ -180,21 +179,32 @@ func (k Querier) ValidatorUnbondingDelegations(ctx context.Context, req *types.Q } store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx)) - srcValPrefix := types.GetUBDsByValIndexKey(valAddr) - ubdStore := prefix.NewStore(store, srcValPrefix) - pageRes, err := query.Paginate(ubdStore, req.Pagination, func(key, value []byte) error { - storeKey := types.GetUBDKeyFromValIndexKey(append(srcValPrefix, key...)) - storeValue := store.Get(storeKey) + keys, pageRes, err := query.CollectionPaginate( + ctx, + k.UnbondingDelegationByValIndex, + req.Pagination, + func(key collections.Pair[[]byte, []byte], value []byte) (collections.Pair[[]byte, []byte], error) { + return key, nil + }, + query.WithCollectionPaginationPairPrefix[[]byte, []byte](valAddr), + ) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + // loop over the collected keys and fetch unbonding delegations + var ubds []types.UnbondingDelegation + for _, key := range keys { + valAddr := key.K1() + delAddr := key.K2() + ubdKey := types.GetUBDKey(delAddr, valAddr) + storeValue := store.Get(ubdKey) ubd, err := types.UnmarshalUBD(k.cdc, storeValue) if err != nil { - return err + return nil, err } ubds = append(ubds, ubd) - return nil - }) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) } return &types.QueryValidatorUnbondingDelegationsResponse{ diff --git a/x/staking/keeper/keeper.go b/x/staking/keeper/keeper.go index 74543035580e..b4e22ee4f095 100644 --- a/x/staking/keeper/keeper.go +++ b/x/staking/keeper/keeper.go @@ -34,22 +34,23 @@ type Keeper struct { validatorAddressCodec addresscodec.Codec consensusAddressCodec addresscodec.Codec - Schema collections.Schema - HistoricalInfo collections.Map[uint64, types.HistoricalInfo] - LastTotalPower collections.Item[math.Int] - ValidatorUpdates collections.Item[types.ValidatorUpdates] - DelegationsByValidator collections.Map[collections.Pair[sdk.ValAddress, sdk.AccAddress], []byte] - UnbondingID collections.Sequence - ValidatorByConsensusAddress collections.Map[sdk.ConsAddress, sdk.ValAddress] - UnbondingType collections.Map[uint64, uint64] - Redelegations collections.Map[collections.Triple[[]byte, []byte, []byte], types.Redelegation] - Delegations collections.Map[collections.Pair[sdk.AccAddress, sdk.ValAddress], types.Delegation] - UnbondingIndex collections.Map[uint64, []byte] - UnbondingQueue collections.Map[time.Time, types.DVPairs] - Validators collections.Map[[]byte, types.Validator] - UnbondingDelegations collections.Map[collections.Pair[[]byte, []byte], types.UnbondingDelegation] - RedelegationsByValDst collections.Map[collections.Triple[[]byte, []byte, []byte], []byte] - RedelegationsByValSrc collections.Map[collections.Triple[[]byte, []byte, []byte], []byte] + Schema collections.Schema + HistoricalInfo collections.Map[uint64, types.HistoricalInfo] + LastTotalPower collections.Item[math.Int] + ValidatorUpdates collections.Item[types.ValidatorUpdates] + DelegationsByValidator collections.Map[collections.Pair[sdk.ValAddress, sdk.AccAddress], []byte] + UnbondingID collections.Sequence + ValidatorByConsensusAddress collections.Map[sdk.ConsAddress, sdk.ValAddress] + UnbondingType collections.Map[uint64, uint64] + Redelegations collections.Map[collections.Triple[[]byte, []byte, []byte], types.Redelegation] + Delegations collections.Map[collections.Pair[sdk.AccAddress, sdk.ValAddress], types.Delegation] + UnbondingIndex collections.Map[uint64, []byte] + UnbondingQueue collections.Map[time.Time, types.DVPairs] + Validators collections.Map[[]byte, types.Validator] + UnbondingDelegations collections.Map[collections.Pair[[]byte, []byte], types.UnbondingDelegation] + RedelegationsByValDst collections.Map[collections.Triple[[]byte, []byte, []byte], []byte] + RedelegationsByValSrc collections.Map[collections.Triple[[]byte, []byte, []byte], []byte] + UnbondingDelegationByValIndex collections.Map[collections.Pair[[]byte, []byte], []byte] } // NewKeeper creates a new staking Keeper instance @@ -127,6 +128,12 @@ func NewKeeper( codec.CollValue[types.Redelegation](cdc), ), UnbondingIndex: collections.NewMap(sb, types.UnbondingIndexKey, "unbonding_index", collections.Uint64Key, collections.BytesValue), + UnbondingDelegationByValIndex: collections.NewMap( + sb, types.UnbondingDelegationByValIndexKey, + "unbonding_delegation_by_val_index", + collections.PairKeyCodec(sdk.LengthPrefixedBytesKey, sdk.LengthPrefixedBytesKey), // sdk.LengthPrefixedBytesKey is needed to retain state compatibility + collections.BytesValue, + ), UnbondingQueue: collections.NewMap(sb, types.UnbondingQueueKey, "unbonidng_queue", sdk.TimeKey, codec.CollValue[types.DVPairs](cdc)), // key format is: 53 | lengthPrefixedBytes(SrcValAddr) | lengthPrefixedBytes(AccAddr) | lengthPrefixedBytes(DstValAddr) RedelegationsByValSrc: collections.NewMap( diff --git a/x/staking/keeper/keeper_test.go b/x/staking/keeper/keeper_test.go index bba448548031..77d09991354e 100644 --- a/x/staking/keeper/keeper_test.go +++ b/x/staking/keeper/keeper_test.go @@ -174,6 +174,13 @@ func getUBDKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte { return append(append(unbondingDelegationKey, addresstypes.MustLengthPrefix(delAddr)...), addresstypes.MustLengthPrefix(valAddr)...) } +// getUBDByValIndexKey creates the index-key for an unbonding delegation, stored by validator-index +// VALUE: none (key rearrangement used) +func getUBDByValIndexKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte { + unbondingDelegationByValIndexKey := []byte{0x33} + return append(append(unbondingDelegationByValIndexKey, addresstypes.MustLengthPrefix(valAddr)...), addresstypes.MustLengthPrefix(delAddr)...) +} + // getUnbondingDelegationTimeKey creates the prefix for all unbonding delegations from a delegator func getUnbondingDelegationTimeKey(timestamp time.Time) []byte { bz := sdk.FormatTimeBytes(timestamp) @@ -275,7 +282,7 @@ func (s *KeeperTestSuite) TestUnbondingDelegationsMigrationToColls() { } bz := stakingtypes.MustMarshalUBD(s.cdc, ubd) s.ctx.KVStore(s.key).Set(getUBDKey(delAddrs[i], valAddrs[i]), bz) - s.ctx.KVStore(s.key).Set(stakingtypes.GetUBDByValIndexKey(delAddrs[i], valAddrs[i]), []byte{}) + s.ctx.KVStore(s.key).Set(getUBDByValIndexKey(delAddrs[i], valAddrs[i]), []byte{}) }, "d03ca412f3f6849b5148a2ca49ac2555f65f90b7fab6a289575ed337f15c0f4b", ) diff --git a/x/staking/migrations/v2/store_test.go b/x/staking/migrations/v2/store_test.go index 8baeb3193695..d40570f79199 100644 --- a/x/staking/migrations/v2/store_test.go +++ b/x/staking/migrations/v2/store_test.go @@ -10,7 +10,7 @@ import ( storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/codec/address" - sdktestuil "github.com/cosmos/cosmos-sdk/testutil" + sdktestutil "github.com/cosmos/cosmos-sdk/testutil" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" sdkaddress "github.com/cosmos/cosmos-sdk/types/address" @@ -23,7 +23,7 @@ import ( func TestStoreMigration(t *testing.T) { stakingKey := storetypes.NewKVStoreKey("staking") tStakingKey := storetypes.NewTransientStoreKey("transient_test") - ctx := sdktestuil.DefaultContext(stakingKey, tStakingKey) + ctx := sdktestutil.DefaultContext(stakingKey, tStakingKey) store := ctx.KVStore(stakingKey) _, pk1, addr1 := testdata.KeyTestPubAddr() @@ -81,7 +81,7 @@ func TestStoreMigration(t *testing.T) { { "UnbondingDelegationByValIndexKey", v1.GetUBDByValIndexKey(addr4, valAddr1), - types.GetUBDByValIndexKey(addr4, valAddr1), + getUBDByValIndexKey(addr4, valAddr1), }, { "RedelegationKey", @@ -141,6 +141,10 @@ func TestStoreMigration(t *testing.T) { } } +func getUBDByValIndexKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte { + return append(append(types.UnbondingDelegationByValIndexKey, sdkaddress.MustLengthPrefix(valAddr)...), sdkaddress.MustLengthPrefix(delAddr)...) +} + func getUnbondingDelegationTimeKey(timestamp time.Time) []byte { bz := sdk.FormatTimeBytes(timestamp) return append(types.UnbondingQueueKey, bz...) diff --git a/x/staking/types/keys.go b/x/staking/types/keys.go index 19c9627decd5..e7a2b79e5753 100644 --- a/x/staking/types/keys.go +++ b/x/staking/types/keys.go @@ -41,10 +41,11 @@ var ( DelegationKey = collections.NewPrefix(49) // key for a delegation UnbondingDelegationKey = collections.NewPrefix(50) // key for an unbonding-delegation - UnbondingDelegationByValIndexKey = []byte{0x33} // prefix for each key for an unbonding-delegation, by validator operator - RedelegationKey = collections.NewPrefix(52) // key for a redelegation - RedelegationByValSrcIndexKey = collections.NewPrefix(53) // prefix for each key for an redelegation, by source validator operator - RedelegationByValDstIndexKey = collections.NewPrefix(54) // prefix for each key for an redelegation, by destination validator operator + UnbondingDelegationByValIndexKey = collections.NewPrefix(51) // prefix for each key for an unbonding-delegation, by validator operator + + RedelegationKey = collections.NewPrefix(52) // key for a redelegation + RedelegationByValSrcIndexKey = collections.NewPrefix(53) // prefix for each key for an redelegation, by source validator operator + RedelegationByValDstIndexKey = collections.NewPrefix(54) // prefix for each key for an redelegation, by destination validator operator UnbondingIDKey = collections.NewPrefix(55) // key for the counter for the incrementing id for UnbondingOperations UnbondingIndexKey = collections.NewPrefix(56) // prefix for an index for looking up unbonding operations by their IDs @@ -196,31 +197,6 @@ func GetUBDKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte { return append(append(UnbondingDelegationKey, address.MustLengthPrefix(delAddr)...), address.MustLengthPrefix(valAddr)...) } -// GetUBDByValIndexKey creates the index-key for an unbonding delegation, stored by validator-index -// VALUE: none (key rearrangement used) -func GetUBDByValIndexKey(delAddr sdk.AccAddress, valAddr sdk.ValAddress) []byte { - return append(GetUBDsByValIndexKey(valAddr), address.MustLengthPrefix(delAddr)...) -} - -// GetUBDKeyFromValIndexKey rearranges the ValIndexKey to get the UBDKey -func GetUBDKeyFromValIndexKey(indexKey []byte) []byte { - kv.AssertKeyAtLeastLength(indexKey, 2) - addrs := indexKey[1:] // remove prefix bytes - - valAddrLen := addrs[0] - kv.AssertKeyAtLeastLength(addrs, 2+int(valAddrLen)) - valAddr := addrs[1 : 1+valAddrLen] - kv.AssertKeyAtLeastLength(addrs, 3+int(valAddrLen)) - delAddr := addrs[valAddrLen+2:] - - return append(append(UnbondingDelegationKey, address.MustLengthPrefix(delAddr)...), address.MustLengthPrefix(valAddr)...) -} - -// GetUBDsByValIndexKey creates the prefix keyspace for the indexes of unbonding delegations for a validator -func GetUBDsByValIndexKey(valAddr sdk.ValAddress) []byte { - return append(UnbondingDelegationByValIndexKey, address.MustLengthPrefix(valAddr)...) -} - // GetREDKey returns a key prefix for indexing a redelegation from a delegator // and source validator to a destination validator. func GetREDKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) []byte {