From 4c7e407fa70ba0d690fd5f7a76b70893ff698f82 Mon Sep 17 00:00:00 2001 From: nagarajdivine Date: Sat, 27 Jun 2026 02:01:05 +0800 Subject: [PATCH] switch to collections in the ibchooks module. --- .../features/2492-collections-ibchooks.md | 1 + app/app.go | 2 +- x/ibchooks/keeper/keeper.go | 74 +++++++++++------ x/ibchooks/keeper/migrations.go | 83 +++++++++++++++++++ x/ibchooks/keeper/params.go | 21 +++-- x/ibchooks/module.go | 6 +- x/ibchooks/types/keys.go | 15 +++- 7 files changed, 166 insertions(+), 36 deletions(-) create mode 100644 .changelog/unreleased/features/2492-collections-ibchooks.md create mode 100644 x/ibchooks/keeper/migrations.go diff --git a/.changelog/unreleased/features/2492-collections-ibchooks.md b/.changelog/unreleased/features/2492-collections-ibchooks.md new file mode 100644 index 0000000000..94b3f21f8e --- /dev/null +++ b/.changelog/unreleased/features/2492-collections-ibchooks.md @@ -0,0 +1 @@ +* Switch to collections in the ibchooks module [#2492](https://github.com/provenance-io/provenance/issues/2492). diff --git a/app/app.go b/app/app.go index 1ed31c4354..1e2cd45738 100644 --- a/app/app.go +++ b/app/app.go @@ -509,7 +509,7 @@ func New( // Configure the hooks keeper hooksKeeper := ibchookskeeper.NewKeeper( appCodec, - keys[ibchookstypes.StoreKey], + runtime.NewKVStoreService(keys[ibchookstypes.StoreKey]), app.IBCKeeper.ChannelKeeper, nil, ) diff --git a/x/ibchooks/keeper/keeper.go b/x/ibchooks/keeper/keeper.go index 33cd0927f0..0ba9aba1a1 100644 --- a/x/ibchooks/keeper/keeper.go +++ b/x/ibchooks/keeper/keeper.go @@ -10,9 +10,10 @@ import ( "github.com/cometbft/cometbft/crypto/tmhash" + "cosmossdk.io/collections" + corestore "cosmossdk.io/core/store" sdkerrors "cosmossdk.io/errors" "cosmossdk.io/log" - storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -26,29 +27,46 @@ import ( type ( Keeper struct { - cdc codec.BinaryCodec - storeKey storetypes.StoreKey - + cdc codec.BinaryCodec + storeService corestore.KVStoreService + schema collections.Schema channelKeeper types.ChannelKeeper ContractKeeper *wasmkeeper.PermissionedKeeper authority string + // Params stores the module's persistent parameters using the legacy key (0x01). + params collections.Item[types.Params] + // PacketCallbacks stores temporary callback state for packets. + // Maps (channel, sequence) to a contract bech32 address (prefix 0x02). + packetCallbacks collections.Map[collections.Pair[string, uint64], string] + // PacketAckActors maps (channel, sequence) to "contract::hash" bytes. + // Used to store temporary acknowledgment state for each packet (prefix 0x03). + packetAckActors collections.Map[collections.Pair[string, uint64], []byte] } ) // NewKeeper returns a new instance of the x/ibchooks keeper func NewKeeper( cdc codec.BinaryCodec, - storeKey storetypes.StoreKey, + storeService corestore.KVStoreService, channelKeeper types.ChannelKeeper, contractKeeper *wasmkeeper.PermissionedKeeper, ) Keeper { + sb := collections.NewSchemaBuilder(storeService) keeper := Keeper{ - cdc: cdc, - storeKey: storeKey, - channelKeeper: channelKeeper, - ContractKeeper: contractKeeper, - authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + cdc: cdc, + storeService: storeService, + channelKeeper: channelKeeper, + ContractKeeper: contractKeeper, + authority: authtypes.NewModuleAddress(govtypes.ModuleName).String(), + params: collections.NewItem(sb, types.ParamsKeyPrefix, "params", codec.CollValue[types.Params](cdc)), + packetCallbacks: collections.NewMap(sb, types.PacketCallbackKeyPrefix, "packet_callbacks", collections.PairKeyCodec(collections.StringKey, collections.Uint64Key), collections.StringValue), + packetAckActors: collections.NewMap(sb, types.PacketAckKeyPrefix, "packet_ack_actors", collections.PairKeyCodec(collections.StringKey, collections.Uint64Key), collections.BytesValue), } + schema, err := sb.Build() + if err != nil { + panic(err) + } + keeper.schema = schema return keeper } @@ -108,39 +126,44 @@ func GeneratePacketAckValue(packet channeltypes.Packet, contract string) ([]byte // StorePacketCallback stores which contract will be listening for the ack or timeout of a packet func (k Keeper) StorePacketCallback(ctx sdk.Context, channel string, packetSequence uint64, contract string) { - store := ctx.KVStore(k.storeKey) - store.Set(GetPacketCallbackKey(channel, packetSequence), []byte(contract)) + if err := k.packetCallbacks.Set(ctx, collections.Join(channel, packetSequence), contract); err != nil { + panic(err) + } } // GetPacketCallback returns the bech32 addr of the contract that is expecting a callback from a packet func (k Keeper) GetPacketCallback(ctx sdk.Context, channel string, packetSequence uint64) string { - store := ctx.KVStore(k.storeKey) - return string(store.Get(GetPacketCallbackKey(channel, packetSequence))) + v, err := k.packetCallbacks.Get(ctx, collections.Join(channel, packetSequence)) + if err != nil { + return "" + } + return v } // DeletePacketCallback deletes the callback from storage once it has been processed func (k Keeper) DeletePacketCallback(ctx sdk.Context, channel string, packetSequence uint64) { - store := ctx.KVStore(k.storeKey) - store.Delete(GetPacketCallbackKey(channel, packetSequence)) + if err := k.packetCallbacks.Remove(ctx, collections.Join(channel, packetSequence)); err != nil { + panic(err) + } } // StorePacketAckActor stores which contract is allowed to send an ack for the packet func (k Keeper) StorePacketAckActor(ctx sdk.Context, packet channeltypes.Packet, contract string) { - store := ctx.KVStore(k.storeKey) - channel := packet.GetSourceChannel() - packetSequence := packet.GetSequence() - val, err := GeneratePacketAckValue(packet, contract) if err != nil { panic(err) } - store.Set(GetPacketAckKey(channel, packetSequence), val) + if err := k.packetAckActors.Set(ctx, collections.Join(packet.GetSourceChannel(), packet.GetSequence()), val); err != nil { + panic(err) + } } // GetPacketAckActor returns the bech32 addr of the contract that is allowed to send an ack for the packet and the packet hash func (k Keeper) GetPacketAckActor(ctx sdk.Context, channel string, packetSequence uint64) (string, string) { - store := ctx.KVStore(k.storeKey) - rawData := store.Get(GetPacketAckKey(channel, packetSequence)) + rawData, err := k.packetAckActors.Get(ctx, collections.Join(channel, packetSequence)) + if err != nil { + return "", "" + } if rawData == nil { return "", "" } @@ -162,8 +185,9 @@ func (k Keeper) GetPacketAckActor(ctx sdk.Context, channel string, packetSequenc // DeletePacketAckActor deletes the ack actor from storage once it has been used func (k Keeper) DeletePacketAckActor(ctx sdk.Context, channel string, packetSequence uint64) { - store := ctx.KVStore(k.storeKey) - store.Delete(GetPacketAckKey(channel, packetSequence)) + if err := k.packetAckActors.Remove(ctx, collections.Join(channel, packetSequence)); err != nil { + panic(err) + } } // DeriveIntermediateSender derives the sender address to be used when calling wasm hooks diff --git a/x/ibchooks/keeper/migrations.go b/x/ibchooks/keeper/migrations.go new file mode 100644 index 0000000000..824fa5fa82 --- /dev/null +++ b/x/ibchooks/keeper/migrations.go @@ -0,0 +1,83 @@ +package keeper + +import ( + "strconv" + "strings" + + "cosmossdk.io/collections" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/provenance-io/provenance/x/ibchooks/types" +) + +// Migrator handles in-place store migrations for the x/ibchooks module. +type Migrator struct { + keeper Keeper +} + +// NewMigrator returns a Migrator for the x/ibchooks module. +func NewMigrator(k Keeper) Migrator { + return Migrator{keeper: k} +} + +// Migrate1to2 re-keys legacy raw-string packet entries into the new collections: +// - "channel::seq" -> PacketCallbacks[(channel, seq)] (prefix 0x02) +// - "channel::seq::ack" -> PacketAckActors[(channel, seq)] (prefix 0x03) +// +// Params (0x01) is already byte-identical under the new collections.Item and needs no migration. +// Legacy entries are ephemeral, so there are usually few (only in-flight packets) at upgrade time. +func (m Migrator) Migrate1to2(ctx sdk.Context) error { + store := m.keeper.storeService.OpenKVStore(ctx) + it, err := store.Iterator(nil, nil) + if err != nil { + return err + } + + type entry struct{ key, val []byte } + var legacy []entry + for ; it.Valid(); it.Next() { + key := it.Key() + if len(key) == 1 && key[0] == types.ParamsKeyBz[0] { + continue + } + if len(key) > 0 && (key[0] == types.PacketCallbackKeyBz[0] || key[0] == types.PacketAckKeyBz[0]) { + continue + } + legacy = append(legacy, + entry{key: append([]byte(nil), key...), val: append([]byte(nil), it.Value()...)}) + } + if cerr := it.Close(); cerr != nil { + return cerr + } + + for _, e := range legacy { + parts := strings.Split(string(e.key), "::") + switch { + case len(parts) == 2: + seq, perr := strconv.ParseUint(parts[1], 10, 64) + if perr != nil { + continue + } + if err := m.keeper.packetCallbacks.Set(ctx, collections.Join(parts[0], seq), string(e.val)); err != nil { + return err + } + case len(parts) == 3 && parts[2] == "ack": + seq, perr := strconv.ParseUint(parts[1], 10, 64) + if perr != nil { + continue + } + if err := m.keeper.packetAckActors.Set(ctx, collections.Join(parts[0], seq), e.val); err != nil { + return err + } + default: + continue + } + if err := store.Delete(e.key); err != nil { + return err + } + } + + ctx.Logger().Info("ibchooks 1->2 migration complete: re-keyed legacy packet entries", "count", len(legacy)) + return nil +} diff --git a/x/ibchooks/keeper/params.go b/x/ibchooks/keeper/params.go index f8ffe477df..5109341839 100644 --- a/x/ibchooks/keeper/params.go +++ b/x/ibchooks/keeper/params.go @@ -1,6 +1,10 @@ package keeper import ( + "errors" + + "cosmossdk.io/collections" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/provenance-io/provenance/x/ibchooks/types" @@ -8,18 +12,19 @@ import ( // GetParams returns the total set of the module's parameters. func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { - store := ctx.KVStore(k.storeKey) - bz := store.Get(types.IbcHooksParamStoreKey) - if bz == nil { - return types.DefaultParams() + params, err := k.params.Get(ctx) + if err != nil { + if errors.Is(err, collections.ErrNotFound) { + return types.DefaultParams() + } + panic(err) } - k.cdc.MustUnmarshal(bz, ¶ms) return params } // SetParams sets the module's parameters with the provided parameters. func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { - store := ctx.KVStore(k.storeKey) - bz := k.cdc.MustMarshal(¶ms) - store.Set(types.IbcHooksParamStoreKey, bz) + if err := k.params.Set(ctx, params); err != nil { + panic(err) + } } diff --git a/x/ibchooks/module.go b/x/ibchooks/module.go index 3e508e0cc4..d68be3393a 100644 --- a/x/ibchooks/module.go +++ b/x/ibchooks/module.go @@ -116,6 +116,10 @@ func (AppModule) Name() string { func (am AppModule) RegisterServices(cfg module.Configurator) { types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) types.RegisterQueryServer(cfg.QueryServer(), am.keeper) + migrator := keeper.NewMigrator(am.keeper) + if err := cfg.RegisterMigration(types.ModuleName, 1, migrator.Migrate1to2); err != nil { + panic(fmt.Sprintf("failed to register ibchooks migration 1->2: %v", err)) + } } // InitGenesis performs genesis initialization for the ibchooks module. It returns @@ -158,4 +162,4 @@ func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.Weig } // ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 1 } +func (AppModule) ConsensusVersion() uint64 { return 2 } diff --git a/x/ibchooks/types/keys.go b/x/ibchooks/types/keys.go index 76a9b764fb..64ee86e495 100644 --- a/x/ibchooks/types/keys.go +++ b/x/ibchooks/types/keys.go @@ -1,5 +1,7 @@ package types +import "cosmossdk.io/collections" + const ( ModuleName = "ibchooks" StoreKey = "hooks-for-ibc" // not using the module name because of collisions with key "ibc" @@ -16,6 +18,17 @@ const ( ) var ( + + // Params keeps 0x01 (byte-identical to the legacy layout). + ParamsKeyBz = []byte{0x01} + PacketCallbackKeyBz = []byte{0x02} + PacketAckKeyBz = []byte{0x03} + // IbcHooksParamStoreKey key for ibchooks module's params - IbcHooksParamStoreKey = []byte{0x01} + IbcHooksParamStoreKey = ParamsKeyBz + + // Collection prefixes. + ParamsKeyPrefix = collections.NewPrefix(ParamsKeyBz) + PacketCallbackKeyPrefix = collections.NewPrefix(PacketCallbackKeyBz) + PacketAckKeyPrefix = collections.NewPrefix(PacketAckKeyBz) )