Skip to content

Migrate trigger module from KV store to collections.#2546

Open
nagarajdivine wants to merge 14 commits into
mainfrom
feat/trigger-module-collections
Open

Migrate trigger module from KV store to collections.#2546
nagarajdivine wants to merge 14 commits into
mainfrom
feat/trigger-module-collections

Conversation

@nagarajdivine

@nagarajdivine nagarajdivine commented Nov 21, 2025

Copy link
Copy Markdown
Contributor

Description


Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.

  • Targeted PR against correct branch (see CONTRIBUTING.md).
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Wrote unit and integration tests
  • Updated relevant documentation (docs/) or specification (x/<module>/spec/).
  • Added relevant godoc comments.
  • Added relevant changelog entries under .changelog/unreleased (see Adding Changes).
  • Re-reviewed Files changed in the Github PR explorer.
  • Review Codecov Report in the comment section below once CI passes.

Summary by CodeRabbit

  • Changelog

    • Migrated trigger module to collection-backed state storage.
  • New Features

    • Collection-backed APIs for triggers, listeners, and queue: existence checks, counts, bulk removals, hashed event keys, enqueue/dequeue/peek, iteration, and paginated trigger queries.
    • Stable trigger ID sequencing and strict 32-byte event-hash key support.
  • Bug Fixes

    • Clearer not-found mapping, explicit error propagation, improved logging and resilience.
  • Chores

    • Storage migration, init/export validation, and migration tooling added.
  • Tests

    • Expanded coverage for collections, queue, listeners, migrations and backward compatibility.

@nagarajdivine nagarajdivine requested a review from a team as a code owner November 21, 2025 15:25
@coderabbitai

coderabbitai Bot commented Nov 21, 2025

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces raw KV store usage in x/trigger with cosmossdk.io/collections: Keeper now accepts a runtime KVStoreService, exposes typed collections (triggers, event listeners, queue, metadata), and many storage APIs now return errors; keys/codecs, genesis, migrations, msg/query servers, and tests adapted accordingly.

Changes

Cohort / File(s) Summary
Changelog
changelog/unreleased/features/2499-trigger-module-collections.md
Added entry documenting migration of trigger module to collections.
App wiring
app/app.go
Construct Keeper with runtime.NewKVStoreService(...) instead of raw StoreKey.
Keeper core & schema
x/trigger/keeper/keeper.go
New NewKeeper signature uses store.KVStoreService; added Schema and typed collections (TriggersMap, EventListeners, Queue, NextTriggerID, QueueStartIndex, QueueLength); schema builder wiring and collection initialization.
Trigger storage & helpers
x/trigger/keeper/trigger.go, x/trigger/keeper/trigger_registry.go
Triggers moved to TriggersMap; Set/Get/Iterate/ID logic now use collections and return errors; added HasTrigger; registry logs/propagates collection errors.
Event listeners
x/trigger/keeper/event_listener.go, x/trigger/keeper/event_listener_test.go
Event keys normalized via hashed event name; listeners stored in a collections.KeySet keyed by (hash, order, triggerID); APIs now return errors and include Has/Count/RemoveForTriggerID/iteration changes; tests updated/expanded.
Queue management
x/trigger/keeper/queue.go, x/trigger/keeper/queue_test.go
Queue backed by collections.Map and metadata collections.Item; Enqueue/Dequeue/Peek/Iterate/length APIs now return errors and use collection semantics; new queue tests added.
Msg & Query servers
x/trigger/keeper/msg_server.go, x/trigger/keeper/query_server.go
NewTriggerWithID now returns (Trigger, error); Create/Destroy/Query handlers propagate collection errors and map collections.ErrNotFound to module errors; query pagination uses collections.
Genesis & migrations
x/trigger/keeper/genesis.go, x/trigger/keeper/migrations_v2.go
Genesis uses collection-backed getters/setters with error checks; added migration Migrate1To2 and DeleteGasLimits to remove deprecated gas-limit keys.
Keys & codecs
x/trigger/types/keys.go, x/trigger/types/codec.go, x/trigger/types/keys_test.go
Raw prefixes replaced by collections.NewPrefix; added FixedBytes32KeyCodec and EventListenerKeyCodec for [32-byte hash][uint64][uint64] keys; key APIs accept hashed event bytes and return typed key objects; tests updated.
Decoder & simulation
x/trigger/simulation/decoder.go, x/trigger/simulation/genesis.go
Decoder switched to exact-key checks and safer uint64 decoding; minor simulation comment change.
Tests & compatibility
x/trigger/keeper/*_test.go, x/trigger/keeper/keeper_test.go, x/trigger/keeper/trigger_test.go, x/trigger/keeper/event_detector_test.go
Tests updated to handle new error returns; added backward-compatibility test to read legacy-format triggers; many collection-focused tests added for triggers, listeners, and queue.

Sequence Diagram(s)

sequenceDiagram
    participant Msg as MsgServer (CreateTrigger)
    participant Keeper as TriggerKeeper (collections)
    participant NextID as NextTriggerID (Item)
    participant Triggers as TriggersMap (Map)
    participant Events as EventListeners (KeySet)

    Note over Msg,Keeper: CreateTrigger — collection-backed flow
    Msg->>Keeper: NewTriggerWithID(ctx, owner, event, actions)
    Keeper->>NextID: Get(ctx)
    alt NotFound
        NextID-->>Keeper: NotFound / treat as 0
    else Found
        NextID-->>Keeper: currentID
    end
    Keeper->>NextID: Set(ctx, currentID+1)
    Keeper->>Triggers: Set(ctx, id, trigger)
    Triggers-->>Keeper: ok / error
    Keeper->>Events: Set(ctx, (eventHash, order, id))
    Events-->>Keeper: ok / error
    Keeper-->>Msg: (trigger, error)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • SpicyLemon
  • dcshock
  • kwtalley

Poem

🐇
I hopped from raw bytes into a tidy tree,
hashed every name and set the queue free.
IDs now climb in neat numeric rows,
listeners snug where the hashed wind blows.
A carrot for tests — collections hum, and I go! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objective of the changeset: migrating the trigger module from KV store to collections.
Linked Issues check ✅ Passed The PR successfully migrates the x/trigger module to use collections throughout (keeper.go, trigger.go, queue.go, event_listener.go, keys.go, types/codec.go) and includes a backward compatibility test, addressing issue #2499 requirements.
Out of Scope Changes check ✅ Passed All changes are directly related to the collections migration. The additions include new codecs for event listeners, migration logic for gas limits (a cleanup action), updated error handling, and comprehensive test coverage - all within scope of modernizing state storage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/trigger-module-collections

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (11)
x/trigger/keeper/query_server.go (1)

46-54: CollectionPaginate usage and ID binding look good

Using query.CollectionPaginate over k.TriggersMap with a transformer that sets trigger.Id = key preserves the previous API contract where the ID field matches the storage key. The internal error is correctly wrapped as an Internal gRPC status, and the response assembly is straightforward. If you want to simplify later, you could drop the explicit type parameters and let the compiler infer them, but it's optional.

Also applies to: 56-61, 64-64

x/trigger/keeper/event_listener_test.go (1)

173-237: New collections-based listener tests are solid; minor consistency nit

The new collections tests cover set/get, iteration, and single-listener removal nicely and validate the ErrEventNotFound path after removal. One minor consistency improvement: at Line 234 you ignore the error from trigger1.GetTriggerEventI(), whereas earlier in the same test you assert on it. For clarity and future-proofing, consider:

- eventI, _ := trigger1.GetTriggerEventI()
+ eventI, err := trigger1.GetTriggerEventI()
+ s.Require().NoError(err, "failed to get TriggerEventI for removed trigger1")

Otherwise this block looks good.

x/trigger/keeper/trigger_test.go (1)

248-282: Trigger ID sequence test is appropriate, with minor enhancement opportunity

TestTriggerIDSequence correctly validates initial NextTriggerID and the increments after two NewTriggerWithID calls. For robustness, you might later add a check that the created triggers are also persisted as expected, but for verifying the sequence logic this test is already useful.

x/trigger/types/codec.go (1)

4-9: FixedBytes32KeyCodec correctly enforces and preserves 32‑byte keys

The codec’s binary behavior (strict 32‑byte length check, fixed Size, copy in/out) looks correct for preserving the legacy [32-byte-hash] prefix. Stringify and KeyType are straightforward, and using the same logic for terminal and non‑terminal usage is appropriate for composite keys.

The only potential gap is EncodeJSON/DecodeJSON returning “not implemented” errors. If anything in the future tries to JSON‑encode event listener keys (e.g., debug tooling, introspection), this will surface as a runtime error. Optional improvement:

  • Implement JSON as hex string encoding/decoding, or
  • Add a brief comment noting these are intentionally unsupported and not used anywhere.

Also applies to: 47-111

x/trigger/types/keys.go (1)

157-165: Duplicate event-name hashing logic across types and keeper

GetEventNameBytes normalizes and hashes event names, and keeper/event_listener.go adds a separate getEventNameHash with effectively identical logic. This duplication is brittle and risks drift if one implementation changes.

Consider reusing GetEventNameBytes from the keeper instead of re-implementing the hash there (or moving the helper to a shared location in types and calling it from both places).

x/trigger/keeper/event_listener.go (2)

113-121: RemoveAllEventListenersForTrigger only removes one listener despite the method name

RemoveAllEventListenersForTrigger currently loads the trigger and calls RemoveEventListener once:

trigger, err := k.GetTrigger(ctx, triggerID)
...
_, err = k.RemoveEventListener(ctx, trigger)

If a trigger can have more than one associated event-listener entry (now or in future—e.g. multiple orders per event), this will only remove a single entry, contradicting the “All” in the method name.

If the invariant really is “one listener per trigger”, consider either:

  • Renaming this to RemoveEventListenerForTrigger, or
  • Adding a brief comment documenting that only a single listener can exist per trigger.

If multiple listeners per trigger are possible, this should iterate and remove all matching entries instead.


133-141: Duplicate event-name hashing with types.GetEventNameBytes

getEventNameHash normalizes and hashes the event name using strings.ToLower(strings.TrimSpace()) and SHA-256. This is effectively the same as types.GetEventNameBytes in x/trigger/types/keys.go, but with a different panic message.

To avoid divergence and keep hashing semantics centralized, prefer reusing types.GetEventNameBytes here instead of maintaining a second copy of the logic.

For example:

-func getEventNameHash(name string) []byte {
-	eventName := strings.ToLower(strings.TrimSpace(name))
-	if len(eventName) == 0 {
-		panic("invalid event name: cannot be empty")
-	}
-	hash := sha256.Sum256([]byte(eventName))
-	return hash[:]
-}
+func getEventNameHash(name string) []byte {
+	return types.GetEventNameBytes(name)
+}
x/trigger/keeper/queue.go (2)

13-17: QueueTrigger silently drops Enqueue errors

Enqueue has been upgraded to return an error, but QueueTrigger still ignores it:

func (k Keeper) QueueTrigger(ctx sdk.Context, trigger types.Trigger) {
    item := types.NewQueuedTrigger(...)
    k.Enqueue(ctx, item) // error ignored
}

Given that the collections.Map.Set/Item.Set operations can now fail, silently discarding the error means a failure to enqueue a trigger is completely invisible to callers.

Consider either:

  • Propagating the error up the call chain by changing QueueTrigger’s signature, or
  • At least logging and/or panicking on a non-nil error to avoid silent data loss.

Also applies to: 44-71


109-119: Prefer reusing GetQueueLength semantics in QueueIsEmpty

QueueIsEmpty manually interprets collections.ErrNotFound as “length == 0” while treating other errors as “not empty”:

length, err := k.QueueLength.Get(ctx)
if err != nil {
    if errors.Is(err, collections.ErrNotFound) {
        return true
    }
    return false
}
return length == 0

You’ve already encapsulated the “not found => 0 length” behavior in GetQueueLength. Using that here would keep semantics in one place and avoid divergence:

func (k Keeper) QueueIsEmpty(ctx sdk.Context) bool {
-    length, err := k.QueueLength.Get(ctx)
+    length, err := k.GetQueueLength(ctx)
     if err != nil {
-        if errors.Is(err, collections.ErrNotFound) {
-            return true
-        }
-        return false
+        // optionally: log or panic, rather than silently reporting "not empty"
+        return false
     }
     return length == 0
}

Also applies to: 224-233

x/trigger/keeper/trigger.go (2)

19-32: RemoveTrigger hides underlying storage errors

RemoveTrigger now checks existence and then removes, but converts any Has/Remove errors into a bare false:

exists, err := k.TriggersMap.Has(ctx, id)
if err != nil {
    return false
}
if exists {
    if err := k.TriggersMap.Remove(ctx, id); err != nil {
        return false
    }
    return true
}
return false

This makes it impossible for callers to distinguish “not found” from “I/O or codec error”, which may complicate debugging real failures.

Consider either:

  • Changing the signature to (bool, error) and returning the underlying error, or
  • At least logging the error before returning false.

78-87: HasTrigger should prefer TriggersMap.Has over Get

HasTrigger currently does a full Get:

_, err := k.TriggersMap.Get(ctx, id)
if errors.Is(err, collections.ErrNotFound) {
    return false, nil
}
if err != nil {
    return false, err
}
return true, nil

For a simple existence check this is unnecessarily expensive; Map.Has is designed for this and avoids decoding the value.

A lighter-weight implementation:

func (k Keeper) HasTrigger(ctx sdk.Context, id uint64) (bool, error) {
-   _, err := k.TriggersMap.Get(ctx, id)
-   if errors.Is(err, collections.ErrNotFound) {
-       return false, nil
-   }
-   if err != nil {
-       return false, err
-   }
-   return true, nil
+   has, err := k.TriggersMap.Has(ctx, id)
+   if err != nil {
+       return false, err
+   }
+   return has, nil
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 323c7a2 and 6de171a.

📒 Files selected for processing (18)
  • .changelog/unreleased/features/2499-trigger-module-collections.md (1 hunks)
  • app/app.go (1 hunks)
  • x/trigger/keeper/event_detector_test.go (1 hunks)
  • x/trigger/keeper/event_listener.go (2 hunks)
  • x/trigger/keeper/event_listener_test.go (1 hunks)
  • x/trigger/keeper/genesis.go (1 hunks)
  • x/trigger/keeper/keeper.go (2 hunks)
  • x/trigger/keeper/keeper_test.go (1 hunks)
  • x/trigger/keeper/migrations_v2.go (1 hunks)
  • x/trigger/keeper/msg_server.go (3 hunks)
  • x/trigger/keeper/query_server.go (3 hunks)
  • x/trigger/keeper/queue.go (2 hunks)
  • x/trigger/keeper/queue_test.go (1 hunks)
  • x/trigger/keeper/trigger.go (3 hunks)
  • x/trigger/keeper/trigger_test.go (2 hunks)
  • x/trigger/simulation/genesis.go (1 hunks)
  • x/trigger/types/codec.go (2 hunks)
  • x/trigger/types/keys.go (2 hunks)
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/keys.go:3-12
Timestamp: 2025-08-15T20:59:51.949Z
Learning: In Cosmos SDK, key prefixes only need to be unique within a given module's store, not across the entire application. Each module operates within its own isolated key-value store namespace, so the same byte prefixes can be safely reused across different modules without causing collisions.
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: app/upgrades.go:52-53
Timestamp: 2025-09-05T19:28:28.271Z
Learning: The asset module in the Provenance blockchain doesn't maintain its own state storage and therefore doesn't require a StoreKey in upgrade configurations. It delegates all storage operations to the NFT module, unlike other modules like ledger and registry which have their own state stores.
📚 Learning: 2024-10-08T18:12:51.935Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/migrations_v4.go:57-80
Timestamp: 2024-10-08T18:12:51.935Z
Learning: The `V3WriteNewScope` method in `x/metadata/keeper/migrations_v4.go` must exist until after the migration has been executed and will be deleted as part of the `viridian` cleanup.

Applied to files:

  • x/trigger/keeper/migrations_v2.go
📚 Learning: 2025-08-15T20:59:51.949Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/keys.go:3-12
Timestamp: 2025-08-15T20:59:51.949Z
Learning: In Cosmos SDK, key prefixes only need to be unique within a given module's store, not across the entire application. Each module operates within its own isolated key-value store namespace, so the same byte prefixes can be safely reused across different modules without causing collisions.

Applied to files:

  • x/trigger/keeper/query_server.go
📚 Learning: 2025-08-15T22:52:24.461Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/keeper.go:74-173
Timestamp: 2025-08-15T22:52:24.461Z
Learning: In the ledger keeper NewKeeper function in x/ledger/keeper/keeper.go, SpicyLemon prefers to allow bankKeeper and registryKeeper dependencies to be nil rather than adding validation that would panic on nil values. The design intentionally supports nil keepers for flexibility.

Applied to files:

  • app/app.go
  • x/trigger/keeper/keeper.go
📚 Learning: 2025-08-15T20:59:09.909Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: The BankKeeper interface methods in Cosmos SDK v0.50.x and Provenance use `context.Context` as the context parameter type, not `sdk.Context`. This applies to methods like SendCoins, HasBalance, GetBalance, SpendableCoin, HasSupply, etc. The expected keeper interfaces should consistently use `context.Context` to match the actual Cosmos SDK bank keeper signatures.

Applied to files:

  • app/app.go
  • x/trigger/keeper/queue.go
  • x/trigger/keeper/keeper.go
📚 Learning: 2025-08-15T20:34:01.110Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/codec.go:9-10
Timestamp: 2025-08-15T20:34:01.110Z
Learning: In Go Cosmos SDK projects, sdk.Msg interface embeds proto.Message interface, making it valid to copy elements from []sdk.Msg to []proto.Message even though the slice types are different. The pattern of declaring AllRequestMsgs as []sdk.Msg for type assertions while copying to []proto.Message for RegisterImplementations is a common and correct pattern.

Applied to files:

  • x/trigger/types/codec.go
📚 Learning: 2025-08-15T20:34:01.110Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/codec.go:9-10
Timestamp: 2025-08-15T20:34:01.110Z
Learning: The pattern of declaring AllRequestMsgs as []sdk.Msg and then copying to []proto.Message for RegisterImplementations is a standard, correct pattern used throughout the Provenance codebase. This works because sdk.Msg interface embeds proto.Message interface, making the copy operation valid. This pattern appears consistently across 15+ modules and compiles without errors.

Applied to files:

  • x/trigger/types/codec.go
📚 Learning: 2024-10-02T03:37:18.486Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/msg_server_test.go:1225-1228
Timestamp: 2024-10-02T03:37:18.486Z
Learning: When test cases are run using `s.T().Run` instead of `s.Run`, use `require` with `t` instead of `s.Require()`, to ensure that assertions apply to the correct test instance.

Applied to files:

  • x/trigger/keeper/trigger_test.go
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: nullpointer0x00
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: A GitHub issue was created to track the migration of parameters for the metadata, marker, name, and msgFees modules as indicated by the TODO comments in the codebase. The issue is linked to PR #1932 and can be found at https://github.com/provenance-io/provenance/issues/1935.

Applied to files:

  • .changelog/unreleased/features/2499-trigger-module-collections.md
📚 Learning: 2024-10-02T01:43:01.360Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/marker/types/send_restrictions.go:11-11
Timestamp: 2024-10-02T01:43:01.360Z
Learning: In `x/marker/types/send_restrictions.go`, the `transferAgentKey` is only used for storing data in ephemeral contexts, so changing its value does not impact backward compatibility.

Applied to files:

  • x/trigger/types/keys.go
📚 Learning: 2025-08-15T20:19:31.726Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/simulation/decoder.go:8-14
Timestamp: 2025-08-15T20:19:31.726Z
Learning: The asset module in Provenance doesn't use collections-based storage like ledger/registry modules. Instead, it delegates storage to the NFT module, so a collections-based store decoder isn't applicable here. The module should use its own types.StoreKey rather than exchange.StoreKey in RegisterStoreDecoder.

Applied to files:

  • x/trigger/keeper/keeper.go
🧬 Code graph analysis (11)
x/trigger/keeper/queue_test.go (3)
x/trigger/keeper/keeper_test.go (1)
  • KeeperTestSuite (28-41)
x/trigger/types/trigger.pb.go (6)
  • BlockHeightEvent (170-173)
  • BlockHeightEvent (177-177)
  • BlockHeightEvent (178-180)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/tx.pb.go (3)
  • MsgDestroyTriggerRequest (145-150)
  • MsgDestroyTriggerRequest (154-154)
  • MsgDestroyTriggerRequest (155-157)
x/trigger/keeper/keeper_test.go (1)
x/trigger/types/keys.go (2)
  • ModuleName (14-14)
  • GetTriggerKey (95-102)
x/trigger/keeper/query_server.go (3)
x/trigger/types/errors.go (1)
  • ErrTriggerNotFound (8-8)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/query.pb.go (3)
  • QueryTriggersResponse (172-177)
  • QueryTriggersResponse (181-181)
  • QueryTriggersResponse (182-184)
app/app.go (1)
x/trigger/keeper/keeper.go (1)
  • NewKeeper (30-58)
x/trigger/keeper/trigger_test.go (3)
x/trigger/keeper/keeper_test.go (1)
  • KeeperTestSuite (28-41)
x/trigger/types/trigger.pb.go (3)
  • BlockHeightEvent (170-173)
  • BlockHeightEvent (177-177)
  • BlockHeightEvent (178-180)
x/trigger/types/tx.pb.go (3)
  • MsgDestroyTriggerRequest (145-150)
  • MsgDestroyTriggerRequest (154-154)
  • MsgDestroyTriggerRequest (155-157)
x/trigger/keeper/queue.go (3)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • QueuedTrigger (106-113)
  • QueuedTrigger (117-117)
  • QueuedTrigger (118-120)
x/trigger/types/errors.go (1)
  • ErrQueueIndexNotFound (10-10)
x/trigger/keeper/event_listener_test.go (5)
x/trigger/keeper/keeper_test.go (1)
  • KeeperTestSuite (28-41)
x/trigger/types/trigger.pb.go (9)
  • BlockHeightEvent (170-173)
  • BlockHeightEvent (177-177)
  • BlockHeightEvent (178-180)
  • BlockTimeEvent (216-219)
  • BlockTimeEvent (223-223)
  • BlockTimeEvent (224-226)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/tx.pb.go (3)
  • MsgDestroyTriggerRequest (145-150)
  • MsgDestroyTriggerRequest (154-154)
  • MsgDestroyTriggerRequest (155-157)
x/trigger/types/trigger.go (1)
  • NewTrigger (149-156)
x/trigger/types/errors.go (1)
  • ErrEventNotFound (9-9)
x/trigger/keeper/event_listener.go (4)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
x/trigger/types/errors.go (2)
  • ErrEventNotFound (9-9)
  • ErrTriggerNotFound (8-8)
x/trigger/keeper/keeper.go (3)
x/trigger/types/trigger.pb.go (6)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
  • QueuedTrigger (106-113)
  • QueuedTrigger (117-117)
  • QueuedTrigger (118-120)
x/trigger/types/keys.go (6)
  • TriggerKeyPrefix (59-59)
  • EventListenerKeyPrefix (61-61)
  • QueueKeyPrefix (63-63)
  • NextTriggerIDKey (65-65)
  • QueueStartIndexKey (67-67)
  • QueueLengthKey (69-69)
x/trigger/types/codec.go (1)
  • EventListenerKeyCodec (115-121)
x/trigger/keeper/trigger.go (4)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
x/trigger/types/errors.go (1)
  • ErrTriggerNotFound (8-8)
x/trigger/keeper/msg_server.go (1)
x/trigger/types/errors.go (1)
  • ErrTriggerNotFound (8-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (24)
  • GitHub Check: go-test-multi-db (simple, goleveldb, ubuntu-latest)
  • GitHub Check: go-test-single-db (nondeterminism, ubuntu-latest)
  • GitHub Check: runsim (after-import, goleveldb, ubuntu-latest)
  • GitHub Check: runsim (multi-seed-short, goleveldb, ubuntu-latest)
  • GitHub Check: go-test-single-db (benchmark-invariants, ubuntu-latest)
  • GitHub Check: runsim (import-export, goleveldb, ubuntu-latest)
  • GitHub Check: go-test-multi-db (benchmark, goleveldb, ubuntu-latest)
  • GitHub Check: test-race (04)
  • GitHub Check: test-race (02)
  • GitHub Check: test-race (03)
  • GitHub Check: tests (04)
  • GitHub Check: test-race (00)
  • GitHub Check: tests (01)
  • GitHub Check: test-race (01)
  • GitHub Check: tests (02)
  • GitHub Check: tests (03)
  • GitHub Check: tests (00)
  • GitHub Check: Build OSX
  • GitHub Check: Build Linux
  • GitHub Check: Analyze (go)
  • GitHub Check: golangci-lint
  • GitHub Check: ghcr-publish
  • GitHub Check: docker
  • GitHub Check: heighliner-docker
🔇 Additional comments (17)
x/trigger/simulation/genesis.go (1)

179-184: Good cleanup: nolint removal with explicit safety check.

The removal of the nolint directive is appropriate here. The bounds check on line 179 ensures triggerID ≤ math.MaxInt before the conversion, making the int(triggerID) cast on line 183 explicitly safe. Replacing a lint suppression with proper bounds validation is a solid practice.

.changelog/unreleased/features/2499-trigger-module-collections.md (1)

1-1: LGTM!

The changelog entry is clear and properly references the related issue.

x/trigger/keeper/genesis.go (1)

11-19: LGTM!

The error handling for getTriggerID and getQueueStartIndex is appropriate for genesis export operations. Panicking on error is consistent with the existing error handling pattern in this function.

x/trigger/keeper/migrations_v2.go (1)

28-36: LGTM!

The migration to StoreService.OpenKVStore and the error handling for iterator creation are well-implemented. Returning early on iterator creation failure is appropriate for a migration operation.

x/trigger/keeper/keeper_test.go (1)

103-119: LGTM!

This backward compatibility test is essential for validating that the collections-based storage can read data stored in the old KV format. The test approach is sound: storing with the old key format and reading with the new collections-based method ensures a smooth migration path.

x/trigger/keeper/msg_server.go (2)

39-42: LGTM!

The error handling for NewTriggerWithID is properly implemented. Capturing and propagating the error ensures that trigger creation failures are correctly surfaced to the caller.


61-63: LGTM!

The error mapping from collections.ErrNotFound to types.ErrTriggerNotFound is good practice. This provides a proper abstraction layer, preventing internal collection errors from leaking into the public API.

x/trigger/keeper/queue_test.go (2)

289-308: LGTM!

The test properly validates basic queue operations with the collections-based implementation, including enqueue, peek, and dequeue operations.


310-336: LGTM!

The test effectively validates FIFO ordering and queue length tracking across multiple enqueue/dequeue operations.

app/app.go (1)

645-645: LGTM!

The TriggerKeeper initialization correctly uses runtime.NewKVStoreService to wrap the store key, aligning with the collections-based storage migration. This pattern is consistent with other keeper initializations in the codebase.

x/trigger/keeper/query_server.go (1)

5-5: Error mapping for missing triggers looks correct

Mapping collections.ErrNotFound to types.ErrTriggerNotFound keeps the public query semantics stable after the collections migration. Returning the module error from a gRPC query is consistent with other Cosmos modules and will be translated appropriately upstream. No changes needed.

Also applies to: 10-10, 29-32

x/trigger/keeper/event_listener_test.go (1)

239-259: End-to-end RemoveAllEventListenersForTrigger behavior verified

This test cleanly exercises the “remove all listeners for a trigger” path and confirms that subsequent GetEventListener calls return ErrEventNotFound. It’s a good complement to the single-listener removal test and validates the collections-backed key scheme.

x/trigger/types/codec.go (1)

113-121: EventListenerKeyCodec matches the documented legacy key layout

Composing FixedBytes32KeyCodec with collections.Uint64Key twice via collections.TripleKeyCodec aligns with the documented legacy format [32-byte-hash][8-byte-order][8-byte-triggerID]. This should allow collections to read and write keys compatible with the pre‑collections trigger event listener store while still giving you ordered iteration by (hash, order, triggerID). Looks good.

x/trigger/keeper/event_listener.go (1)

16-51: Collections-based EventListeners usage looks correct

The migration of event listeners to a collections.KeySet[Triple[[]byte,uint64,uint64]] looks sound:

  • Keys are consistently composed via Join3(getEventNameHash(...), order, triggerID).
  • RemoveEventListener properly checks existence with Has before removing and surfaces errors.
  • GetEventListener maps ErrNotFound to types.ErrEventNotFound and then fetches the trigger from the main trigger map, which keeps the listener store as an index only.
  • IterateEventListeners uses NewPrefixedTripleRange with the event-name hash, skips stale entries where the trigger is missing, and respects the handler’s stop flag.

I don’t see functional issues here; this is a good fit for collections.

Also applies to: 53-104

x/trigger/keeper/queue.go (1)

121-143: Iterator usage over Queue looks good

The collections-based iteration in GetAllQueueItems and iterateQueue is straightforward and error-aware:

  • Queue.Iterate(ctx, nil) to cover the full range.
  • Proper defer iter.Close().
  • Decode-time errors from Key()/Value() are surfaced immediately.
  • Handlers control early termination via the stop flag.

This aligns well with collections’ iterator patterns.

Also applies to: 180-204, 206-238

x/trigger/keeper/trigger.go (2)

14-44: Core collections migration for triggers looks correct

The main trigger storage changes are consistent and error-aware:

  • SetTrigger delegates to TriggersMap.Set and returns the error.
  • GetTrigger uses TriggersMap.Get, mapping collections.ErrNotFound to types.ErrTriggerNotFound and propagating other errors.
  • IterateTriggers uses TriggersMap.Iterate(ctx, nil) and iterator.KeyValue() to feed typed triggers to the handler.
  • GetAllTriggers simply wraps IterateTriggers to build a slice.

This is a clean migration off raw KV access and preserves module-specific error types.

Also applies to: 46-76


89-117: Trigger ID handling is internally consistent but should be double-checked against existing state

The new NextTriggerID logic treats the stored value as “current ID”:

  • NewTriggerWithID:
    • Reads currentID from NextTriggerID, defaulting to 0 on not found.
    • Sets nextID := currentID + 1.
    • Writes NextTriggerID = nextID and returns a trigger with Id = nextID.
  • GetNextTriggerID:
    • Reads currentID and returns currentID + 1 (or 1 if missing).
  • setTriggerID writes triggerID - 1 to NextTriggerID, consistent with the above (“store current so next is triggerID”).
  • getTriggerID returns the stored current ID (or 0 if none).

Within this file, those semantics line up and should yield IDs 1, 2, 3, … without gaps under normal use.

Given the PR’s requirement of “no state migration”, it’s worth verifying that the old implementation also treated the stored value as “current ID” (as opposed to “next ID to use”). If legacy state persisted “next ID”, you might need a one-time adjustment or a compatibility shim when reading NextTriggerID for the first time.

Also applies to: 119-146

Comment thread x/trigger/keeper/event_detector_test.go Outdated
Comment thread x/trigger/keeper/event_listener_test.go Outdated
Comment thread x/trigger/keeper/keeper.go
Comment thread x/trigger/keeper/queue_test.go Outdated
Comment thread x/trigger/keeper/queue_test.go
Comment thread x/trigger/keeper/queue.go
Comment thread x/trigger/keeper/trigger_test.go
Comment thread x/trigger/keeper/trigger_test.go
Comment thread x/trigger/types/keys.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
x/trigger/keeper/trigger_test.go (1)

113-117: Don’t discard NewTriggerWithID error in test

NewTriggerWithID now returns (Trigger, error), but this test ignores err:

trigger, _ := s.app.TriggerKeeper.NewTriggerWithID(s.ctx, tc.expected.Owner, tc.expected.Event, tc.expected.Actions)

If an internal error occurs, the test will still compare trigger and may pass or fail for the wrong reason. Capture and assert the error before the equality:

-			trigger, _ := s.app.TriggerKeeper.NewTriggerWithID(s.ctx, tc.expected.Owner, tc.expected.Event, tc.expected.Actions)
-			s.Equal(tc.expected, trigger, "should have correct trigger from NewTrigger")
+			trigger, err := s.app.TriggerKeeper.NewTriggerWithID(s.ctx, tc.expected.Owner, tc.expected.Event, tc.expected.Actions)
+			s.Require().NoError(err, "NewTriggerWithID should not return an error")
+			s.Equal(tc.expected, trigger, "should have correct trigger from NewTrigger")
x/trigger/keeper/queue.go (1)

23-44: Fix queue metadata handling: QueueStartIndex still uninitialized, QueuePeek and Dequeue bypass helpers

On a fresh store, Enqueue sets QueueLength but never initializes QueueStartIndex. QueuePeek and Dequeue then read QueueStartIndex directly via .Get, leading to:

  • QueuePeek: QueueIsEmpty is false (length == 1), but QueueStartIndex.Get returns collections.ErrNotFound, so it returns nil even though an item exists.
  • Dequeue: same situation, but it panics on the ErrNotFound.

You already have helpers getQueueStartIndex and GetQueueLength that default missing metadata to 0; using them here fixes the bug and keeps behavior consistent.

Suggested fix:

func (k Keeper) QueuePeek(ctx sdk.Context) *types.QueuedTrigger {
-   isEmpty := k.QueueIsEmpty(ctx)
-
-   if isEmpty {
-       return nil
-   }
-   startIndex, err := k.QueueStartIndex.Get(ctx)
-   if err != nil {
-       if errors.Is(err, collections.ErrNotFound) {
-           return nil
-       }
-       return nil
-   }
+   if k.QueueIsEmpty(ctx) {
+       return nil
+   }
+   startIndex, err := k.getQueueStartIndex(ctx)
+   if err != nil {
+       // optional: log the error before treating as empty
+       return nil
+   }
    item, err := k.Queue.Get(ctx, startIndex)
    if err != nil {
        if errors.Is(err, collections.ErrNotFound) {
            return nil
        }
        return nil
    }
    return &item
}

func (k Keeper) Dequeue(ctx sdk.Context) {
    if k.QueueIsEmpty(ctx) {
        panic("unable to dequeue from empty queue.")
    }

-   // Get the start index
-   startIndex, err := k.QueueStartIndex.Get(ctx)
-   if err != nil {
-       panic(err)
-   }
-
-   // Get the queue length
-   length, err := k.QueueLength.Get(ctx)
-   if err != nil {
-       panic(err)
-   }
+   startIndex, err := k.getQueueStartIndex(ctx)
+   if err != nil {
+       panic(err)
+   }
+   length, err := k.GetQueueLength(ctx)
+   if err != nil {
+       panic(err)
+   }

    // Remove the first item
    if err := k.Queue.Remove(ctx, startIndex); err != nil {
        panic(err)
    }

    // Update queue metadata
    newStart := startIndex + 1
    newLength := length - 1

    if err := k.QueueStartIndex.Set(ctx, newStart); err != nil {
        panic(err)
    }

    if err := k.QueueLength.Set(ctx, newLength); err != nil {
        panic(err)
    }
}

This makes a newly initialized queue behave correctly and avoids panics due to missing metadata.

Also applies to: 76-110

🧹 Nitpick comments (4)
x/trigger/keeper/event_listener_test.go (1)

174-237: Harden event-listener tests by checking GetTriggerEventI errors and aligning setup

In the new collections tests you correctly assert on errors from SetTrigger, SetEventListener, IterateEventListeners, and RemoveEventListener, but still ignore the error from GetTriggerEventI (e.g., eventI, _ := trigger1.GetTriggerEventI() and the similar call in TestCollectionsRemoveAllEventListenersForTrigger). It would be more robust to capture and Require().NoError these, and to update the older tests in this file to also handle the new error-returning SetTrigger/SetEventListener signatures instead of discarding them, so setup failures can’t silently pass.

A possible tweak here:

-	eventI, _ := trigger1.GetTriggerEventI()
+	eventI, err := trigger1.GetTriggerEventI()
+	s.Require().NoError(err, "failed to get TriggerEventI for removed trigger1")

(and likewise for the other call sites and older tests’ setup).

x/trigger/keeper/trigger_registry.go (1)

10-36: Good step with logging; consider whether callers should see register/unregister failures

Adding error logging around SetTrigger, SetEventListener, and RemoveEventListener is an improvement over the previous silent failures. Depending on how RegisterTrigger/UnregisterTrigger are used, you may eventually want these to surface an error or status back to callers instead of only logging, so that failed registration/unregistration cannot be mistaken for success. For now, the behavior is backward‑compatible, so this can stay a follow‑up consideration.

x/trigger/keeper/event_listener.go (1)

106-141: Consider reusing types.GetEventNameBytes instead of duplicating hashing logic

getEventNameHash replicates the normalization and sha256 hashing already implemented in types.GetEventNameBytes. To avoid drift if that logic ever changes, consider delegating:

func getEventNameHash(name string) []byte {
    return types.GetEventNameBytes(name)
}

This keeps all event-name hashing rules centralized in the types package.

x/trigger/keeper/queue.go (1)

13-20: Queue helpers and iteration mostly look good; consider small cleanups

  • QueueTrigger now logs failures from Enqueue, which is a sensible non-breaking improvement.
  • QueueIsEmpty, GetAllQueueItems, GetQueueItem, SetQueueItem, RemoveQueueIndex, IterateQueue, and the get/set helpers for start index and length are consistent with the collections-based design and error semantics.

Minor optional cleanups:

  • In GetAllQueueItems, you don't need iter.Key() if the key is unused; relying on iter.Value() is enough.
  • In QueueIsEmpty, you might want to log unexpected errors instead of silently treating them as "empty".

These are non-blocking and can be deferred.

Also applies to: 112-143, 145-205, 206-238

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6de171a and 3607aad.

📒 Files selected for processing (12)
  • x/trigger/keeper/event_detector_test.go (1 hunks)
  • x/trigger/keeper/event_listener.go (2 hunks)
  • x/trigger/keeper/event_listener_test.go (1 hunks)
  • x/trigger/keeper/genesis.go (2 hunks)
  • x/trigger/keeper/keeper.go (2 hunks)
  • x/trigger/keeper/migrations_v2.go (2 hunks)
  • x/trigger/keeper/queue.go (2 hunks)
  • x/trigger/keeper/queue_test.go (1 hunks)
  • x/trigger/keeper/trigger_registry.go (1 hunks)
  • x/trigger/keeper/trigger_test.go (2 hunks)
  • x/trigger/types/codec.go (2 hunks)
  • x/trigger/types/keys.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • x/trigger/keeper/event_detector_test.go
  • x/trigger/keeper/migrations_v2.go
🧰 Additional context used
🧠 Learnings (18)
📚 Learning: 2024-10-08T18:12:51.935Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 1993
File: x/marker/client/cli/cli_test.go:1090-1090
Timestamp: 2024-10-08T18:12:51.935Z
Learning: SpicyLemon prefers allowing tests to fail naturally if the setup is incorrect rather than explicitly checking and handling such errors within the test functions. They also recommend using `s.Require().NotNil(...)` instead of `s.T().Fatal(...)` for assertions in test functions.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
  • x/trigger/keeper/trigger_test.go
📚 Learning: 2024-10-10T23:19:54.565Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2176
File: app/upgrades_test.go:610-610
Timestamp: 2024-10-10T23:19:54.565Z
Learning: In `app/upgrades_test.go`, we prefer to keep TODO comments (like `TODO[viridian]`) to handle code deletions manually, and do not want to implement automated processes for code removal, especially for unit tests.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
  • x/trigger/keeper/trigger_test.go
  • x/trigger/types/keys.go
📚 Learning: 2025-08-15T20:57:04.455Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/entries_test.go:26-31
Timestamp: 2025-08-15T20:57:04.455Z
Learning: SpicyLemon prefers string-based error assertions in tests using empty string to indicate "no error expected" and non-empty string for expected error messages. Use assertions.AssertErrorValue instead of errors.Is() comparisons because errors.Is() with s.Require().True() provides poor failure messages that only say "Should be true" without showing expected vs actual error content.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
  • x/trigger/keeper/trigger_test.go
📚 Learning: 2025-08-15T20:34:01.110Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/codec.go:9-10
Timestamp: 2025-08-15T20:34:01.110Z
Learning: In Go Cosmos SDK projects, sdk.Msg interface embeds proto.Message interface, making it valid to copy elements from []sdk.Msg to []proto.Message even though the slice types are different. The pattern of declaring AllRequestMsgs as []sdk.Msg for type assertions while copying to []proto.Message for RegisterImplementations is a common and correct pattern.

Applied to files:

  • x/trigger/types/codec.go
📚 Learning: 2025-08-15T20:34:01.110Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/codec.go:9-10
Timestamp: 2025-08-15T20:34:01.110Z
Learning: The pattern of declaring AllRequestMsgs as []sdk.Msg and then copying to []proto.Message for RegisterImplementations is a standard, correct pattern used throughout the Provenance codebase. This works because sdk.Msg interface embeds proto.Message interface, making the copy operation valid. This pattern appears consistently across 15+ modules and compiles without errors.

Applied to files:

  • x/trigger/types/codec.go
📚 Learning: 2024-10-02T03:04:32.929Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/migrations_v4_test.go:253-479
Timestamp: 2024-10-02T03:04:32.929Z
Learning: In Go tests within this project, it's acceptable to have similar helper functions like `newUUID`, `newScopeID`, `newSpecID`, and `newScope` defined within each test. Tests may tailor these functions to specific needs rather than sharing them, to avoid breaking other tests when changes are made and to keep them non-public.

Applied to files:

  • x/trigger/keeper/trigger_test.go
📚 Learning: 2025-08-15T20:01:55.078Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: app/upgrades_test.go:0-0
Timestamp: 2025-08-15T20:01:55.078Z
Learning: SpicyLemon prefers upgrade tests to fail when expected data is missing rather than skip, using s.Require().NoError() and s.Require().NotEmpty() to enforce hard requirements for upgrade data files in test scenarios.

Applied to files:

  • x/trigger/keeper/trigger_test.go
📚 Learning: 2024-10-02T03:37:18.486Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/msg_server_test.go:1225-1228
Timestamp: 2024-10-02T03:37:18.486Z
Learning: When test cases are run using `s.T().Run` instead of `s.Run`, use `require` with `t` instead of `s.Require()`, to ensure that assertions apply to the correct test instance.

Applied to files:

  • x/trigger/keeper/trigger_test.go
📚 Learning: 2024-10-02T03:42:47.070Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/query_server_test.go:940-945
Timestamp: 2024-10-02T03:42:47.070Z
Learning: In the `TestScopeQuery` function, the conditional checks following the equality assertion are designed to help identify differences when `tc.expResp` and `actResp` are not equal and both are not `nil`. If they are equal, or either is `nil`, the failure message from the equality assertion suffices, and further checks are not needed.

Applied to files:

  • x/trigger/keeper/trigger_test.go
📚 Learning: 2024-10-08T18:12:51.935Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/account_data_test.go:11-11
Timestamp: 2024-10-08T18:12:51.935Z
Learning: Do not raise issues about the `keeper` package still being in use after import removal in test files when there are no unresolved references.

Applied to files:

  • x/trigger/keeper/trigger_test.go
  • x/trigger/keeper/keeper.go
📚 Learning: 2025-08-15T22:52:24.461Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/keeper.go:74-173
Timestamp: 2025-08-15T22:52:24.461Z
Learning: In the ledger keeper NewKeeper function in x/ledger/keeper/keeper.go, SpicyLemon prefers to allow bankKeeper and registryKeeper dependencies to be nil rather than adding validation that would panic on nil values. The design intentionally supports nil keepers for flexibility.

Applied to files:

  • x/trigger/keeper/keeper.go
  • x/trigger/keeper/genesis.go
📚 Learning: 2025-08-15T20:59:09.909Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: The BankKeeper interface methods in Cosmos SDK v0.50.x and Provenance use `context.Context` as the context parameter type, not `sdk.Context`. This applies to methods like SendCoins, HasBalance, GetBalance, SpendableCoin, HasSupply, etc. The expected keeper interfaces should consistently use `context.Context` to match the actual Cosmos SDK bank keeper signatures.

Applied to files:

  • x/trigger/keeper/keeper.go
  • x/trigger/keeper/queue.go
  • x/trigger/keeper/genesis.go
📚 Learning: 2024-10-02T02:06:21.664Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/migrations_v4.go:57-80
Timestamp: 2024-10-02T02:06:21.664Z
Learning: The `V3WriteNewScope` method in `x/metadata/keeper/migrations_v4.go` must exist until after the migration has been executed and will be deleted as part of the `viridian` cleanup.

Applied to files:

  • x/trigger/keeper/keeper.go
📚 Learning: 2025-08-15T20:19:31.726Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/simulation/decoder.go:8-14
Timestamp: 2025-08-15T20:19:31.726Z
Learning: The asset module in Provenance doesn't use collections-based storage like ledger/registry modules. Instead, it delegates storage to the NFT module, so a collections-based store decoder isn't applicable here. The module should use its own types.StoreKey rather than exchange.StoreKey in RegisterStoreDecoder.

Applied to files:

  • x/trigger/keeper/keeper.go
📚 Learning: 2024-10-08T18:12:51.935Z
Learnt from: iramiller
Repo: provenance-io/provenance PR: 2160
File: x/metadata/client/cli/tx.go:1196-1212
Timestamp: 2024-10-08T18:12:51.935Z
Learning: When checking for indentation issues in multi-line strings assigned to the `Long` field in Go code, verify that the source lines are actually indented before flagging them.

Applied to files:

  • x/trigger/keeper/queue_test.go
📚 Learning: 2024-10-02T01:43:01.360Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/marker/types/send_restrictions.go:11-11
Timestamp: 2024-10-02T01:43:01.360Z
Learning: In `x/marker/types/send_restrictions.go`, the `transferAgentKey` is only used for storing data in ephemeral contexts, so changing its value does not impact backward compatibility.

Applied to files:

  • x/trigger/types/keys.go
📚 Learning: 2024-10-08T18:12:51.935Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/types/signer_utils_test.go:478-493
Timestamp: 2024-10-08T18:12:51.935Z
Learning: In Go tests, modifying byte slices directly using arithmetic operations like addition and subtraction is acceptable, even if it causes overflow, as overflow on `uint8` just wraps around. Avoid flagging such usage, and avoid suggesting bitwise operations which can be harder to read and understand.

Applied to files:

  • x/trigger/types/keys.go
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: The `CreateAutoResponseToAddrPrefix` function in the quarantine module should have unit tests that assert specific panic messages to ensure the tests fail for the correct reasons, specifically when the `MustLengthPrefix` function triggers a panic due to address length issues.

Applied to files:

  • x/trigger/types/keys.go
🧬 Code graph analysis (8)
x/trigger/keeper/trigger_registry.go (2)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/keeper/event_listener_test.go (5)
x/trigger/keeper/keeper_test.go (1)
  • KeeperTestSuite (28-41)
x/trigger/types/trigger.pb.go (9)
  • BlockHeightEvent (170-173)
  • BlockHeightEvent (177-177)
  • BlockHeightEvent (178-180)
  • BlockTimeEvent (216-219)
  • BlockTimeEvent (223-223)
  • BlockTimeEvent (224-226)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/tx.pb.go (3)
  • MsgDestroyTriggerRequest (145-150)
  • MsgDestroyTriggerRequest (154-154)
  • MsgDestroyTriggerRequest (155-157)
x/trigger/types/trigger.go (1)
  • NewTrigger (149-156)
x/trigger/types/errors.go (1)
  • ErrEventNotFound (9-9)
x/trigger/keeper/event_listener.go (3)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/errors.go (2)
  • ErrEventNotFound (9-9)
  • ErrTriggerNotFound (8-8)
x/trigger/keeper/trigger_test.go (3)
x/trigger/keeper/keeper_test.go (1)
  • KeeperTestSuite (28-41)
x/trigger/types/trigger.pb.go (3)
  • BlockHeightEvent (170-173)
  • BlockHeightEvent (177-177)
  • BlockHeightEvent (178-180)
x/trigger/types/tx.pb.go (3)
  • MsgDestroyTriggerRequest (145-150)
  • MsgDestroyTriggerRequest (154-154)
  • MsgDestroyTriggerRequest (155-157)
x/trigger/keeper/keeper.go (3)
x/trigger/types/trigger.pb.go (6)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
  • QueuedTrigger (106-113)
  • QueuedTrigger (117-117)
  • QueuedTrigger (118-120)
x/trigger/types/keys.go (6)
  • TriggerKeyPrefix (59-59)
  • EventListenerKeyPrefix (61-61)
  • QueueKeyPrefix (63-63)
  • NextTriggerIDKey (65-65)
  • QueueStartIndexKey (67-67)
  • QueueLengthKey (69-69)
x/trigger/types/codec.go (1)
  • EventListenerKeyCodec (115-121)
x/trigger/keeper/queue.go (3)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • QueuedTrigger (106-113)
  • QueuedTrigger (117-117)
  • QueuedTrigger (118-120)
x/trigger/types/errors.go (1)
  • ErrQueueIndexNotFound (10-10)
x/trigger/keeper/queue_test.go (3)
x/trigger/keeper/keeper_test.go (1)
  • KeeperTestSuite (28-41)
x/trigger/types/trigger.pb.go (6)
  • BlockHeightEvent (170-173)
  • BlockHeightEvent (177-177)
  • BlockHeightEvent (178-180)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/tx.pb.go (3)
  • MsgDestroyTriggerRequest (145-150)
  • MsgDestroyTriggerRequest (154-154)
  • MsgDestroyTriggerRequest (155-157)
x/trigger/keeper/genesis.go (2)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/genesis.pb.go (3)
  • GenesisState (27-39)
  • GenesisState (43-43)
  • GenesisState (44-46)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: tests (04)
  • GitHub Check: test-race (00)
  • GitHub Check: tests (03)
  • GitHub Check: test-race (03)
  • GitHub Check: tests (00)
  • GitHub Check: test-race (02)
  • GitHub Check: test-race (04)
  • GitHub Check: tests (02)
  • GitHub Check: tests (01)
  • GitHub Check: test-race (01)
  • GitHub Check: build-linux
  • GitHub Check: Build Linux
  • GitHub Check: Build OSX
  • GitHub Check: docker
  • GitHub Check: heighliner-docker
  • GitHub Check: ghcr-publish
  • GitHub Check: golangci-lint
  • GitHub Check: Analyze (go)
🔇 Additional comments (6)
x/trigger/keeper/trigger_test.go (1)

208-282: New collections-based trigger tests provide solid coverage

TestTriggerCollectionsOperations and TestTriggerIDSequence exercise the collections-backed SetTrigger, GetTrigger, HasTrigger, RemoveTrigger, and GetNextTriggerID/NewTriggerWithID paths, asserting on errors and expected IDs. This is good targeted coverage of the new storage model.

x/trigger/keeper/genesis.go (1)

13-34: Centralized must helper and error-aware genesis wiring look good

Wiring all genesis reads/writes through getTriggerID/getQueueStartIndex/GetAll* plus the must helper gives consistent, contextual panics for collections-backed state initialization, while preserving the “fail fast” behavior on invalid genesis or storage errors. The updated NewGenesisState construction also cleanly reflects the new queue/triggers layout. LGTM.

Also applies to: 42-62

x/trigger/keeper/keeper.go (1)

15-27: Keeper wiring to collections and StoreService assignment look correct

The keeper now correctly stores the store.KVStoreService, builds a collections schema, and initializes all maps/items with the expected prefixes, matching the documented key layout and avoiding a migration. No issues spotted in the struct layout or NewKeeper wiring.

Also applies to: 30-58

x/trigger/types/codec.go (1)

47-121: Event listener key codecs correctly preserve the legacy key layout

FixedBytes32KeyCodec and EventListenerKeyCodec implement the collections key codec interface as expected and encode keys as [32-byte-hash][8-byte-order][8-byte-triggerID], matching the legacy event-listener key structure when combined with the 0x02 prefix. The explicit JSON-not-implemented errors are reasonable here.

x/trigger/types/keys.go (1)

57-69: Prefix migration to collections.Prefix maintains on-disk key compatibility

The switch to collections.NewPrefix(...) for all trigger-related prefixes, combined with using .Bytes() (or explicit []byte conversion) in the helpers, preserves the documented key layout and keeps both collections and legacy raw-key helpers usable without a migration.

Also applies to: 85-131

x/trigger/keeper/event_listener.go (1)

16-68: Collections-based event listener storage and iteration look sound

Using a KeySet over the triple (eventHash, order, triggerID) plus NewPrefixedTripleRange yields the correct per-event iteration semantics, and GetEventListener/IterateEventListeners appropriately rely on GetTrigger and treat ErrTriggerNotFound as a soft failure. Overall logic and error handling here look solid.

Also applies to: 70-104

Comment thread x/trigger/keeper/queue_test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
x/trigger/keeper/event_listener.go (2)

36-47: Consider simplifying the existence check.

The Has check before Remove is defensive but potentially redundant. The collections API's Remove method should indicate whether the key existed. However, the current explicit approach is clear and safe.


125-125: Incorrect or outdated nolint comment.

The comment //nolint:revive // safe conversion suggests a type conversion, but count is already declared as uint64, so no conversion occurs. This comment should be removed or clarified if it addresses a different linting issue.

Apply this diff:

-	err := k.IterateEventListeners(ctx, eventName, func(trigger triggertypes.Trigger) (bool, error) { //nolint:revive // safe conversion
+	err := k.IterateEventListeners(ctx, eventName, func(trigger triggertypes.Trigger) (bool, error) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3607aad and 6a5703d.

📒 Files selected for processing (2)
  • x/trigger/keeper/event_listener.go (2 hunks)
  • x/trigger/simulation/genesis.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • x/trigger/simulation/genesis.go
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/migrations_v4.go:57-80
Timestamp: 2024-10-08T18:12:51.935Z
Learning: The `V3WriteNewScope` method in `x/metadata/keeper/migrations_v4.go` must exist until after the migration has been executed and will be deleted as part of the `viridian` cleanup.
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/migrations_v4.go:57-80
Timestamp: 2024-10-02T02:06:21.664Z
Learning: The `V3WriteNewScope` method in `x/metadata/keeper/migrations_v4.go` must exist until after the migration has been executed and will be deleted as part of the `viridian` cleanup.
🧬 Code graph analysis (1)
x/trigger/keeper/event_listener.go (4)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
x/trigger/types/errors.go (2)
  • ErrEventNotFound (9-9)
  • ErrTriggerNotFound (8-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: tests (03)
  • GitHub Check: tests (04)
  • GitHub Check: test-race (04)
  • GitHub Check: test-race (02)
  • GitHub Check: tests (01)
  • GitHub Check: tests (02)
  • GitHub Check: test-race (01)
  • GitHub Check: tests (00)
  • GitHub Check: test-race (03)
  • GitHub Check: test-race (00)
  • GitHub Check: heighliner-docker
  • GitHub Check: ghcr-publish
  • GitHub Check: docker
  • GitHub Check: build-linux
  • GitHub Check: Build Linux
  • GitHub Check: Build OSX
  • GitHub Check: golangci-lint
  • GitHub Check: Analyze (go)
🔇 Additional comments (6)
x/trigger/keeper/event_listener.go (6)

3-13: LGTM: Imports align with collections migration.

The imports correctly reflect the migration to collections-based storage, including the necessary crypto, error handling, and collections packages.


16-24: LGTM: Clean migration to collections API.

The function correctly uses the hashed composite key approach and returns errors appropriately.


53-67: LGTM: Correct separation of concerns.

The explicit Has check before fetching the trigger correctly distinguishes between "event listener entry missing" and "trigger missing," returning the appropriate error type.


105-110: LGTM: Clean helper function.

The implementation is straightforward and correctly delegates to the collections API.


134-134: Event name case normalization is intentional and consistently applied.

The verification confirms that lowercase normalization of event names is deliberate design, not an oversight. The same normalization (strings.ToLower(strings.TrimSpace(name))) is applied in both getEventNameHash (event_listener.go:134) and GetEventNameBytes (keys.go:159), ensuring all event listener lookups use consistent hashing. Tests validate this behavior works correctly. There are no collisions or unexpected case-sensitivity issues because all event name operations uniformly normalize at the storage/lookup layer.


86-93: Defensive handling of orphaned event listener entries is necessary and correctly implemented.

The code correctly skips triggers that are not found (lines 89-91). Verification confirms orphaned event listener entries are indeed possible: if RemoveEventListener fails during UnregisterTrigger (trigger_registry.go:28-33), the listener persists while its trigger is deleted. The defensive check properly handles this edge case.

However, orphaned entries can accumulate if removal failures occur repeatedly. Consider whether a periodic cleanup mechanism is warranted to prevent unbounded growth of orphaned entries in storage.

Comment thread x/trigger/keeper/event_listener.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
x/trigger/keeper/event_listener.go (1)

126-134: Function name is misleading.

The function is named RemoveAllEventListenersForTrigger but only removes a single event listener. Since a Trigger has only one Event field, this is functionally correct, but the name suggests it would remove multiple entries.

Consider renaming to RemoveEventListenerForTrigger to match the actual behavior.

Apply this diff:

-// RemoveAllEventListenersForTrigger removes all event listeners for a specific trigger
-func (k Keeper) RemoveAllEventListenersForTrigger(ctx sdk.Context, triggerID uint64) error {
+// RemoveEventListenerForTrigger removes the event listener for a specific trigger
+func (k Keeper) RemoveEventListenerForTrigger(ctx sdk.Context, triggerID uint64) error {
🧹 Nitpick comments (2)
x/trigger/keeper/event_listener.go (2)

99-104: Silently skipping missing triggers may hide inconsistencies.

When a trigger is not found (line 100-101), the code continues iteration without logging or alerting. This could occur if an event listener key exists but its corresponding trigger was deleted, indicating data inconsistency. While this defensive approach prevents iteration failures, it may mask bugs during development or migration.

Consider logging a warning when this occurs to aid debugging.

Example:

 		trigger, err := k.GetTrigger(ctx, triggerID)
 		if err != nil {
 			if errors.Is(err, triggertypes.ErrTriggerNotFound) {
+				// Log warning about orphaned event listener
+				ctx.Logger().Warn("event listener references non-existent trigger", "triggerID", triggerID, "eventName", eventName)
 				continue
 			}
 			return err
 		}

146-154: Consider using a more specific error for invalid input.

Returning ErrEventNotFound for an empty event name (line 150) is semantically misleading. This error typically means "event not found in storage," but an empty string is actually invalid input rather than a missing entry.

Consider wrapping the error with more context:

 func getEventNameHash(name string) ([]byte, error) {
 	eventName := strings.ToLower(strings.TrimSpace(name))
 	if len(eventName) == 0 {
-		return nil, triggertypes.ErrEventNotFound
+		return nil, triggertypes.ErrEventNotFound.Wrap("event name cannot be empty")
 	}
 	hash := sha256.Sum256([]byte(eventName))
 	return hash[:], nil
 }

This provides clearer context for debugging while maintaining error type consistency.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a5703d and 7d8f767.

📒 Files selected for processing (1)
  • x/trigger/keeper/event_listener.go (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: The `GetLockedCoins` function in `x/hold/keeper/locked_coins.go` must conform to the `banktypes.GetLockedCoinsFn` type, which does not allow returning an error. In cases where `k.GetHoldCoins` encounters an error, using `panic` is considered appropriate due to the severity of such errors.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2025-08-15T20:39:34.095Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/util.go:68-102
Timestamp: 2025-08-15T20:39:34.095Z
Learning: In the Provenance codebase, functions with "Must" prefix (like MustGetMarkerAddress) can panic and should only be used when you know they won't panic. Instead, use the twin function that returns an error (like GetMarkerAddress) and handle the error properly.

Applied to files:

  • x/trigger/keeper/event_listener.go
🧬 Code graph analysis (1)
x/trigger/keeper/event_listener.go (4)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
x/trigger/types/errors.go (2)
  • ErrEventNotFound (9-9)
  • ErrTriggerNotFound (8-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
  • GitHub Check: tests (01)
  • GitHub Check: tests (04)
  • GitHub Check: tests (03)
  • GitHub Check: tests (00)
  • GitHub Check: tests (02)
  • GitHub Check: test-race (03)
  • GitHub Check: test-race (01)
  • GitHub Check: test-race (00)
  • GitHub Check: test-race (04)
  • GitHub Check: test-race (02)
  • GitHub Check: ghcr-publish
  • GitHub Check: heighliner-docker
  • GitHub Check: docker
  • GitHub Check: Build Linux
  • GitHub Check: Build OSX
  • GitHub Check: Analyze (go)
  • GitHub Check: golangci-lint
🔇 Additional comments (6)
x/trigger/keeper/event_listener.go (6)

4-8: LGTM: Imports are appropriate.

The new imports support the collections-based implementation and event name hashing.


16-27: LGTM: SetEventListener correctly stores hashed composite keys.

The function properly hashes the event name, constructs a composite key, and stores it in the EventListeners KeySet.


30-56: LGTM: RemoveEventListener has correct semantics.

The function appropriately checks existence before removal and returns (false, nil) when the listener doesn't exist, which is good defensive programming.


59-76: LGTM: GetEventListener correctly handles KeySet design.

The function appropriately checks the EventListeners KeySet for existence, then fetches the actual trigger from the TriggersMap. This is correct since EventListeners stores only relationships, not trigger data.


116-124: LGTM: HasEventListener is a useful helper.

This function provides a clean way to check event listener existence without fetching the trigger.


136-144: LGTM: GetEventListenerCount is correct but iterates all entries.

The function correctly counts event listeners by iterating all matching entries. While this has O(n) complexity, it's acceptable given the collections KeySet API doesn't provide a direct count mechanism.

Comment thread x/trigger/types/keys.go Outdated
Comment thread x/trigger/keeper/event_listener.go Outdated
@SpicyLemon

Copy link
Copy Markdown
Contributor

The import-export sim is failing: https://github.com/provenance-io/provenance/actions/runs/19579700988/job/56074364016?pr=2546

That test starts a new chain, runs some Txs, stops the chain, exports genesis, then creates a new chain by importing that genesis file. It then checks that both the chains have the exact same state. It's failing, which usually means that state is changing between export and import genesis. You'll need to fix that.

Comment thread x/trigger/keeper/event_listener.go Outdated
Comment thread x/trigger/keeper/event_listener.go Outdated
Comment thread x/trigger/keeper/genesis.go Outdated
Comment thread x/trigger/keeper/query_server.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (8)
x/trigger/keeper/event_listener_test.go (2)

201-209: Drop or use the unused expectErr field

The expectErr field in the table for TestCollectionsSetAndGetEventListener is never read. Either assert on err using this flag or remove the field to keep the test table minimal and easier to maintain.


239-258: Align test name with RemoveEventListenerForTriggerID behavior

The test is still named TestCollectionsRemoveAllEventListenersForTrigger, but it exercises RemoveEventListenerForTriggerID, which removes a single listener for one trigger ID. Renaming the test to match the method name would reduce confusion when grepping for this behavior.

x/trigger/keeper/query_server.go (1)

26-35: Wrap non‑ErrNotFound errors in TriggerByID with more context

You correctly translate collections.ErrNotFound to types.ErrTriggerNotFound, but other errors are returned as‑is. For consistency with the rest of the keeper (where errors are wrapped with context) and easier debugging, consider wrapping here too, e.g. including the trigger ID in the message before returning.

x/trigger/keeper/event_listener.go (4)

17-35: Centralize key construction via GetEventListenerKey

SetEventListener uses triggertypes.GetEventListenerKey, but RemoveEventListener builds keys with collections.Join3 directly. For consistency and to avoid future representation drift, consider using GetEventListenerKey everywhere you construct an event-listener key (including in RemoveEventListener, GetEventListener, and HasEventListener).

Also applies to: 38-64


38-64: Wrap propagated errors with context for easier debugging

Most of these methods (RemoveEventListener, GetEventListener, IterateEventListeners, HasEventListener, RemoveEventListenerForTriggerID, GetEventListenerCount) return underlying errors directly, while SetEventListener wraps them with helpful context. Adding similar context (e.g., trigger ID, event prefix) before returning would make it much easier to trace failures in production.

Also applies to: 66-84, 86-122, 124-152


86-122: Double-check the decision to silently skip missing triggers in iteration

In IterateEventListeners, you continue when GetTrigger returns ErrTriggerNotFound. That’s defensively safe, but it also silently hides inconsistencies between the event-listener index and the trigger store. Consider at least wrapping this with logging or surfacing a different error if missing triggers should never happen.


134-142: Update comment to match RemoveEventListenerForTriggerID behavior

The comment still says “RemoveAllEventListenersForTrigger removes all event listeners for a specific trigger”, but the function is named RemoveEventListenerForTriggerID and delegates to RemoveEventListener for a single trigger. Update the comment (and any external references) so the name and description align.

x/trigger/types/keys.go (1)

67-69: Fix QueueLengthPrefix comment typo

The comment says QueueLengthPrefix but the identifier is QueueLengthKey. Updating the comment to match the actual name will avoid confusion when reading or grepping the code.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d8f767 and fa981be.

📒 Files selected for processing (7)
  • x/trigger/keeper/event_listener.go (2 hunks)
  • x/trigger/keeper/event_listener_test.go (1 hunks)
  • x/trigger/keeper/genesis.go (2 hunks)
  • x/trigger/keeper/query_server.go (3 hunks)
  • x/trigger/simulation/genesis.go (1 hunks)
  • x/trigger/types/keys.go (2 hunks)
  • x/trigger/types/keys_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • x/trigger/simulation/genesis.go
🧰 Additional context used
🧠 Learnings (14)
📓 Common learnings
Learnt from: nullpointer0x00
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: A GitHub issue was created to track the migration of parameters for the metadata, marker, name, and msgFees modules as indicated by the TODO comments in the codebase. The issue is linked to PR #1932 and can be found at https://github.com/provenance-io/provenance/issues/1935.
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: The BankKeeper interface methods in Cosmos SDK v0.50.x and Provenance use `context.Context` as the context parameter type, not `sdk.Context`. This applies to methods like SendCoins, HasBalance, GetBalance, SpendableCoin, HasSupply, etc. The expected keeper interfaces should consistently use `context.Context` to match the actual Cosmos SDK bank keeper signatures.
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: The `GetLockedCoins` function in `x/hold/keeper/locked_coins.go` must conform to the `banktypes.GetLockedCoinsFn` type, which does not allow returning an error. In cases where `k.GetHoldCoins` encounters an error, using `panic` is considered appropriate due to the severity of such errors.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2025-08-15T20:39:34.095Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/util.go:68-102
Timestamp: 2025-08-15T20:39:34.095Z
Learning: In the Provenance codebase, functions with "Must" prefix (like MustGetMarkerAddress) can panic and should only be used when you know they won't panic. Instead, use the twin function that returns an error (like GetMarkerAddress) and handle the error properly.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2024-10-10T23:13:56.722Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2176
File: x/ibchooks/types/params.go:22-26
Timestamp: 2024-10-10T23:13:56.722Z
Learning: In the file `x/ibchooks/types/params.go`, maintain the existing behavior in the `Validate` method without modifying the error messages, even if they may lack additional context.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2024-10-08T18:12:51.936Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/msg_server.go:106-109
Timestamp: 2024-10-08T18:12:51.936Z
Learning: In the `AddScopeDataAccess` method (and similar cases), redeclaring `err` with `:=` is appropriate when previous `err` variables are scoped within `if` statements, as using `=` would result in a compilation error.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2024-10-08T18:12:51.935Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 1993
File: x/marker/client/cli/cli_test.go:1090-1090
Timestamp: 2024-10-08T18:12:51.935Z
Learning: SpicyLemon prefers allowing tests to fail naturally if the setup is incorrect rather than explicitly checking and handling such errors within the test functions. They also recommend using `s.Require().NotNil(...)` instead of `s.T().Fatal(...)` for assertions in test functions.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
📚 Learning: 2024-10-10T23:19:54.565Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2176
File: app/upgrades_test.go:610-610
Timestamp: 2024-10-10T23:19:54.565Z
Learning: In `app/upgrades_test.go`, we prefer to keep TODO comments (like `TODO[viridian]`) to handle code deletions manually, and do not want to implement automated processes for code removal, especially for unit tests.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
📚 Learning: 2025-08-15T20:57:04.455Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/entries_test.go:26-31
Timestamp: 2025-08-15T20:57:04.455Z
Learning: SpicyLemon prefers string-based error assertions in tests using empty string to indicate "no error expected" and non-empty string for expected error messages. Use assertions.AssertErrorValue instead of errors.Is() comparisons because errors.Is() with s.Require().True() provides poor failure messages that only say "Should be true" without showing expected vs actual error content.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
📚 Learning: 2025-08-15T22:52:24.461Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/keeper.go:74-173
Timestamp: 2025-08-15T22:52:24.461Z
Learning: In the ledger keeper NewKeeper function in x/ledger/keeper/keeper.go, SpicyLemon prefers to allow bankKeeper and registryKeeper dependencies to be nil rather than adding validation that would panic on nil values. The design intentionally supports nil keepers for flexibility.

Applied to files:

  • x/trigger/keeper/genesis.go
📚 Learning: 2025-08-15T20:59:09.909Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: The BankKeeper interface methods in Cosmos SDK v0.50.x and Provenance use `context.Context` as the context parameter type, not `sdk.Context`. This applies to methods like SendCoins, HasBalance, GetBalance, SpendableCoin, HasSupply, etc. The expected keeper interfaces should consistently use `context.Context` to match the actual Cosmos SDK bank keeper signatures.

Applied to files:

  • x/trigger/keeper/genesis.go
📚 Learning: 2024-10-02T01:43:01.360Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/marker/types/send_restrictions.go:11-11
Timestamp: 2024-10-02T01:43:01.360Z
Learning: In `x/marker/types/send_restrictions.go`, the `transferAgentKey` is only used for storing data in ephemeral contexts, so changing its value does not impact backward compatibility.

Applied to files:

  • x/trigger/types/keys_test.go
📚 Learning: 2024-10-02T03:06:21.997Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/migrations_v4_test.go:619-630
Timestamp: 2024-10-02T03:06:21.997Z
Learning: When unit tests exist for a key generation function, it's preferable to reuse that function in tests instead of manually constructing byte slices.

Applied to files:

  • x/trigger/types/keys_test.go
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: The `CreateAutoResponseToAddrPrefix` function in the quarantine module should have unit tests that assert specific panic messages to ensure the tests fail for the correct reasons, specifically when the `MustLengthPrefix` function triggers a panic due to address length issues.

Applied to files:

  • x/trigger/types/keys.go
📚 Learning: 2025-08-15T20:59:51.949Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/keys.go:3-12
Timestamp: 2025-08-15T20:59:51.949Z
Learning: In Cosmos SDK, key prefixes only need to be unique within a given module's store, not across the entire application. Each module operates within its own isolated key-value store namespace, so the same byte prefixes can be safely reused across different modules without causing collisions.

Applied to files:

  • x/trigger/keeper/query_server.go
🧬 Code graph analysis (4)
x/trigger/keeper/event_listener.go (5)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/keys.go (1)
  • GetEventListenerKey (73-78)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
x/trigger/types/errors.go (2)
  • ErrEventNotFound (9-9)
  • ErrTriggerNotFound (8-8)
x/trigger/keeper/genesis.go (2)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/genesis.pb.go (3)
  • GenesisState (27-39)
  • GenesisState (43-43)
  • GenesisState (44-46)
x/trigger/types/keys.go (2)
x/trigger/simulation/genesis.go (1)
  • TriggerID (23-23)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
x/trigger/keeper/query_server.go (4)
x/trigger/types/errors.go (1)
  • ErrTriggerNotFound (8-8)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/query.pb.go (3)
  • QueryTriggersResponse (172-177)
  • QueryTriggersResponse (181-181)
  • QueryTriggersResponse (182-184)
x/trigger/simulation/genesis.go (1)
  • Triggers (27-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
  • GitHub Check: tests (04)
  • GitHub Check: test-race (04)
  • GitHub Check: test-race (02)
  • GitHub Check: test-race (03)
  • GitHub Check: test-race (00)
  • GitHub Check: tests (03)
  • GitHub Check: tests (02)
  • GitHub Check: tests (01)
  • GitHub Check: test-race (01)
  • GitHub Check: tests (00)
  • GitHub Check: Build Linux
  • GitHub Check: Build OSX
  • GitHub Check: golangci-lint
  • GitHub Check: ghcr-publish
  • GitHub Check: docker
  • GitHub Check: heighliner-docker
  • GitHub Check: Analyze (go)
🔇 Additional comments (7)
x/trigger/keeper/genesis.go (1)

13-33: ExportGenesis: collections-based access pattern looks consistent

Using getTriggerID, getQueueStartIndex, GetAllTriggers, and GetAllQueueItems here lines up with the new collections-based state layout, and panicking on unexpected errors during genesis export is reasonable since the module should never be in a partially valid state at this point. The construction of the GenesisState via types.NewGenesisState(triggerID, queueStartIndex, triggers, queue) also keeps the export logic nicely centralized.

x/trigger/keeper/event_listener_test.go (1)

168-170: RemoveEventListener test now correctly asserts on errors

Capturing the error and asserting NoError before checking success gives good coverage of the new (bool, error) signature and will catch regressions in the keeper implementation.

x/trigger/keeper/query_server.go (1)

46-61: Collection-based pagination wiring looks correct

Using query.CollectionPaginate over k.TriggersMap and rebinding trigger.Id = key ensures the response IDs align with the collection keys while leveraging collections-native pagination. The error is appropriately surfaced as an internal gRPC status.

x/trigger/types/keys_test.go (2)

13-26: Good reuse of GetEventNameBytes and new Triple accessors

The test now derives eventHash via GetEventNameBytes and checks K1/K2/K3 on the collections.Triple, which keeps the test aligned with the production hashing and key layout and avoids hand-rolled byte slicing.


29-34: Prefix and hash assertions correctly reflect new key layout

TestGetEventListenerPrefix still validates that the first byte is the event-listener prefix and the following 32 bytes match the event hash, which is the key compatibility we care about after migrating to collections.

x/trigger/types/keys.go (2)

58-70: Collections prefix migration preserves key layout

Switching the various *_Prefix/*Key vars to collections.NewPrefix and using .Bytes() in the helpers keeps the on-disk key bytes compatible with the previous scheme while making these prefixes usable with collections. The updated helpers (GetTriggerKey, GetEventListenerPrefix, queue keys) look consistent.

Also applies to: 85-122


72-78: GetEventListenerKey triple helper fits the collections-backed design

Having GetEventListenerKey return a collections.Triple[[]byte, uint64, uint64] aligns with the EventListeners keyset type and avoids manual byte concatenation. The panic on empty eventHash is acceptable here given callers now validate input before calling this helper and tests assert the panic message.

Comment thread x/trigger/keeper/event_listener.go
Comment thread x/trigger/keeper/genesis.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
x/trigger/keeper/event_listener.go (1)

153-161: Invalid event names still use ErrEventNotFound; consider a dedicated validation error

getEventNameHash returns triggertypes.ErrEventNotFound when the normalized event name is empty, which conflates invalid input with a genuinely missing listener; this can make it harder for callers to distinguish “buggy request” from “no listener registered”.

If you revisit this area, introducing something like ErrInvalidEventName (or reusing a general invalid-input error) for empty/whitespace names and reserving ErrEventNotFound for true “not present in store” cases would make error handling clearer.

🧹 Nitpick comments (5)
x/trigger/simulation/decoder.go (1)

41-43: Validate whether the length check should apply to the key or the value.

Here you guard on len(kvA.Key) < 1+types.TriggerIDLength, but the actual decoding uses kvA.Value / kvB.Value via GetTriggerIDFromBytes. If TriggerIDLength refers to the encoded uint64 length (8 bytes), it’s more typical that the value has that length while the key is just the prefix (length 1). In that common layout this condition would always fire (1 < 9), and you still wouldn’t protect against malformed values.

Please double‑check the actual layout of the NextTriggerID entry:

  • If the ID lives in the value (prefix‑only key), you probably want to validate kvA.Value/kvB.Value instead, e.g.:
-       if len(kvA.Key) < 1+types.TriggerIDLength {
-           return fmt.Sprintf("invalid trigger key length: %d", len(kvA.Key))
-       }
+       if len(kvA.Value) != types.TriggerIDLength || len(kvB.Value) != types.TriggerIDLength {
+           return fmt.Sprintf(
+               "invalid trigger ID value lengths: A=%d B=%d",
+               len(kvA.Value), len(kvB.Value),
+           )
+       }
  • If the ID is actually encoded into the key with layout <prefix><id-bytes>, then you probably want to slice the key for GetTriggerIDFromBytes instead of the value.

Either way, worth confirming to avoid silently misclassifying valid entries or leaving the real malformed data path unchecked.

x/trigger/keeper/keeper_test.go (1)

34-34: Clarify the purpose of the unused keeper field.

The keeper field is added but doesn't appear to be initialized in SetupTest or used in the test methods (which use s.app.TriggerKeeper instead). Is this field intended for future use, or should it be removed to avoid confusion?

x/trigger/keeper/genesis.go (2)

13-38: Genesis export logic and queue-length check look correct; consider hard‑failing on queue metadata errors

The switch to using collection helpers with panics on error for trigger ID, queue start, and trigger/queue reads is appropriate for genesis export and should prevent silent partial exports. The queue-length vs actual-queue warning is useful, but if GetQueueLength ever returns a real error (e.g. missing metadata), treating that as fatal (panic) instead of silently skipping the check would make corruption more obvious during simulations and debugging.


49-79: InitGenesis ordering fixes the queue‑metadata issue; panic messages could be slightly improved

Initializing QueueLength to 0 before enqueuing, then using Enqueue plus SetTrigger/SetEventListener, gives a clean, deterministic reconstruction of state and should address the earlier import–export mismatch. As a small polish, you could switch the panic(fmt.Sprintf(...)) calls to panic(fmt.Errorf("context: %w", err)) (or log + panic) for more structured error chaining and consistency with other keepers.

x/trigger/keeper/event_listener.go (1)

17-35: Event listener set/remove/has logic looks correct; consider wrapping propagated errors for context

The switch to hashed keys via getEventNameHash + GetEventListenerKey and using the EventListeners KeySet is sound, and the boolean return from RemoveEventListener nicely distinguishes “not found” from real errors. To aid debugging, you might mirror the SetEventListener style and wrap errors in RemoveEventListener, HasEventListener, and the HasEventListener callsites (e.g. “failed checking/removing event listener for trigger %d: %w”) instead of returning raw errors.

Based on learnings, wrapping errors with context is preferred in this codebase.

Also applies to: 38-64, 123-131

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa981be and 2d1111c.

📒 Files selected for processing (6)
  • x/trigger/keeper/event_listener.go (2 hunks)
  • x/trigger/keeper/genesis.go (3 hunks)
  • x/trigger/keeper/keeper_test.go (2 hunks)
  • x/trigger/simulation/decoder.go (1 hunks)
  • x/trigger/simulation/genesis.go (1 hunks)
  • x/trigger/types/keys.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • x/trigger/simulation/genesis.go
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: nullpointer0x00
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: A GitHub issue was created to track the migration of parameters for the metadata, marker, name, and msgFees modules as indicated by the TODO comments in the codebase. The issue is linked to PR #1932 and can be found at https://github.com/provenance-io/provenance/issues/1935.
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: The BankKeeper interface methods in Cosmos SDK v0.50.x and Provenance use `context.Context` as the context parameter type, not `sdk.Context`. This applies to methods like SendCoins, HasBalance, GetBalance, SpendableCoin, HasSupply, etc. The expected keeper interfaces should consistently use `context.Context` to match the actual Cosmos SDK bank keeper signatures.
📚 Learning: 2025-08-15T20:59:09.909Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: The BankKeeper interface methods in Cosmos SDK v0.50.x and Provenance use `context.Context` as the context parameter type, not `sdk.Context`. This applies to methods like SendCoins, HasBalance, GetBalance, SpendableCoin, HasSupply, etc. The expected keeper interfaces should consistently use `context.Context` to match the actual Cosmos SDK bank keeper signatures.

Applied to files:

  • x/trigger/keeper/keeper_test.go
  • x/trigger/keeper/genesis.go
📚 Learning: 2025-08-15T22:52:24.461Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/keeper.go:74-173
Timestamp: 2025-08-15T22:52:24.461Z
Learning: In the ledger keeper NewKeeper function in x/ledger/keeper/keeper.go, SpicyLemon prefers to allow bankKeeper and registryKeeper dependencies to be nil rather than adding validation that would panic on nil values. The design intentionally supports nil keepers for flexibility.

Applied to files:

  • x/trigger/keeper/keeper_test.go
  • x/trigger/keeper/genesis.go
📚 Learning: 2025-08-15T20:59:09.909Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: In Cosmos SDK v0.50.x, the bank keeper interface methods use `context.Context` rather than `sdk.Context`. The BankKeeper interface should use `context.Context` for methods like SendCoins, HasBalance, GetBalance, etc.

Applied to files:

  • x/trigger/keeper/keeper_test.go
📚 Learning: 2025-08-15T20:39:34.095Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/util.go:68-102
Timestamp: 2025-08-15T20:39:34.095Z
Learning: In the Provenance codebase, functions with "Must" prefix (like MustGetMarkerAddress) can panic and should only be used when you know they won't panic. Instead, use the twin function that returns an error (like GetMarkerAddress) and handle the error properly.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: The `GetLockedCoins` function in `x/hold/keeper/locked_coins.go` must conform to the `banktypes.GetLockedCoinsFn` type, which does not allow returning an error. In cases where `k.GetHoldCoins` encounters an error, using `panic` is considered appropriate due to the severity of such errors.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2024-10-10T23:13:56.722Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2176
File: x/ibchooks/types/params.go:22-26
Timestamp: 2024-10-10T23:13:56.722Z
Learning: In the file `x/ibchooks/types/params.go`, maintain the existing behavior in the `Validate` method without modifying the error messages, even if they may lack additional context.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2024-10-08T18:12:51.936Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/msg_server.go:106-109
Timestamp: 2024-10-08T18:12:51.936Z
Learning: In the `AddScopeDataAccess` method (and similar cases), redeclaring `err` with `:=` is appropriate when previous `err` variables are scoped within `if` statements, as using `=` would result in a compilation error.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: The `CreateAutoResponseToAddrPrefix` function in the quarantine module should have unit tests that assert specific panic messages to ensure the tests fail for the correct reasons, specifically when the `MustLengthPrefix` function triggers a panic due to address length issues.

Applied to files:

  • x/trigger/types/keys.go
🧬 Code graph analysis (5)
x/trigger/keeper/keeper_test.go (2)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/keys.go (2)
  • ModuleName (14-14)
  • GetTriggerKey (86-93)
x/trigger/simulation/decoder.go (1)
x/trigger/types/keys.go (1)
  • TriggerIDLength (23-23)
x/trigger/keeper/event_listener.go (4)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/keys.go (1)
  • GetEventListenerKey (73-78)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
x/trigger/types/errors.go (2)
  • ErrEventNotFound (9-9)
  • ErrTriggerNotFound (8-8)
x/trigger/keeper/genesis.go (3)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/genesis.pb.go (3)
  • GenesisState (27-39)
  • GenesisState (43-43)
  • GenesisState (44-46)
x/trigger/simulation/genesis.go (3)
  • QueueStart (24-24)
  • QueuedTriggers (28-28)
  • Triggers (27-27)
x/trigger/types/keys.go (2)
x/trigger/simulation/genesis.go (1)
  • TriggerID (23-23)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: test-race (02)
  • GitHub Check: test-race (04)
  • GitHub Check: tests (03)
  • GitHub Check: tests (00)
  • GitHub Check: tests (04)
  • GitHub Check: tests (02)
  • GitHub Check: tests (01)
  • GitHub Check: test-race (03)
  • GitHub Check: test-race (01)
  • GitHub Check: test-race (00)
  • GitHub Check: build-linux
  • GitHub Check: Build OSX
  • GitHub Check: Build Linux
  • GitHub Check: Analyze (go)
  • GitHub Check: docker
  • GitHub Check: ghcr-publish
  • GitHub Check: heighliner-docker
  • GitHub Check: golangci-lint
🔇 Additional comments (3)
x/trigger/keeper/keeper_test.go (1)

103-119: Encoding compatibility verified; extend backward compatibility coverage for EventListeners.

Collections.Uint64Key encodes uint64 as 8-byte big-endian integer, matching the old format used in the test. The backward compatibility test for TriggersMap is sound and correctly validates storage layer compatibility.

However, expand coverage to include:

  1. EventListeners: Uses types.EventListenerKeyCodec() (custom codec) rather than standard Uint64Key. This requires explicit backward compatibility testing since it has a different encoding scheme than the standard uint64 codecs.

  2. Queue: While it uses collections.Uint64Key (same encoding as TriggersMap), it's worth adding a backward compatibility test for completeness and confidence.

  3. Genesis logic: This test validates that collections can read old-format data, but the reported import-export simulation failure may stem from issues in genesis export/import logic itself (e.g., in ExportGenesis or InitGenesis), which this test doesn't directly verify.

x/trigger/types/keys.go (1)

57-70: Prefix migration to collections.Prefix and .Bytes() usage look consistent

The move to collections.NewPrefix for all trigger/queue prefixes and the corresponding use of .Bytes() in the key helpers keeps the on-disk key layout stable while making the collections integration type-safe. This should satisfy the earlier concern about collections.Prefix vs []byte and remove compilation issues in the helpers.

Also applies to: 80-83, 90-122

x/trigger/keeper/event_listener.go (1)

66-121: Iteration and counting over hashed event listeners are implemented correctly

Using getEventNameHash plus collections.NewPrefixedTripleRange to scope iteration, then resolving to full triggers via GetTrigger, gives a clean abstraction over the underlying key structure. Skipping entries whose trigger has been deleted while continuing iteration is a reasonable resilience tradeoff and makes IterateEventListeners and GetEventListenerCount safe to use even with minor store inconsistencies.

Also applies to: 143-151

Comment thread x/trigger/keeper/event_listener.go Outdated
Comment thread x/trigger/types/keys.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (3)
x/trigger/keeper/queue.go (2)

23-44: Queue metadata bug: QueuePeek breaks on fresh store—use helper method

This is the same critical issue flagged in the previous review. On a freshly initialized queue, QueueStartIndex is never set by Enqueue, so QueueStartIndex.Get(ctx) at line 29 returns collections.ErrNotFound, causing QueuePeek to return nil even when the queue has items.

The helper method getQueueStartIndex (lines 198–207) already exists and correctly defaults to 0 on ErrNotFound. Replace the direct .Get() call with the helper.

This bug likely contributes to the import-export simulation failure noted in the PR objectives.


72-101: Queue metadata bug: Dequeue panics on fresh store—use helper methods

This is the same critical issue flagged in the previous review. On a freshly initialized queue, both QueueStartIndex.Get(ctx) (line 77) and QueueLength.Get(ctx) (line 82) return collections.ErrNotFound, causing panics instead of defaulting to 0.

Helper methods getQueueStartIndex (lines 198–207) and GetQueueLength (lines 215–224) already exist and correctly default to 0 on ErrNotFound. Replace the direct .Get() calls with these helpers.

This bug likely contributes to the import-export simulation failure noted in the PR objectives.

x/trigger/keeper/event_listener.go (1)

154-161: Use a more appropriate error for invalid event names

This issue was flagged in the previous review. getEventNameHash returns triggertypes.ErrEventNotFound when the normalized event name is empty (line 157). This conflates "invalid input" (validation failure) with "listener not found in store" (query failure), making debugging harder.

Consider introducing ErrInvalidEventName or reusing an existing validation error for empty/invalid event names, reserving ErrEventNotFound for cases where a listener genuinely doesn't exist in the store.

🧹 Nitpick comments (4)
x/trigger/keeper/event_listener.go (2)

38-64: Consider wrapping errors in RemoveEventListener

Based on learnings, error wrapping helps reconstruct what went wrong from error messages. While lines 20, 25, and 31 in SetEventListener wrap errors with context (trigger ID, operation), RemoveEventListener returns bare errors from GetTriggerEventI (line 41), getEventNameHash (line 46), and EventListeners.Remove (line 60).

For consistency and debuggability, consider wrapping these errors with context like fmt.Errorf("failed to remove event listener for trigger %d: %w", trigger.Id, err).

Based on learnings, wrapping errors provides better context for debugging.


133-141: RemoveEventListenerForTriggerID discards removal status

The function calls RemoveEventListener (line 139) which returns (bool, error) but only propagates the error, discarding the boolean that indicates whether a listener was actually removed. If callers need to distinguish "removed successfully" from "listener didn't exist", this information is lost.

If the boolean return is not needed by callers, this is fine. Otherwise, consider either returning (bool, error) from this function or documenting that it treats "not found" as success.

x/trigger/keeper/trigger.go (2)

117-119: Confusing ID management in setTriggerID

The function stores triggerID-1 with the comment "Store as current, so next is triggerID". This off-by-one management is error-prone and conflicts with NewTriggerWithID which stores nextID directly after incrementing (line 103).

For clarity, consider storing the "next ID to use" directly in NextTriggerID and removing the -1 offset, updating getters/setters accordingly. This makes the semantics clearer: "NextTriggerID contains the next ID that will be assigned".


78-88: HasTrigger implementation is inefficient

The function calls TriggersMap.Get (line 80) to check existence, which retrieves and deserializes the entire trigger. Collections typically provide a cheaper Has method that only checks for key existence.

Consider using k.TriggersMap.Has(ctx, id) directly instead of Get followed by error checking. This avoids unnecessary deserialization and improves performance, especially if this method is called frequently.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d1111c and 1122dc2.

📒 Files selected for processing (4)
  • x/trigger/keeper/event_listener.go (2 hunks)
  • x/trigger/keeper/queue.go (2 hunks)
  • x/trigger/keeper/trigger.go (3 hunks)
  • x/trigger/simulation/decoder.go (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: nullpointer0x00
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: A GitHub issue was created to track the migration of parameters for the metadata, marker, name, and msgFees modules as indicated by the TODO comments in the codebase. The issue is linked to PR #1932 and can be found at https://github.com/provenance-io/provenance/issues/1935.
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: The BankKeeper interface methods in Cosmos SDK v0.50.x and Provenance use `context.Context` as the context parameter type, not `sdk.Context`. This applies to methods like SendCoins, HasBalance, GetBalance, SpendableCoin, HasSupply, etc. The expected keeper interfaces should consistently use `context.Context` to match the actual Cosmos SDK bank keeper signatures.
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: The `GetLockedCoins` function in `x/hold/keeper/locked_coins.go` must conform to the `banktypes.GetLockedCoinsFn` type, which does not allow returning an error. In cases where `k.GetHoldCoins` encounters an error, using `panic` is considered appropriate due to the severity of such errors.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2025-08-15T20:39:34.095Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/util.go:68-102
Timestamp: 2025-08-15T20:39:34.095Z
Learning: In the Provenance codebase, functions with "Must" prefix (like MustGetMarkerAddress) can panic and should only be used when you know they won't panic. Instead, use the twin function that returns an error (like GetMarkerAddress) and handle the error properly.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2024-10-10T23:13:56.722Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2176
File: x/ibchooks/types/params.go:22-26
Timestamp: 2024-10-10T23:13:56.722Z
Learning: In the file `x/ibchooks/types/params.go`, maintain the existing behavior in the `Validate` method without modifying the error messages, even if they may lack additional context.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2024-10-08T18:12:51.936Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/msg_server.go:106-109
Timestamp: 2024-10-08T18:12:51.936Z
Learning: In the `AddScopeDataAccess` method (and similar cases), redeclaring `err` with `:=` is appropriate when previous `err` variables are scoped within `if` statements, as using `=` would result in a compilation error.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2025-08-15T20:59:09.909Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: The BankKeeper interface methods in Cosmos SDK v0.50.x and Provenance use `context.Context` as the context parameter type, not `sdk.Context`. This applies to methods like SendCoins, HasBalance, GetBalance, SpendableCoin, HasSupply, etc. The expected keeper interfaces should consistently use `context.Context` to match the actual Cosmos SDK bank keeper signatures.

Applied to files:

  • x/trigger/keeper/queue.go
🧬 Code graph analysis (4)
x/trigger/simulation/decoder.go (2)
x/trigger/types/keys.go (9)
  • NextTriggerIDKey (65-65)
  • GetTriggerIDFromBytes (137-142)
  • QueueStartIndexKey (67-67)
  • GetQueueIndexFromBytes (125-127)
  • QueueLengthKey (69-69)
  • TriggerIDLength (23-23)
  • EventListenerKeyPrefix (61-61)
  • QueueKeyPrefix (63-63)
  • QueueIndexLength (24-24)
x/trigger/types/trigger.pb.go (6)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
  • QueuedTrigger (106-113)
  • QueuedTrigger (117-117)
  • QueuedTrigger (118-120)
x/trigger/keeper/event_listener.go (4)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/keys.go (1)
  • GetEventListenerKey (73-78)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
x/trigger/types/errors.go (2)
  • ErrEventNotFound (9-9)
  • ErrTriggerNotFound (8-8)
x/trigger/keeper/queue.go (3)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • QueuedTrigger (106-113)
  • QueuedTrigger (117-117)
  • QueuedTrigger (118-120)
x/trigger/types/errors.go (1)
  • ErrQueueIndexNotFound (10-10)
x/trigger/keeper/trigger.go (3)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
x/trigger/types/errors.go (1)
  • ErrTriggerNotFound (8-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (22)
  • GitHub Check: go-test-single-db (nondeterminism, ubuntu-latest)
  • GitHub Check: go-test-multi-db (benchmark, goleveldb, ubuntu-latest)
  • GitHub Check: go-test-single-db (benchmark-invariants, ubuntu-latest)
  • GitHub Check: runsim (multi-seed-short, goleveldb, ubuntu-latest)
  • GitHub Check: go-test-multi-db (simple, goleveldb, ubuntu-latest)
  • GitHub Check: test-race (04)
  • GitHub Check: test-race (01)
  • GitHub Check: tests (03)
  • GitHub Check: test-race (03)
  • GitHub Check: test-race (02)
  • GitHub Check: test-race (00)
  • GitHub Check: tests (02)
  • GitHub Check: tests (04)
  • GitHub Check: tests (01)
  • GitHub Check: tests (00)
  • GitHub Check: Build Linux
  • GitHub Check: Build OSX
  • GitHub Check: heighliner-docker
  • GitHub Check: docker
  • GitHub Check: ghcr-publish
  • GitHub Check: Analyze (go)
  • GitHub Check: golangci-lint

Comment thread x/trigger/keeper/trigger.go
Comment thread x/trigger/simulation/decoder.go
Comment thread x/trigger/simulation/decoder.go Outdated
Comment thread x/trigger/simulation/decoder.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (5)
x/trigger/types/keys.go (1)

72-78: Update panic message to accurately describe the eventHash parameter.

The panic message at line 75 still refers to "invalid event name: " when the function parameter is eventHash []byte. This was flagged in a previous review but remains unchanged.

Apply this diff to fix the message:

 func GetEventListenerKey(eventHash []byte, order uint64, id TriggerID) collections.Triple[[]byte, uint64, uint64] {
 	if len(eventHash) == 0 {
-		panic("invalid event name: ")
+		panic("invalid event hash: empty")
 	}
 	return collections.Join3(eventHash, order, id)
 }
x/trigger/simulation/decoder.go (3)

34-37: Replace unsafe slicing with bytes.HasPrefix to prevent panic.

Line 34 uses kvA.Key[:1] which will panic if kvA.Key is empty. The length validation at lines 35-37 executes only after the case matches, so it cannot prevent the panic. This issue was flagged in a previous review but remains unresolved.

Apply this diff:

-	case bytes.Equal(kvA.Key[:1], types.TriggerKeyPrefix.Bytes()):
+	case bytes.HasPrefix(kvA.Key, types.TriggerKeyPrefix.Bytes()):
 		if len(kvA.Key) < 1+types.TriggerIDLength {
 			return fmt.Sprintf("invalid Trigger key length: %d", len(kvA.Key))
 		}

58-67: Replace unsafe slicing with bytes.HasPrefix to prevent panic.

Line 58 uses kvA.Key[:1] which will panic if kvA.Key is empty. The length validation at lines 62-67 executes only after the case matches, so it cannot prevent the panic. This issue was flagged in a previous review but remains unresolved.

Apply this diff:

-	case bytes.Equal(kvA.Key[:1], types.EventListenerKeyPrefix.Bytes()):
+	case bytes.HasPrefix(kvA.Key, types.EventListenerKeyPrefix.Bytes()):
 		triggerIDA := extractTriggerIDFromEventListenerKey(kvA.Key)
 		triggerIDB := extractTriggerIDFromEventListenerKey(kvB.Key)

71-74: Replace unsafe slicing with bytes.HasPrefix to prevent panic.

Line 71 uses kvA.Key[:1] which will panic if kvA.Key is empty. The length validation at lines 72-74 executes only after the case matches, so it cannot prevent the panic. This issue was flagged in a previous review but remains unresolved.

Apply this diff:

-	case bytes.Equal(kvA.Key[:1], types.QueueKeyPrefix.Bytes()):
+	case bytes.HasPrefix(kvA.Key, types.QueueKeyPrefix.Bytes()):
 		if len(kvA.Key) < 1+types.QueueIndexLength {
 			return fmt.Sprintf("invalid Queue key length: %d", len(kvA.Key))
 		}
x/trigger/keeper/trigger.go (1)

20-32: RemoveTrigger silently swallows storage errors, preventing diagnosis of import-export failures.

When TriggersMap.Has (lines 21-23) or TriggersMap.Remove (lines 26-27) fail, the function returns false without propagating the error or logging it. Callers cannot distinguish "trigger not found" from "storage operation failed".

Given that:

  • Other methods in this file return errors (SetTrigger, GetTrigger, IterateTriggers)
  • The PR objectives mention an import-export simulation failure with differing state
  • Silent error swallowing makes storage issues impossible to diagnose

Change the signature to return (bool, error) and propagate errors, or at minimum log errors before returning false.

Apply this diff to propagate errors:

-func (k Keeper) RemoveTrigger(ctx sdk.Context, id types.TriggerID) bool {
+func (k Keeper) RemoveTrigger(ctx sdk.Context, id types.TriggerID) (bool, error) {
 	exists, err := k.TriggersMap.Has(ctx, id)
 	if err != nil {
-		return false
+		return false, err
 	}
 	if exists {
 		if err := k.TriggersMap.Remove(ctx, id); err != nil {
-			return false
+			return false, err
 		}
-		return true
+		return true, nil
 	}
-	return false
+	return false, nil
 }

Note: This change requires updating all callers to handle the new (bool, error) return signature.

🧹 Nitpick comments (2)
x/trigger/keeper/genesis.go (2)

4-4: Prefer fmt.Errorf (with %w) over fmt.Sprintf in panic paths for richer error context

Right now the InitGenesis error paths all do panic(fmt.Sprintf("...: %v", err)), which turns the original error into a plain string and loses its type/causes. Using fmt.Errorf with %w preserves the error value and wraps context, which is generally more useful when inspecting panics.

Consider:

-import (
-    "fmt"
-
-    sdk "github.com/cosmos/cosmos-sdk/types"
-    "github.com/provenance-io/provenance/x/trigger/types"
-)
+import (
+    "fmt"
+
+    sdk "github.com/cosmos/cosmos-sdk/types"
+    "github.com/provenance-io/provenance/x/trigger/types"
+)
@@
-    if err := k.setTriggerID(ctx, data.TriggerId); err != nil {
-        panic(fmt.Sprintf("Failed to set trigger ID: %v", err))
-    }
+    if err := k.setTriggerID(ctx, data.TriggerId); err != nil {
+        panic(fmt.Errorf("failed to set trigger ID: %w", err))
+    }
@@
-    if err := k.setQueueStartIndex(ctx, data.QueueStart); err != nil {
-        panic(fmt.Sprintf("Failed to set queue start index: %v", err))
-    }
+    if err := k.setQueueStartIndex(ctx, data.QueueStart); err != nil {
+        panic(fmt.Errorf("failed to set queue start index: %w", err))
+    }
@@
-    if err := k.setQueueLength(ctx, queueLength); err != nil {
-        panic(fmt.Sprintf("Failed to set queue length: %v", err))
-    }
+    if err := k.setQueueLength(ctx, queueLength); err != nil {
+        panic(fmt.Errorf("failed to set queue length: %w", err))
+    }
@@
-        if err := k.Enqueue(ctx, queuedTrigger); err != nil {
-            panic(fmt.Sprintf("Failed to enqueue trigger: %v", err))
-        }
+        if err := k.Enqueue(ctx, queuedTrigger); err != nil {
+            panic(fmt.Errorf("failed to enqueue trigger from genesis: %w", err))
+        }
@@
-        if err := k.SetTrigger(ctx, trigger); err != nil {
-            panic(fmt.Sprintf("Failed to set trigger %d: %v", trigger.Id, err))
-        }
-        if err := k.SetEventListener(ctx, trigger); err != nil {
-            panic(fmt.Sprintf("Failed to set event listener for trigger %d: %v", trigger.Id, err))
-        }
+        if err := k.SetTrigger(ctx, trigger); err != nil {
+            panic(fmt.Errorf("failed to set trigger %d from genesis: %w", trigger.Id, err))
+        }
+        if err := k.SetEventListener(ctx, trigger); err != nil {
+            panic(fmt.Errorf("failed to set event listener for trigger %d from genesis: %w", trigger.Id, err))
+        }

You could optionally apply the same pattern in ExportGenesis for the getTriggerID / getQueueStartIndex panics for consistency.

Also applies to: 49-52, 55-57, 59-62, 65-67, 71-76


59-62: Double-check QueueLength initialization vs Enqueue behavior to avoid possible double-counting

Here you set:

queueLength := uint64(len(data.QueuedTriggers))
if err := k.setQueueLength(ctx, queueLength); err != nil {
    ...
}

for _, queuedTrigger := range data.QueuedTriggers {
    if err := k.Enqueue(ctx, queuedTrigger); err != nil {
        ...
    }
}

If Enqueue uses QueueLength as the current size and increments it internally (which is a common pattern with queue metadata), pre-seeding QueueLength to len(data.QueuedTriggers) before the loop could leave the stored length at 2 * len(QueuedTriggers) after InitGenesis. If Enqueue now ignores QueueLength or no longer updates it, this is fine—but then the pre-set to len(...) may be unnecessary.

Please confirm the current Enqueue/Dequeue implementation:

  • If Enqueue increments QueueLength, initialize it to 0 (or let Enqueue handle missing as zero) before populating the queue; or
  • If Enqueue no longer uses QueueLength, consider either setting it to uint64(len(data.QueuedTriggers)) after the loop or documenting that it’s a compatibility-only value.

Also applies to: 65-67

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1122dc2 and 2dfaac7.

📒 Files selected for processing (4)
  • x/trigger/keeper/genesis.go (3 hunks)
  • x/trigger/keeper/trigger.go (3 hunks)
  • x/trigger/simulation/decoder.go (2 hunks)
  • x/trigger/types/keys.go (3 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: nullpointer0x00
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: A GitHub issue was created to track the migration of parameters for the metadata, marker, name, and msgFees modules as indicated by the TODO comments in the codebase. The issue is linked to PR #1932 and can be found at https://github.com/provenance-io/provenance/issues/1935.
📚 Learning: 2024-10-08T18:12:51.935Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/types/signer_utils_test.go:478-493
Timestamp: 2024-10-08T18:12:51.935Z
Learning: In Go tests, modifying byte slices directly using arithmetic operations like addition and subtraction is acceptable, even if it causes overflow, as overflow on `uint8` just wraps around. Avoid flagging such usage, and avoid suggesting bitwise operations which can be harder to read and understand.

Applied to files:

  • x/trigger/simulation/decoder.go
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: The `CreateAutoResponseToAddrPrefix` function in the quarantine module should have unit tests that assert specific panic messages to ensure the tests fail for the correct reasons, specifically when the `MustLengthPrefix` function triggers a panic due to address length issues.

Applied to files:

  • x/trigger/simulation/decoder.go
  • x/trigger/types/keys.go
📚 Learning: 2025-08-15T22:52:24.461Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/keeper.go:74-173
Timestamp: 2025-08-15T22:52:24.461Z
Learning: In the ledger keeper NewKeeper function in x/ledger/keeper/keeper.go, SpicyLemon prefers to allow bankKeeper and registryKeeper dependencies to be nil rather than adding validation that would panic on nil values. The design intentionally supports nil keepers for flexibility.

Applied to files:

  • x/trigger/keeper/genesis.go
📚 Learning: 2025-08-15T20:59:09.909Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: The BankKeeper interface methods in Cosmos SDK v0.50.x and Provenance use `context.Context` as the context parameter type, not `sdk.Context`. This applies to methods like SendCoins, HasBalance, GetBalance, SpendableCoin, HasSupply, etc. The expected keeper interfaces should consistently use `context.Context` to match the actual Cosmos SDK bank keeper signatures.

Applied to files:

  • x/trigger/keeper/genesis.go
📚 Learning: 2024-10-10T23:13:56.722Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2176
File: x/ibchooks/types/params.go:22-26
Timestamp: 2024-10-10T23:13:56.722Z
Learning: In the file `x/ibchooks/types/params.go`, maintain the existing behavior in the `Validate` method without modifying the error messages, even if they may lack additional context.

Applied to files:

  • x/trigger/types/keys.go
📚 Learning: 2025-08-15T20:39:34.095Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/util.go:68-102
Timestamp: 2025-08-15T20:39:34.095Z
Learning: In the Provenance codebase, functions with "Must" prefix (like MustGetMarkerAddress) can panic and should only be used when you know they won't panic. Instead, use the twin function that returns an error (like GetMarkerAddress) and handle the error properly.

Applied to files:

  • x/trigger/types/keys.go
🧬 Code graph analysis (4)
x/trigger/simulation/decoder.go (2)
x/trigger/types/keys.go (9)
  • NextTriggerIDKey (65-65)
  • QueueStartIndexKey (67-67)
  • QueueLengthKey (69-69)
  • TriggerKeyPrefix (59-59)
  • TriggerIDLength (23-23)
  • EventListenerKeyPrefix (61-61)
  • QueueKeyPrefix (63-63)
  • QueueIndexLength (24-24)
  • ModuleName (14-14)
x/trigger/types/trigger.pb.go (6)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
  • QueuedTrigger (106-113)
  • QueuedTrigger (117-117)
  • QueuedTrigger (118-120)
x/trigger/keeper/genesis.go (3)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/genesis.pb.go (3)
  • GenesisState (27-39)
  • GenesisState (43-43)
  • GenesisState (44-46)
x/trigger/simulation/genesis.go (3)
  • QueueStart (24-24)
  • QueuedTriggers (28-28)
  • Triggers (27-27)
x/trigger/keeper/trigger.go (4)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/simulation/genesis.go (1)
  • TriggerID (23-23)
x/trigger/types/errors.go (1)
  • ErrTriggerNotFound (8-8)
x/trigger/types/keys.go (2)
x/trigger/simulation/genesis.go (1)
  • TriggerID (23-23)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
  • GitHub Check: runsim (after-import, goleveldb, ubuntu-latest)
  • GitHub Check: runsim (import-export, goleveldb, ubuntu-latest)
  • GitHub Check: go-test-multi-db (benchmark, goleveldb, ubuntu-latest)
  • GitHub Check: runsim (multi-seed-short, goleveldb, ubuntu-latest)
  • GitHub Check: go-test-single-db (benchmark-invariants, ubuntu-latest)
  • GitHub Check: go-test-single-db (nondeterminism, ubuntu-latest)
  • GitHub Check: go-test-multi-db (simple, goleveldb, ubuntu-latest)
  • GitHub Check: tests (02)
  • GitHub Check: tests (04)
  • GitHub Check: test-race (04)
  • GitHub Check: tests (03)
  • GitHub Check: tests (00)
  • GitHub Check: test-race (02)
  • GitHub Check: tests (01)
  • GitHub Check: test-race (01)
  • GitHub Check: test-race (03)
  • GitHub Check: test-race (00)
  • GitHub Check: Build OSX
  • GitHub Check: ghcr-publish
  • GitHub Check: docker
  • GitHub Check: golangci-lint
  • GitHub Check: heighliner-docker
  • GitHub Check: Analyze (go)
🔇 Additional comments (1)
x/trigger/keeper/genesis.go (1)

13-21: ExportGenesis: stricter metadata handling and queue-length sanity check look good

Panicking on getTriggerID / getQueueStartIndex errors and validating QueueLength against the actual queue size avoids silently exporting an inconsistent genesis and adds a useful warning signal without changing behavior.

Also applies to: 33-38

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
x/trigger/keeper/trigger.go (1)

20-31: RemoveTrigger still collapses storage errors into false (same concern as earlier review)

TriggersMap.Has and TriggersMap.Remove errors currently cause RemoveTrigger to return false, which is indistinguishable from “trigger didn’t exist” and hides real storage failures—this was already flagged in a prior review and remains unresolved.

Consider changing the signature to (bool, error) and returning false, err on any storage error, with true, nil when a trigger is successfully removed and false, nil when it wasn’t present. At minimum, log the errors so they’re visible during debugging (especially for the import‑export sim).

🧹 Nitpick comments (1)
x/trigger/keeper/trigger.go (1)

78-88: HasTrigger should prefer Has over Get and align on types.TriggerID

Functionally this works, but you can simplify and avoid decoding values by using TriggersMap.Has:

-func (k Keeper) HasTrigger(ctx sdk.Context, id uint64) (bool, error) {
-	_, err := k.TriggersMap.Get(ctx, id)
-	if errors.Is(err, collections.ErrNotFound) {
-		return false, nil
-	}
-	if err != nil {
-		return false, err
-	}
-	return true, nil
-}
+func (k Keeper) HasTrigger(ctx sdk.Context, id types.TriggerID) (bool, error) {
+	return k.TriggersMap.Has(ctx, id)
+}

This is a small consistency and efficiency improvement, and it keeps the public API using types.TriggerID like the other methods.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2dfaac7 and fad8543.

📒 Files selected for processing (1)
  • x/trigger/keeper/trigger.go (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: nullpointer0x00
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: A GitHub issue was created to track the migration of parameters for the metadata, marker, name, and msgFees modules as indicated by the TODO comments in the codebase. The issue is linked to PR #1932 and can be found at https://github.com/provenance-io/provenance/issues/1935.
🧬 Code graph analysis (1)
x/trigger/keeper/trigger.go (5)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/simulation/genesis.go (1)
  • TriggerID (23-23)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
x/trigger/types/errors.go (1)
  • ErrTriggerNotFound (8-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: test-race (03)
  • GitHub Check: test-race (01)
  • GitHub Check: test-race (00)
  • GitHub Check: test-race (04)
  • GitHub Check: tests (01)
  • GitHub Check: test-race (02)
  • GitHub Check: tests (02)
  • GitHub Check: tests (03)
  • GitHub Check: tests (04)
  • GitHub Check: tests (00)
  • GitHub Check: build-linux
  • GitHub Check: Build Linux
  • GitHub Check: Build OSX
  • GitHub Check: ghcr-publish
  • GitHub Check: heighliner-docker
  • GitHub Check: docker
  • GitHub Check: Analyze (go)
  • GitHub Check: golangci-lint
🔇 Additional comments (3)
x/trigger/keeper/trigger.go (3)

3-17: SetTrigger collections usage looks correct; ensure all callers handle the new error return

Switching SetTrigger to TriggersMap.Set with an error return is appropriate and consistent with the collections API. Please double‑check that all call sites now check/propagate this error so failures in the underlying store don’t get silently ignored.


35-44: GetTrigger’s error mapping to ErrTriggerNotFound is appropriate

Using TriggersMap.Get and mapping collections.ErrNotFound to types.ErrTriggerNotFound while propagating all other errors preserves the module‑specific error contract and surfaces storage issues cleanly. This looks correct and aligned with collections semantics.


47-66: IterateTriggers correctly uses the collections iterator

Using TriggersMap.Iterate(ctx, nil) with iterator.KeyValue() and deferring iterator.Close() is the expected pattern for a full‑range scan, and the callback error/stop handling looks correct. No changes needed here.

Comment thread x/trigger/keeper/trigger.go
Comment thread x/trigger/keeper/trigger.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
x/trigger/keeper/trigger_test.go (1)

115-116: Assert on NewTriggerWithID errors instead of discarding

NewTriggerWithID returns (types.Trigger, error), but the test uses trigger, _ := ..., so any internal error is silently ignored and the equality assertion may still run on a zero trigger.

Capture and assert on the error before comparing:

-			trigger, _ := s.app.TriggerKeeper.NewTriggerWithID(s.ctx, tc.expected.Owner, tc.expected.Event, tc.expected.Actions)
-			s.Equal(tc.expected, trigger, "should have correct trigger from NewTrigger")
+			trigger, err := s.app.TriggerKeeper.NewTriggerWithID(s.ctx, tc.expected.Owner, tc.expected.Event, tc.expected.Actions)
+			s.Require().NoError(err, "NewTriggerWithID should not return an error")
+			s.Equal(tc.expected, trigger, "should have correct trigger from NewTrigger")

Using Require here aligns with suite preferences for failing fast on setup errors. Based on learnings, SpicyLemon prefers this style.

x/trigger/keeper/trigger.go (1)

20-32: RemoveTrigger still swallows storage errors; prefer (bool, error)

RemoveTrigger returns false on any error from TriggersMap.Has or TriggersMap.Remove, making it indistinguishable from "ID does not exist" and hiding underlying store issues (e.g., during import/export or I/O failures).

Consider changing the signature and propagating errors:

-func (k Keeper) RemoveTrigger(ctx sdk.Context, id types.TriggerID) bool {
-	exists, err := k.TriggersMap.Has(ctx, id)
-	if err != nil {
-		return false
-	}
-	if exists {
-		if err := k.TriggersMap.Remove(ctx, id); err != nil {
-			return false
-		}
-		return true
-	}
-	return false
-}
+func (k Keeper) RemoveTrigger(ctx sdk.Context, id types.TriggerID) (bool, error) {
+	exists, err := k.TriggersMap.Has(ctx, id)
+	if err != nil {
+		return false, err
+	}
+	if !exists {
+		return false, nil
+	}
+	if err := k.TriggersMap.Remove(ctx, id); err != nil {
+		return false, err
+	}
+	return true, nil
+}

Callers (including tests) would then assert on both the boolean and the error. This keeps RemoveTrigger consistent with SetTrigger/GetTrigger in terms of error visibility.

🧹 Nitpick comments (2)
x/trigger/keeper/trigger.go (2)

78-88: Use Has and types.TriggerID in HasTrigger for clarity and efficiency

HasTrigger currently does a full Get/decode just to check existence and takes a raw uint64:

func (k Keeper) HasTrigger(ctx sdk.Context, id uint64) (bool, error) {
	_, err := k.TriggersMap.Get(ctx, id)
	...
}

Consider:

  • Using TriggersMap.Has instead of Get to avoid unnecessary decoding.
  • Accepting id types.TriggerID for consistency with other methods.

For example:

func (k Keeper) HasTrigger(ctx sdk.Context, id types.TriggerID) (bool, error) {
	return k.TriggersMap.Has(ctx, id)
}

This keeps the method lightweight and aligned with the rest of the keeper API.


90-137: Implement recommended InitGenesis and round‑trip testing to ensure consistent NextTriggerID key presence

Verification confirms the concern: ExportGenesis calls getTriggerID(ctx), which returns 1 without creating the NextTriggerID key when it's absent; InitGenesis calls setTriggerID(ctx, data.TriggerId), which always writes the key. On a fresh chain that never allocated a trigger, exporting yields TriggerId: 1 with no key in the store, but importing re‑creates the key—breaking the invariant that import-export should preserve store state byte-for-byte.

The Validate() function (x/trigger/types/genesis.go) prevents TriggerId: 0 but does not guard against this key-presence inconsistency. No round-trip tests currently exist.

Recommended:

  • Ensure InitGenesis always calls setTriggerID(ctx, data.TriggerId) before processing triggers (it already does) to guarantee the key is present after import.
  • Add a keeper-level unit test:
    • Create a state with empty triggers, export it, then immediately import it back
    • Assert that NextTriggerID key is present and matches the exported value both before and after the round-trip
    • Repeat with a non-empty trigger set to confirm ID consistency across the cycle

This will prevent subtle differences in key presence from silently breaking the import-export simulation.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fad8543 and bc1877c.

📒 Files selected for processing (2)
  • x/trigger/keeper/trigger.go (3 hunks)
  • x/trigger/keeper/trigger_test.go (2 hunks)
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: nullpointer0x00
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: A GitHub issue was created to track the migration of parameters for the metadata, marker, name, and msgFees modules as indicated by the TODO comments in the codebase. The issue is linked to PR #1932 and can be found at https://github.com/provenance-io/provenance/issues/1935.
📚 Learning: 2024-10-08T18:12:51.935Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 1993
File: x/marker/client/cli/cli_test.go:1090-1090
Timestamp: 2024-10-08T18:12:51.935Z
Learning: SpicyLemon prefers allowing tests to fail naturally if the setup is incorrect rather than explicitly checking and handling such errors within the test functions. They also recommend using `s.Require().NotNil(...)` instead of `s.T().Fatal(...)` for assertions in test functions.

Applied to files:

  • x/trigger/keeper/trigger_test.go
📚 Learning: 2025-08-15T20:57:04.455Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/entries_test.go:26-31
Timestamp: 2025-08-15T20:57:04.455Z
Learning: SpicyLemon prefers string-based error assertions in tests using empty string to indicate "no error expected" and non-empty string for expected error messages. Use assertions.AssertErrorValue instead of errors.Is() comparisons because errors.Is() with s.Require().True() provides poor failure messages that only say "Should be true" without showing expected vs actual error content.

Applied to files:

  • x/trigger/keeper/trigger_test.go
📚 Learning: 2025-08-15T20:01:55.078Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: app/upgrades_test.go:0-0
Timestamp: 2025-08-15T20:01:55.078Z
Learning: SpicyLemon prefers upgrade tests to fail when expected data is missing rather than skip, using s.Require().NoError() and s.Require().NotEmpty() to enforce hard requirements for upgrade data files in test scenarios.

Applied to files:

  • x/trigger/keeper/trigger_test.go
📚 Learning: 2024-10-10T23:19:54.565Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2176
File: app/upgrades_test.go:610-610
Timestamp: 2024-10-10T23:19:54.565Z
Learning: In `app/upgrades_test.go`, we prefer to keep TODO comments (like `TODO[viridian]`) to handle code deletions manually, and do not want to implement automated processes for code removal, especially for unit tests.

Applied to files:

  • x/trigger/keeper/trigger_test.go
📚 Learning: 2024-10-02T03:37:18.486Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/msg_server_test.go:1225-1228
Timestamp: 2024-10-02T03:37:18.486Z
Learning: When test cases are run using `s.T().Run` instead of `s.Run`, use `require` with `t` instead of `s.Require()`, to ensure that assertions apply to the correct test instance.

Applied to files:

  • x/trigger/keeper/trigger_test.go
📚 Learning: 2024-10-02T03:42:47.070Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/query_server_test.go:940-945
Timestamp: 2024-10-02T03:42:47.070Z
Learning: In the `TestScopeQuery` function, the conditional checks following the equality assertion are designed to help identify differences when `tc.expResp` and `actResp` are not equal and both are not `nil`. If they are equal, or either is `nil`, the failure message from the equality assertion suffices, and further checks are not needed.

Applied to files:

  • x/trigger/keeper/trigger_test.go
📚 Learning: 2024-10-08T18:12:51.935Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/account_data_test.go:11-11
Timestamp: 2024-10-08T18:12:51.935Z
Learning: Do not raise issues about the `keeper` package still being in use after import removal in test files when there are no unresolved references.

Applied to files:

  • x/trigger/keeper/trigger_test.go
📚 Learning: 2025-08-15T20:59:09.909Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: The BankKeeper interface methods in Cosmos SDK v0.50.x and Provenance use `context.Context` as the context parameter type, not `sdk.Context`. This applies to methods like SendCoins, HasBalance, GetBalance, SpendableCoin, HasSupply, etc. The expected keeper interfaces should consistently use `context.Context` to match the actual Cosmos SDK bank keeper signatures.

Applied to files:

  • x/trigger/keeper/trigger.go
🧬 Code graph analysis (2)
x/trigger/keeper/trigger_test.go (3)
x/trigger/keeper/keeper_test.go (1)
  • KeeperTestSuite (27-41)
x/trigger/types/trigger.pb.go (3)
  • BlockHeightEvent (170-173)
  • BlockHeightEvent (177-177)
  • BlockHeightEvent (178-180)
x/trigger/types/tx.pb.go (3)
  • MsgDestroyTriggerRequest (145-150)
  • MsgDestroyTriggerRequest (154-154)
  • MsgDestroyTriggerRequest (155-157)
x/trigger/keeper/trigger.go (4)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
x/trigger/types/errors.go (1)
  • ErrTriggerNotFound (8-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (22)
  • GitHub Check: runsim (import-export, goleveldb, ubuntu-latest)
  • GitHub Check: go-test-multi-db (simple, goleveldb, ubuntu-latest)
  • GitHub Check: runsim (after-import, goleveldb, ubuntu-latest)
  • GitHub Check: runsim (multi-seed-short, goleveldb, ubuntu-latest)
  • GitHub Check: go-test-multi-db (benchmark, goleveldb, ubuntu-latest)
  • GitHub Check: tests (04)
  • GitHub Check: tests (02)
  • GitHub Check: tests (01)
  • GitHub Check: tests (00)
  • GitHub Check: tests (03)
  • GitHub Check: test-race (03)
  • GitHub Check: test-race (04)
  • GitHub Check: test-race (00)
  • GitHub Check: test-race (02)
  • GitHub Check: test-race (01)
  • GitHub Check: Build Linux
  • GitHub Check: Build OSX
  • GitHub Check: ghcr-publish
  • GitHub Check: heighliner-docker
  • GitHub Check: docker
  • GitHub Check: golangci-lint
  • GitHub Check: Analyze (go)
🔇 Additional comments (3)
x/trigger/keeper/trigger_test.go (1)

208-246: New collections round‑trip test looks good

TestTriggerCollectionsOperations exercises SetTrigger, GetTrigger, HasTrigger, and RemoveTrigger end‑to‑end against the collections-backed keeper and uses Require for critical checks, which is a solid addition for the new API.

x/trigger/keeper/trigger.go (2)

14-17: SetTrigger error surface via collections is appropriate

Switching SetTrigger to delegate to TriggersMap.Set and return an error is the right direction for propagating storage failures to callers.


35-76: Collections-based GetTrigger/IterateTriggers/GetAllTriggers look correct

  • GetTrigger correctly maps collections.ErrNotFound to types.ErrTriggerNotFound while propagating other errors.
  • IterateTriggers uses TriggersMap.Iterate and passes decoded values into the handler, closing the iterator via defer.
  • GetAllTriggers simply accumulates via IterateTriggers, which is straightforward.

No issues spotted here.

@nagarajdivine nagarajdivine force-pushed the feat/trigger-module-collections branch from bc1877c to fd735fa Compare December 12, 2025 18:26

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
x/trigger/keeper/query_server.go (1)

21-35: Return status.Error(codes.NotFound, ...) directly for consistency in QueryServer error handling.

The code mixes error handling patterns: line 23 uses status.Error(codes.InvalidArgument, ...) with explicit gRPC codes, but line 30 returns types.ErrTriggerNotFound directly. For QueryServer methods, explicitly use status.Error(codes.NotFound, "trigger not found") to ensure consistent and predictable gRPC status codes across all error paths.

♻️ Duplicate comments (7)
x/trigger/simulation/decoder.go (1)

34-36: Fix unsafe kvA.Key[:1] slicing (panic on empty key)

Even with later length checks, the slice happens first and can panic if kvA.Key is empty. Use bytes.HasPrefix.

-	case bytes.Equal(kvA.Key[:1], types.TriggerKeyPrefix.Bytes()):
+	case bytes.HasPrefix(kvA.Key, types.TriggerKeyPrefix.Bytes()):
@@
-	case bytes.Equal(kvA.Key[:1], types.EventListenerKeyPrefix.Bytes()):
+	case bytes.HasPrefix(kvA.Key, types.EventListenerKeyPrefix.Bytes()):
@@
-	case bytes.Equal(kvA.Key[:1], types.QueueKeyPrefix.Bytes()):
+	case bytes.HasPrefix(kvA.Key, types.QueueKeyPrefix.Bytes()):

Also applies to: 58-59, 71-74

x/trigger/keeper/event_listener.go (2)

153-160: Don’t use ErrEventNotFound for invalid event name

Empty/whitespace input is a caller bug, not “not found”. Return a distinct validation error (or add one in x/trigger/types/errors.go) and wrap it here.


133-141: Fix misleading doc: function removes one listener, not “all”

Update the comment to match behavior (and Go doc convention):

-// RemoveEventListenerForTriggerID removes all event listeners for a specific trigger
+// RemoveEventListenerForTriggerID removes the event listener for the specified trigger.
x/trigger/keeper/trigger.go (2)

20-32: RemoveTrigger still swallows storage errors (hard to debug failures)

At minimum log; ideally return (bool, error) like other methods.


113-137: Guard against TriggerID == 0 to keep ID semantics consistent

getTriggerID treats missing as 1, but setTriggerID will happily persist 0. If TriggerId in genesis can be 0, NewTriggerWithID can produce Id: 0.

Suggested patch:

 func (k Keeper) setTriggerID(ctx sdk.Context, triggerID types.TriggerID) error {
+	if triggerID == 0 {
+		return errors.New("triggerID must be > 0")
+	}
 	return k.NextTriggerID.Set(ctx, triggerID)
 }
x/trigger/keeper/queue.go (1)

22-44: Critical: QueuePeek/Dequeue bypass “missing => 0” helpers; fresh queue can peek nil / dequeue panic

Use getQueueStartIndex + GetQueueLength, and initialize QueueStartIndex on first enqueue.

 func (k Keeper) QueuePeek(ctx sdk.Context) *types.QueuedTrigger {
 	isEmpty := k.QueueIsEmpty(ctx)
@@
-	startIndex, err := k.QueueStartIndex.Get(ctx)
-	if err != nil {
-		if errors.Is(err, collections.ErrNotFound) {
-			return nil
-		}
-		return nil
-	}
+	startIndex, err := k.getQueueStartIndex(ctx)
+	if err != nil {
+		return nil
+	}
@@
 func (k Keeper) Enqueue(ctx sdk.Context, item types.QueuedTrigger) error {
-	startIndex, err := k.QueueStartIndex.Get(ctx)
-	if err != nil && !errors.Is(err, collections.ErrNotFound) {
-		return err
-	}
-	if errors.Is(err, collections.ErrNotFound) {
-		startIndex = 0
-	}
+	startIndex, err := k.getQueueStartIndex(ctx)
+	if err != nil {
+		return err
+	}
+	// Ensure metadata exists once we have items.
+	if err := k.QueueStartIndex.Set(ctx, startIndex); err != nil {
+		return err
+	}
@@
 func (k Keeper) Dequeue(ctx sdk.Context) {
@@
-	startIndex, err := k.QueueStartIndex.Get(ctx)
-	if err != nil {
-		panic(err)
-	}
-
-	length, err := k.QueueLength.Get(ctx)
-	if err != nil {
-		panic(err)
-	}
+	startIndex, err := k.getQueueStartIndex(ctx)
+	if err != nil {
+		panic(err)
+	}
+	length, err := k.GetQueueLength(ctx)
+	if err != nil {
+		panic(err)
+	}

Also applies to: 47-69, 71-100

x/trigger/types/keys.go (1)

76-82: Fix panic text + stale comment: this function takes eventHash, not “event name”.
The panic message (Line 79) and the doc comment (Line 76) don’t reflect the parameter anymore.

-// GetEventListenerKey converts an event name, order, and trigger ID into an event registry key format.
+// GetEventListenerKey converts an event hash, order, and trigger ID into an event registry key format.
 func GetEventListenerKey(eventHash []byte, order uint64, id TriggerID) collections.Triple[[]byte, uint64, uint64] {
 	if len(eventHash) == 0 {
-		panic("invalid event name: ")
+		panic("invalid event hash: empty")
 	}
 	return collections.Join3(eventHash, order, id)
 }
🧹 Nitpick comments (7)
x/trigger/types/codec.go (1)

47-111: The JSON methods are never called in the codebase; verify if this is a real concern before implementing.

EncodeJSON and DecodeJSON currently return errors and are never invoked anywhere. If these methods are only used as part of the EventListenerKeyCodec for backward compatibility with the old KVStore key format, and generic collections tooling doesn't attempt to call JSON methods on all KeyCodecs, then implementing them may not be necessary. Consider either removing these methods if they're not required by the KeyCodec interface, documenting why they're intentionally unimplemented, or confirming that generic debug/CLI/export helpers actually depend on them before implementing.

x/trigger/simulation/decoder.go (1)

58-70: EventListener key decoding looks brittle (fixed offsets)

extractTriggerIDFromEventListenerKey assumes a fixed layout (key[41:49]). With collections, the []byte element may be length-prefixed, so offsets may change. Consider decoding via the same codec used for the key (e.g., types.EventListenerKeyCodec()), or at least parse the last 8 bytes for triggerID if the encoding guarantees that.

Also applies to: 99-108

x/trigger/keeper/genesis.go (1)

49-77: Prefer panic(fmt.Errorf(...)) over panic(fmt.Sprintf(...))

Panicking with a formatted string drops the wrapped error. This preserves %w context:

-	panic(fmt.Sprintf("Failed to set trigger ID: %v", err))
+	panic(fmt.Errorf("failed to set trigger ID: %w", err))
x/trigger/keeper/trigger.go (1)

78-88: Use TriggersMap.Has for HasTrigger (avoid Get+decode)

 func (k Keeper) HasTrigger(ctx sdk.Context, id uint64) (bool, error) {
-	_, err := k.TriggersMap.Get(ctx, id)
-	if errors.Is(err, collections.ErrNotFound) {
-		return false, nil
-	}
-	if err != nil {
-		return false, err
-	}
-	return true, nil
+	return k.TriggersMap.Has(ctx, id)
 }
x/trigger/types/keys.go (3)

57-70: Minor doc/name nit: “QueueLengthPrefix” comment doesn’t match QueueLengthKey.
Line 68’s comment says QueueLengthPrefix but the identifier is QueueLengthKey, which is a bit confusing for future readers.


84-126: Avoid append(prefix.Bytes(), ...) patterns if Bytes() can share backing storage.
If Prefix.Bytes() returns a slice backed by shared storage, append can (in edge cases) mutate data you expect to be immutable. Safer to build into a fresh buffer for GetEventListenerPrefix, GetTriggerKey, GetQueueKey, etc.

 func GetEventListenerPrefix(eventHash []byte) []byte {
-	prefix := EventListenerKeyPrefix.Bytes()
-	return append(prefix, eventHash...)
+	prefix := EventListenerKeyPrefix.Bytes()
+	out := make([]byte, 0, len(prefix)+len(eventHash))
+	out = append(out, prefix...)
+	out = append(out, eventHash...)
+	return out
 }

128-149: Double-check “return 0 on short slice” is a safe sentinel.
Returning 0 on malformed/truncated keys can silently collapse different invalid inputs into a valid-looking ID/index (especially if 0 is meaningful anywhere). If this is only for defensive decoding/logging paths, it’s fine; otherwise consider returning (val, ok) or an error.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc1877c and fd735fa.

📒 Files selected for processing (21)
  • .changelog/unreleased/features/2499-trigger-module-collections.md (1 hunks)
  • app/app.go (1 hunks)
  • x/trigger/keeper/event_detector_test.go (1 hunks)
  • x/trigger/keeper/event_listener.go (2 hunks)
  • x/trigger/keeper/event_listener_test.go (1 hunks)
  • x/trigger/keeper/genesis.go (3 hunks)
  • x/trigger/keeper/keeper.go (2 hunks)
  • x/trigger/keeper/keeper_test.go (2 hunks)
  • x/trigger/keeper/migrations_v2.go (1 hunks)
  • x/trigger/keeper/msg_server.go (3 hunks)
  • x/trigger/keeper/query_server.go (3 hunks)
  • x/trigger/keeper/queue.go (2 hunks)
  • x/trigger/keeper/queue_test.go (1 hunks)
  • x/trigger/keeper/trigger.go (3 hunks)
  • x/trigger/keeper/trigger_registry.go (1 hunks)
  • x/trigger/keeper/trigger_test.go (2 hunks)
  • x/trigger/simulation/decoder.go (2 hunks)
  • x/trigger/simulation/genesis.go (1 hunks)
  • x/trigger/types/codec.go (2 hunks)
  • x/trigger/types/keys.go (3 hunks)
  • x/trigger/types/keys_test.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • x/trigger/simulation/genesis.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • x/trigger/keeper/msg_server.go
  • .changelog/unreleased/features/2499-trigger-module-collections.md
  • x/trigger/keeper/trigger_registry.go
  • x/trigger/keeper/trigger_test.go
  • x/trigger/keeper/queue_test.go
🧰 Additional context used
🧠 Learnings (20)
📓 Common learnings
Learnt from: nullpointer0x00
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: A GitHub issue was created to track the migration of parameters for the metadata, marker, name, and msgFees modules as indicated by the TODO comments in the codebase. The issue is linked to PR #1932 and can be found at https://github.com/provenance-io/provenance/issues/1935.
📚 Learning: 2025-08-15T20:34:01.110Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/codec.go:9-10
Timestamp: 2025-08-15T20:34:01.110Z
Learning: In Go Cosmos SDK projects, sdk.Msg interface embeds proto.Message interface, making it valid to copy elements from []sdk.Msg to []proto.Message even though the slice types are different. The pattern of declaring AllRequestMsgs as []sdk.Msg for type assertions while copying to []proto.Message for RegisterImplementations is a common and correct pattern.

Applied to files:

  • x/trigger/types/codec.go
📚 Learning: 2025-08-15T20:34:01.110Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/codec.go:9-10
Timestamp: 2025-08-15T20:34:01.110Z
Learning: The pattern of declaring AllRequestMsgs as []sdk.Msg and then copying to []proto.Message for RegisterImplementations is a standard, correct pattern used throughout the Provenance codebase. This works because sdk.Msg interface embeds proto.Message interface, making the copy operation valid. This pattern appears consistently across 15+ modules and compiles without errors.

Applied to files:

  • x/trigger/types/codec.go
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 1993
File: x/marker/client/cli/cli_test.go:1090-1090
Timestamp: 2024-06-10T19:25:28.209Z
Learning: SpicyLemon prefers allowing tests to fail naturally if the setup is incorrect rather than explicitly checking and handling such errors within the test functions. They also recommend using `s.Require().NotNil(...)` instead of `s.T().Fatal(...)` for assertions in test functions.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
  • x/trigger/keeper/event_detector_test.go
📚 Learning: 2024-10-10T23:19:54.565Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2176
File: app/upgrades_test.go:610-610
Timestamp: 2024-10-10T23:19:54.565Z
Learning: In `app/upgrades_test.go`, we prefer to keep TODO comments (like `TODO[viridian]`) to handle code deletions manually, and do not want to implement automated processes for code removal, especially for unit tests.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
📚 Learning: 2025-08-15T20:57:04.455Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/entries_test.go:26-31
Timestamp: 2025-08-15T20:57:04.455Z
Learning: SpicyLemon prefers string-based error assertions in tests using empty string to indicate "no error expected" and non-empty string for expected error messages. Use assertions.AssertErrorValue instead of errors.Is() comparisons because errors.Is() with s.Require().True() provides poor failure messages that only say "Should be true" without showing expected vs actual error content.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
📚 Learning: 2025-08-15T22:52:24.461Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/keeper.go:74-173
Timestamp: 2025-08-15T22:52:24.461Z
Learning: In the ledger keeper NewKeeper function in x/ledger/keeper/keeper.go, SpicyLemon prefers to allow bankKeeper and registryKeeper dependencies to be nil rather than adding validation that would panic on nil values. The design intentionally supports nil keepers for flexibility.

Applied to files:

  • x/trigger/keeper/genesis.go
  • x/trigger/keeper/keeper_test.go
  • app/app.go
  • x/trigger/keeper/keeper.go
📚 Learning: 2025-08-15T20:59:09.909Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: The BankKeeper interface methods in Cosmos SDK v0.50.x and Provenance use `context.Context` as the context parameter type, not `sdk.Context`. This applies to methods like SendCoins, HasBalance, GetBalance, SpendableCoin, HasSupply, etc. The expected keeper interfaces should consistently use `context.Context` to match the actual Cosmos SDK bank keeper signatures.

Applied to files:

  • x/trigger/keeper/genesis.go
  • x/trigger/keeper/keeper_test.go
  • app/app.go
  • x/trigger/keeper/trigger.go
  • x/trigger/keeper/queue.go
  • x/trigger/keeper/keeper.go
📚 Learning: 2024-10-02T18:03:10.008Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/types/signer_utils_test.go:478-493
Timestamp: 2024-10-02T18:03:10.008Z
Learning: In Go tests, modifying byte slices directly using arithmetic operations like addition and subtraction is acceptable, even if it causes overflow, as overflow on `uint8` just wraps around. Avoid flagging such usage, and avoid suggesting bitwise operations which can be harder to read and understand.

Applied to files:

  • x/trigger/simulation/decoder.go
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: The `CreateAutoResponseToAddrPrefix` function in the quarantine module should have unit tests that assert specific panic messages to ensure the tests fail for the correct reasons, specifically when the `MustLengthPrefix` function triggers a panic due to address length issues.

Applied to files:

  • x/trigger/simulation/decoder.go
  • x/trigger/types/keys.go
📚 Learning: 2024-10-02T03:06:21.997Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/migrations_v4_test.go:619-630
Timestamp: 2024-10-02T03:06:21.997Z
Learning: When unit tests exist for a key generation function, it's preferable to reuse that function in tests instead of manually constructing byte slices.

Applied to files:

  • x/trigger/types/keys_test.go
📚 Learning: 2024-10-02T02:06:21.664Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/migrations_v4.go:57-80
Timestamp: 2024-10-02T02:06:21.664Z
Learning: The `V3WriteNewScope` method in `x/metadata/keeper/migrations_v4.go` must exist until after the migration has been executed and will be deleted as part of the `viridian` cleanup.

Applied to files:

  • x/trigger/keeper/migrations_v2.go
  • x/trigger/keeper/keeper.go
📚 Learning: 2025-08-15T20:39:34.095Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/util.go:68-102
Timestamp: 2025-08-15T20:39:34.095Z
Learning: In the Provenance codebase, functions with "Must" prefix (like MustGetMarkerAddress) can panic and should only be used when you know they won't panic. Instead, use the twin function that returns an error (like GetMarkerAddress) and handle the error properly.

Applied to files:

  • x/trigger/keeper/event_listener.go
  • x/trigger/types/keys.go
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: The `GetLockedCoins` function in `x/hold/keeper/locked_coins.go` must conform to the `banktypes.GetLockedCoinsFn` type, which does not allow returning an error. In cases where `k.GetHoldCoins` encounters an error, using `panic` is considered appropriate due to the severity of such errors.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2024-10-10T23:13:56.722Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2176
File: x/ibchooks/types/params.go:22-26
Timestamp: 2024-10-10T23:13:56.722Z
Learning: In the file `x/ibchooks/types/params.go`, maintain the existing behavior in the `Validate` method without modifying the error messages, even if they may lack additional context.

Applied to files:

  • x/trigger/keeper/event_listener.go
  • x/trigger/types/keys.go
📚 Learning: 2024-10-08T18:12:51.936Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/msg_server.go:106-109
Timestamp: 2024-10-08T18:12:51.936Z
Learning: In the `AddScopeDataAccess` method (and similar cases), redeclaring `err` with `:=` is appropriate when previous `err` variables are scoped within `if` statements, as using `=` would result in a compilation error.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2025-08-15T20:59:09.909Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: In Cosmos SDK v0.50.x, the bank keeper interface methods use `context.Context` rather than `sdk.Context`. The BankKeeper interface should use `context.Context` for methods like SendCoins, HasBalance, GetBalance, etc.

Applied to files:

  • x/trigger/keeper/keeper_test.go
📚 Learning: 2024-10-02T01:43:01.360Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/marker/types/send_restrictions.go:11-11
Timestamp: 2024-10-02T01:43:01.360Z
Learning: In `x/marker/types/send_restrictions.go`, the `transferAgentKey` is only used for storing data in ephemeral contexts, so changing its value does not impact backward compatibility.

Applied to files:

  • app/app.go
📚 Learning: 2025-08-15T20:59:51.949Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/keys.go:3-12
Timestamp: 2025-08-15T20:59:51.949Z
Learning: In Cosmos SDK, key prefixes only need to be unique within a given module's store, not across the entire application. Each module operates within its own isolated key-value store namespace, so the same byte prefixes can be safely reused across different modules without causing collisions.

Applied to files:

  • x/trigger/keeper/query_server.go
📚 Learning: 2025-08-15T20:19:31.726Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/simulation/decoder.go:8-14
Timestamp: 2025-08-15T20:19:31.726Z
Learning: The asset module in Provenance doesn't use collections-based storage like ledger/registry modules. Instead, it delegates storage to the NFT module, so a collections-based store decoder isn't applicable here. The module should use its own types.StoreKey rather than exchange.StoreKey in RegisterStoreDecoder.

Applied to files:

  • x/trigger/keeper/keeper.go
🧬 Code graph analysis (6)
x/trigger/keeper/event_listener_test.go (4)
x/trigger/keeper/keeper_test.go (1)
  • KeeperTestSuite (27-41)
x/trigger/types/trigger.pb.go (9)
  • BlockHeightEvent (170-173)
  • BlockHeightEvent (177-177)
  • BlockHeightEvent (178-180)
  • BlockTimeEvent (216-219)
  • BlockTimeEvent (223-223)
  • BlockTimeEvent (224-226)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/trigger.go (1)
  • NewTrigger (149-156)
x/trigger/types/errors.go (1)
  • ErrEventNotFound (9-9)
x/trigger/keeper/genesis.go (3)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/genesis.pb.go (3)
  • GenesisState (27-39)
  • GenesisState (43-43)
  • GenesisState (44-46)
x/trigger/simulation/genesis.go (3)
  • QueueStart (24-24)
  • QueuedTriggers (28-28)
  • Triggers (27-27)
x/trigger/types/keys_test.go (1)
x/trigger/types/keys.go (3)
  • GetEventNameBytes (159-166)
  • GetEventListenerKey (77-82)
  • GetEventListenerPrefix (84-87)
app/app.go (2)
x/trigger/keeper/keeper.go (1)
  • NewKeeper (30-59)
x/trigger/types/keys.go (1)
  • StoreKey (17-17)
x/trigger/keeper/queue.go (4)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • QueuedTrigger (106-113)
  • QueuedTrigger (117-117)
  • QueuedTrigger (118-120)
x/exchange/keeper/export_test.go (1)
  • Iterate (85-85)
x/trigger/types/errors.go (1)
  • ErrQueueIndexNotFound (10-10)
x/trigger/types/keys.go (2)
x/trigger/simulation/genesis.go (1)
  • TriggerID (23-23)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: tests (03)
  • GitHub Check: tests (04)
  • GitHub Check: tests (00)
  • GitHub Check: tests (02)
  • GitHub Check: tests (01)
  • GitHub Check: test-race (01)
  • GitHub Check: test-race (04)
  • GitHub Check: test-race (00)
  • GitHub Check: test-race (02)
  • GitHub Check: test-race (03)
  • GitHub Check: build-linux
  • GitHub Check: Build OSX
  • GitHub Check: Build Linux
  • GitHub Check: golangci-lint
  • GitHub Check: docker
  • GitHub Check: ghcr-publish
  • GitHub Check: heighliner-docker
  • GitHub Check: Analyze (go)
🔇 Additional comments (8)
x/trigger/types/codec.go (1)

113-121: Key layout compatibility looks correct.

The composed key format [32-byte-hash][8-byte-order][8-byte-triggerID] matches the stated legacy layout, and using a fixed-width codec should preserve ordering semantics.

app/app.go (1)

642-646: Keeper wiring update to KVStoreService looks correct.

Passing runtime.NewKVStoreService(keys[triggertypes.StoreKey]) matches the updated triggerkeeper.NewKeeper signature and aligns with the collections schema builder usage.

x/trigger/keeper/event_detector_test.go (1)

258-266: Good: test setup now checks NewTriggerWithID errors.

This makes failures in trigger construction explicit and avoids cascading false negatives.

x/trigger/keeper/query_server.go (1)

38-62: Collections pagination change looks solid.

Using query.CollectionPaginate over k.TriggersMap should provide deterministic ordering by key, and setting trigger.Id = key keeps the response consistent with the storage key.

x/trigger/keeper/migrations_v2.go (1)

21-23: The legacy prefix 0x04 is safe and does not overlap with any active trigger keys.

Verification confirms that GasLimitKeyPrefix (0x04) is unique among trigger module collections. The current active prefixes are: 0x01 (TriggersMap), 0x02 (EventListeners), 0x03 (Queue), 0x05 (NextTriggerID), 0x06 (QueueStartIndex), and 0x07 (QueueLength). The 0x04 prefix is documented as a legacy/deleted gas limit key in types/keys.go and is not used by any other collection. The migration deletion using Iterator(GasLimitKeyPrefix, storetypes.PrefixEndBytes(GasLimitKeyPrefix)) will safely delete only gas limit keys without affecting other triggers.

x/trigger/keeper/keeper_test.go (1)

103-119: [rewritten comment]
[classification tag]

x/trigger/keeper/event_listener_test.go (1)

168-170: Good: don’t ignore RemoveEventListener error

This matches the updated (bool, error) signature and prevents silent failures.

x/trigger/keeper/keeper.go (1)

30-58: Keeper collections wiring looks solid

SchemaBuilder usage and explicit StoreService assignment are consistent with the collections migration.

Comment thread x/trigger/keeper/event_listener_test.go
Comment thread x/trigger/keeper/event_listener_test.go
Comment thread x/trigger/keeper/event_listener.go
Comment thread x/trigger/keeper/genesis.go
Comment thread x/trigger/keeper/migrations_v2.go
Comment thread x/trigger/keeper/migrations_v2.go
Comment thread x/trigger/types/keys_test.go
@nagarajdivine nagarajdivine force-pushed the feat/trigger-module-collections branch from fd735fa to 60dd65d Compare December 18, 2025 15:47

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (16)
x/trigger/simulation/decoder.go (3)

34-37: Unsafe key slicing can panic; use bytes.HasPrefix instead.

The expression kvA.Key[:1] at line 34 will panic if kvA.Key is empty. The length validation at lines 35-37 executes only after the case condition matches, so it cannot prevent the panic. Use bytes.HasPrefix(kvA.Key, types.TriggerKeyPrefix.Bytes()) for safe, idiomatic prefix matching.

🔎 Apply this diff to fix the unsafe slicing:
-	case bytes.Equal(kvA.Key[:1], types.TriggerKeyPrefix.Bytes()):
+	case bytes.HasPrefix(kvA.Key, types.TriggerKeyPrefix.Bytes()):
 		if len(kvA.Key) < 1+types.TriggerIDLength {
 			return fmt.Sprintf("invalid Trigger key length: %d", len(kvA.Key))
 		}

58-67: Unsafe slicing can cause panic; use bytes.HasPrefix instead.

The expression kvA.Key[:1] at line 58 will panic if kvA.Key is empty. Although length validation exists at lines 62-67, it executes only after the case condition matches. Use bytes.HasPrefix(kvA.Key, types.EventListenerKeyPrefix.Bytes()) instead.

🔎 Apply this diff:
-	case bytes.Equal(kvA.Key[:1], types.EventListenerKeyPrefix.Bytes()):
+	case bytes.HasPrefix(kvA.Key, types.EventListenerKeyPrefix.Bytes()):
 		triggerIDA := extractTriggerIDFromEventListenerKey(kvA.Key)
 		triggerIDB := extractTriggerIDFromEventListenerKey(kvB.Key)
 
 		if len(kvA.Key) < 49 {

71-74: Use bytes.HasPrefix for safe prefix matching.

The expression kvA.Key[:1] at line 71 will panic if kvA.Key is empty. Although length validation exists at lines 72-74, it executes only after the case condition matches. Use bytes.HasPrefix(kvA.Key, types.QueueKeyPrefix.Bytes()) instead.

🔎 Apply this diff:
-	case bytes.Equal(kvA.Key[:1], types.QueueKeyPrefix.Bytes()):
+	case bytes.HasPrefix(kvA.Key, types.QueueKeyPrefix.Bytes()):
 		if len(kvA.Key) < 1+types.QueueIndexLength {
 			return fmt.Sprintf("invalid queue key length: %d", len(kvA.Key))
 		}
x/trigger/keeper/queue.go (3)

23-44: Critical bug: QueuePeek returns nil on fresh store despite non-empty queue.

On a freshly initialized queue (or after metadata is cleared):

  1. Enqueue defaults startIndex to 0 locally but never persists QueueStartIndex to the store
  2. QueuePeek correctly checks QueueIsEmpty() (returns false), then calls QueueStartIndex.Get() which returns collections.ErrNotFound
  3. The function returns nil instead of the queued item at index 0

Fix: Use the existing helper k.getQueueStartIndex(ctx) which defaults to 0 on ErrNotFound.

🔎 Apply this diff:
 func (k Keeper) QueuePeek(ctx sdk.Context) *types.QueuedTrigger {
-	isEmpty := k.QueueIsEmpty(ctx)
-
-	if isEmpty {
+	if k.QueueIsEmpty(ctx) {
 		return nil
 	}
-	startIndex, err := k.QueueStartIndex.Get(ctx)
-	if err != nil {
-		if errors.Is(err, collections.ErrNotFound) {
-			return nil
-		}
-		return nil
-	}
+	startIndex, err := k.getQueueStartIndex(ctx)
+	if err != nil {
+		// if metadata is corrupt, treat as empty
+		return nil
+	}
 	item, err := k.Queue.Get(ctx, startIndex)

72-101: Critical bug: Dequeue panics on fresh store despite non-empty queue.

Dequeue directly calls QueueStartIndex.Get() and QueueLength.Get() at lines 77 and 82, which panic on ErrNotFound when metadata isn't initialized. This breaks dequeue operations on freshly initialized queues.

Fix: Use the existing helpers k.getQueueStartIndex(ctx) and k.GetQueueLength(ctx) which default to 0 on ErrNotFound.

🔎 Apply this diff:
 func (k Keeper) Dequeue(ctx sdk.Context) {
 	if k.QueueIsEmpty(ctx) {
 		panic("unable to dequeue from empty queue.")
 	}
 
-	startIndex, err := k.QueueStartIndex.Get(ctx)
-	if err != nil {
-		panic(err)
-	}
-
-	length, err := k.QueueLength.Get(ctx)
-	if err != nil {
-		panic(err)
-	}
+	startIndex, err := k.getQueueStartIndex(ctx)
+	if err != nil {
+		panic(err)
+	}
+	length, err := k.GetQueueLength(ctx)
+	if err != nil {
+		panic(err)
+	}

47-69: Critical bug: Enqueue never initializes QueueStartIndex in the store.

On a fresh store, Enqueue defaults startIndex to 0 locally (lines 52-54) but never persists QueueStartIndex to the store. This causes QueuePeek and Dequeue to fail on non-empty queues because they directly call QueueStartIndex.Get() which returns ErrNotFound.

Fix: Initialize QueueStartIndex when it doesn't exist.

🔎 Apply this diff:
 func (k Keeper) Enqueue(ctx sdk.Context, item types.QueuedTrigger) error {
 	startIndex, err := k.QueueStartIndex.Get(ctx)
 	if err != nil && !errors.Is(err, collections.ErrNotFound) {
 		return err
 	}
 	if errors.Is(err, collections.ErrNotFound) {
 		startIndex = 0
+		if err := k.QueueStartIndex.Set(ctx, startIndex); err != nil {
+			return err
+		}
 	}
 
 	length, err := k.QueueLength.Get(ctx)
x/trigger/keeper/event_listener_test.go (2)

234-236: Avoid ignoring the error from GetTriggerEventI.

The error from trigger1.GetTriggerEventI() is being discarded. This should be captured and asserted to catch potential regressions.

🔎 Apply this diff:
-	eventI, _ := trigger1.GetTriggerEventI()
+	eventI, err := trigger1.GetTriggerEventI()
+	s.Require().NoError(err, "failed to get TriggerEventI for trigger1")

239-259: Test name doesn't match behavior.

The test is named TestCollectionsRemoveAllEventListenersForTrigger but calls RemoveEventListenerForTriggerID, which removes a single listener per trigger (not "all" listeners in the plural sense). Also, line 256 ignores the error from GetTriggerEventI().

🔎 Apply this diff:
-func (s *KeeperTestSuite) TestCollectionsRemoveAllEventListenersForTrigger() {
+func (s *KeeperTestSuite) TestCollectionsRemoveEventListenerForTriggerID() {
@@
-	eventI, _ := trigger.GetTriggerEventI()
+	eventI, err := trigger.GetTriggerEventI()
+	s.Require().NoError(err, "failed to get TriggerEventI for trigger")
x/trigger/types/keys_test.go (1)

17-24: Clean up duplicate assertions and use correct types.

Lines 17-18 and 19-20 assert the same thing (both check eventHash against K1()). Additionally, K2() and K3() return uint64, so comparisons should use uint64 literals instead of int() casts for type consistency.

🔎 Apply this diff:
 	assert.EqualValues(t, eventHash, key1.K1(), "should have correct event hash for key1")
 	assert.EqualValues(t, eventHash, key2.K1(), "should have correct event hash for key2")
-	assert.EqualValues(t, eventHash, key1.K1(), "should have correct name bytes in GetEventListenerKey for key1")
-	assert.EqualValues(t, eventHash, key2.K1(), "should have correct name bytes in GetEventListenerKey for key2")
-	assert.EqualValues(t, int(5), key1.K2(), "should have correct order bytes in GetEventListenerKey for key1")
-	assert.EqualValues(t, int(2), key2.K2(), "should have correct order bytes in GetEventListenerKey for key2")
-	assert.EqualValues(t, int(1), key1.K3(), "should have correct trigger id bytes in GetEventListenerKey for key1")
-	assert.EqualValues(t, int(0), key2.K3(), "should have correct trigger id bytes in GetEventListenerKey for key2")
+	assert.EqualValues(t, uint64(5), key1.K2(), "should have correct order bytes in GetEventListenerKey for key1")
+	assert.EqualValues(t, uint64(2), key2.K2(), "should have correct order bytes in GetEventListenerKey for key2")
+	assert.EqualValues(t, uint64(1), key1.K3(), "should have correct trigger id bytes in GetEventListenerKey for key1")
+	assert.EqualValues(t, uint64(0), key2.K3(), "should have correct trigger id bytes in GetEventListenerKey for key2")
x/trigger/keeper/migrations_v2.go (2)

12-19: Migration errors are still being swallowed.

Migrate1To2 always returns nil even if DeleteGasLimits encounters errors. This can leave partially-migrated state and lead to inconsistent node behavior. The function should propagate errors from DeleteGasLimits.

🔎 Apply this diff:
-func (m Migrator) DeleteGasLimits(ctx sdk.Context, logger log.Logger) {
+func (m Migrator) DeleteGasLimits(ctx sdk.Context, logger log.Logger) error {
@@
 	iter, err := store.Iterator(GasLimitKeyPrefix, storetypes.PrefixEndBytes(GasLimitKeyPrefix))
 	if err != nil {
 		logger.Error("Failed to create iterator", "err", err)
-		return
+		return err
 	}
@@
+	return nil
 }
 func (m Migrator) Migrate1To2(ctx sdk.Context) error {
 	logger := m.keeper.Logger(ctx)
 	logger.Info("Starting migration of x/trigger from 1 to 2.")
-	m.DeleteGasLimits(ctx, logger)
+	if err := m.DeleteGasLimits(ctx, logger); err != nil {
+		return err
+	}
 	logger.Info("Done migrating x/trigger from 1 to 2.")
 	return nil
 }

45-48: Copy iterator keys before storing them.

Depending on iterator implementation, iter.Key() may reference a buffer that gets reused on Next(), which can cause toDelete to contain corrupted or duplicated keys.

🔎 Apply this diff:
 var toDelete [][]byte
 for ; iter.Valid(); iter.Next() {
-	toDelete = append(toDelete, iter.Key())
+	k := iter.Key()
+	kCopy := make([]byte, len(k))
+	copy(kCopy, k)
+	toDelete = append(toDelete, kCopy)
 }
x/trigger/keeper/genesis.go (1)

59-68: Critical: Queue length double-counting causes import-export simulation failure.

This is the root cause of the failing CI. The code pre-sets QueueLength to len(data.QueuedTriggers), then calls Enqueue for each item. Since Enqueue increments QueueLength internally, the final length becomes 2N instead of N.

On export: queue has N items with QueueLength = N
On import: setQueueLength(N) + N Enqueue calls = QueueLength = 2N

This divergence causes the import-export simulation to detect different state between chains.

🔎 Apply this diff to fix:

Option 1: Initialize to 0 and let Enqueue manage the length:

-	queueLength := uint64(len(data.QueuedTriggers))
-	if err := k.setQueueLength(ctx, queueLength); err != nil {
+	// Initialize queue length to 0; Enqueue will increment it per item
+	if err := k.setQueueLength(ctx, 0); err != nil {
 		panic(fmt.Sprintf("Failed to set queue length: %v", err))
 	}

Option 2: Set length and write items directly without Enqueue:
If you need to avoid calling Enqueue (to preserve exact indices), you'll need to write queue items directly to storage without calling Enqueue, then set QueueLength to N once afterward.

x/trigger/keeper/event_listener.go (2)

38-64: Wrap errors with context in RemoveEventListener.

The function returns raw errors from trigger.GetTriggerEventI(), getEventNameHash(), EventListeners.Has(), and EventListeners.Remove() without context at lines 41, 46, 52, and 60. This makes debugging difficult when failures occur.

🔎 Apply this diff to wrap errors with context:
 	event, err := trigger.GetTriggerEventI()
 	if err != nil {
-		return false, err
+		return false, fmt.Errorf("could not get trigger event for trigger %d: %w", trigger.Id, err)
 	}

 	eventHash, err := getEventNameHash(event.GetEventPrefix())
 	if err != nil {
-		return false, err
+		return false, fmt.Errorf("could not compute event hash for trigger %d: %w", trigger.Id, err)
 	}
 	key := triggertypes.GetEventListenerKey(eventHash, event.GetEventOrder(), trigger.GetId())

 	exists, err := k.EventListeners.Has(ctx, key)
 	if err != nil {
-		return false, err
+		return false, fmt.Errorf("failed checking event listener existence for trigger %d: %w", trigger.Id, err)
 	}
 	if !exists {
 		return false, nil
 	}

 	err = k.EventListeners.Remove(ctx, key)
 	if err != nil {
-		return false, err
+		return false, fmt.Errorf("failed removing event listener for trigger %d: %w", trigger.Id, err)
 	}

Based on learnings, error wrapping helps reconstruct what went wrong from error messages.


153-161: Use a more appropriate error for invalid event names.

getEventNameHash returns triggertypes.ErrEventNotFound (line 157) when the normalized event name is empty. This conflates two distinct scenarios: invalid input (empty/whitespace name) versus a missing listener in storage. The same ErrEventNotFound is returned by GetEventListener when a listener doesn't exist, making it impossible to distinguish these cases during debugging.

Consider introducing ErrInvalidEventName or similar for input validation failures, reserving ErrEventNotFound for cases where the listener genuinely doesn't exist in the store.

🔎 Suggested approach:

Add a new error constant to x/trigger/types/errors.go:

ErrInvalidEventName = cerrs.Register(ModuleName, X, "invalid event name")

Then update getEventNameHash:

 func getEventNameHash(name string) ([]byte, error) {
 	eventName := strings.ToLower(strings.TrimSpace(name))
 	if len(eventName) == 0 {
-		return nil, triggertypes.ErrEventNotFound
+		return nil, triggertypes.ErrInvalidEventName.Wrap("event name cannot be empty")
 	}
 	hash := sha256.Sum256([]byte(eventName))
 	return hash[:], nil
 }
x/trigger/keeper/trigger.go (1)

19-32: RemoveTrigger still swallows errors, breaking error observability.

When TriggersMap.Has (line 21) or TriggersMap.Remove (line 26) fail, the function returns false without propagating the error. This makes it impossible for callers to distinguish "trigger didn't exist" from "storage operation failed"—a critical distinction for debugging the import-export simulation failure mentioned in the PR objectives.

Change the signature to (bool, error) and propagate errors, or at minimum log errors before returning false.

🔎 Apply this diff to propagate errors:
-func (k Keeper) RemoveTrigger(ctx sdk.Context, id types.TriggerID) bool {
+func (k Keeper) RemoveTrigger(ctx sdk.Context, id types.TriggerID) (bool, error) {
 	exists, err := k.TriggersMap.Has(ctx, id)
 	if err != nil {
-		return false
+		return false, err
 	}
 	if exists {
 		if err := k.TriggersMap.Remove(ctx, id); err != nil {
-			return false
+			return false, err
 		}
-		return true
+		return true, nil
 	}
-	return false
+	return false, nil
 }

Update all callers to handle the new (bool, error) return.

x/trigger/types/keys.go (1)

76-82: Fix misleading panic message in GetEventListenerKey.

The panic message at line 79 says "invalid event name: " but the parameter is eventHash []byte, not an event name. This is confusing for debugging.

🔎 Apply this diff:
 func GetEventListenerKey(eventHash []byte, order uint64, id TriggerID) collections.Triple[[]byte, uint64, uint64] {
 	if len(eventHash) == 0 {
-		panic("invalid event name: ")
+		panic("invalid event hash: empty")
 	}
 	return collections.Join3(eventHash, order, id)
 }
🧹 Nitpick comments (7)
x/trigger/keeper/queue.go (1)

103-110: Prefer using the helper method for consistency.

Line 105 calls k.QueueLength.Get(ctx) directly. For consistency with the rest of the queue implementation and to ensure proper not-found handling, consider using the k.GetQueueLength(ctx) helper instead.

🔎 Apply this diff:
 func (k Keeper) QueueIsEmpty(ctx sdk.Context) bool {
-	length, err := k.QueueLength.Get(ctx)
-	if err != nil {
-		return errors.Is(err, collections.ErrNotFound)
-	}
-	return length == 0
+	length, err := k.GetQueueLength(ctx)
+	if err != nil {
+		return true // treat errors as empty
+	}
+	return length == 0
 }
x/trigger/keeper/event_listener.go (4)

66-83: Wrap errors with context in GetEventListener.

Errors from getEventNameHash() (line 70) and EventListeners.Has() (line 75) are returned without contextual information. Add wrapping to identify which event name and trigger ID failed.

🔎 Apply this diff to wrap errors:
 	eventHash, err := getEventNameHash(eventName)
 	if err != nil {
-		return triggertypes.Trigger{}, err
+		return triggertypes.Trigger{}, fmt.Errorf("could not hash event name %q: %w", eventName, err)
 	}
 	key := triggertypes.GetEventListenerKey(eventHash, order, triggerID)
 	exists, err := k.EventListeners.Has(ctx, key)
 	if err != nil {
-		return triggertypes.Trigger{}, err
+		return triggertypes.Trigger{}, fmt.Errorf("failed checking event listener for trigger %d: %w", triggerID, err)
 	}

85-121: Wrap errors with context in IterateEventListeners.

Errors are returned without context at lines 89, 95, 102, and 110. Add contextual information to help identify which event name or operation failed.

🔎 Apply this diff to wrap errors:
 	eventHash, err := getEventNameHash(eventName)
 	if err != nil {
-		return err
+		return fmt.Errorf("could not hash event name %q: %w", eventName, err)
 	}
 	rng := collections.NewPrefixedTripleRange[[]byte, uint64, uint64](eventHash)

 	iter, err := k.EventListeners.Iterate(ctx, rng)
 	if err != nil {
-		return err
+		return fmt.Errorf("failed to iterate event listeners for %q: %w", eventName, err)
 	}
 	defer iter.Close()

 	for ; iter.Valid(); iter.Next() {
 		key, err := iter.Key()
 		if err != nil {
-			return err
+			return fmt.Errorf("failed to read iterator key for event %q: %w", eventName, err)
 		}
 		triggerID := key.K3()
 		trigger, err := k.GetTrigger(ctx, triggerID)
 		if err != nil {
 			if errors.Is(err, triggertypes.ErrTriggerNotFound) {
 				continue
 			}
-			return err
+			return fmt.Errorf("failed to get trigger %d for event %q: %w", triggerID, eventName, err)
 		}

123-131: Wrap error with context in HasEventListener.

The error from getEventNameHash() at line 127 lacks context about which event name failed.

🔎 Apply this diff:
 	eventHash, err := getEventNameHash(eventName)
 	if err != nil {
-		return false, err
+		return false, fmt.Errorf("could not hash event name %q: %w", eventName, err)
 	}

133-141: Wrap error with context in RemoveEventListenerForTriggerID.

The error from GetTrigger() at line 137 should include the trigger ID to clarify which trigger couldn't be retrieved.

🔎 Apply this diff:
 	trigger, err := k.GetTrigger(ctx, triggerID)
 	if err != nil {
-		return err
+		return fmt.Errorf("failed to get trigger %d: %w", triggerID, err)
 	}
x/trigger/keeper/trigger.go (2)

78-88: Consider using TriggersMap.Has for efficiency.

The function calls TriggersMap.Get and discards the value, when TriggersMap.Has would be more efficient and expressive for checking existence.

🔎 Apply this diff:
 func (k Keeper) HasTrigger(ctx sdk.Context, id uint64) (bool, error) {
-	_, err := k.TriggersMap.Get(ctx, id)
-	if errors.Is(err, collections.ErrNotFound) {
-		return false, nil
-	}
-	if err != nil {
-		return false, err
-	}
-	return true, nil
+	return k.TriggersMap.Has(ctx, id)
 }

113-116: Add guard against zero trigger ID.

While the -1 offset issue from the previous review has been correctly removed, the function should still guard against triggerID == 0, which would be invalid and could indicate a logic error upstream.

🔎 Apply this diff:
 func (k Keeper) setTriggerID(ctx sdk.Context, triggerID types.TriggerID) error {
+	if triggerID == 0 {
+		return fmt.Errorf("triggerID must be > 0")
+	}
 	return k.NextTriggerID.Set(ctx, triggerID)
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd735fa and 60dd65d.

📒 Files selected for processing (21)
  • .changelog/unreleased/features/2499-trigger-module-collections.md (1 hunks)
  • app/app.go (1 hunks)
  • x/trigger/keeper/event_detector_test.go (1 hunks)
  • x/trigger/keeper/event_listener.go (2 hunks)
  • x/trigger/keeper/event_listener_test.go (1 hunks)
  • x/trigger/keeper/genesis.go (3 hunks)
  • x/trigger/keeper/keeper.go (2 hunks)
  • x/trigger/keeper/keeper_test.go (2 hunks)
  • x/trigger/keeper/migrations_v2.go (1 hunks)
  • x/trigger/keeper/msg_server.go (3 hunks)
  • x/trigger/keeper/query_server.go (3 hunks)
  • x/trigger/keeper/queue.go (2 hunks)
  • x/trigger/keeper/queue_test.go (1 hunks)
  • x/trigger/keeper/trigger.go (3 hunks)
  • x/trigger/keeper/trigger_registry.go (1 hunks)
  • x/trigger/keeper/trigger_test.go (2 hunks)
  • x/trigger/simulation/decoder.go (2 hunks)
  • x/trigger/simulation/genesis.go (1 hunks)
  • x/trigger/types/codec.go (2 hunks)
  • x/trigger/types/keys.go (3 hunks)
  • x/trigger/types/keys_test.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .changelog/unreleased/features/2499-trigger-module-collections.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • x/trigger/keeper/queue_test.go
  • app/app.go
  • x/trigger/keeper/trigger_test.go
🧰 Additional context used
🧠 Learnings (30)
📓 Common learnings
Learnt from: nullpointer0x00
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: A GitHub issue was created to track the migration of parameters for the metadata, marker, name, and msgFees modules as indicated by the TODO comments in the codebase. The issue is linked to PR #1932 and can be found at https://github.com/provenance-io/provenance/issues/1935.
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 1993
File: x/marker/client/cli/cli_test.go:1090-1090
Timestamp: 2024-06-10T19:25:28.209Z
Learning: SpicyLemon prefers allowing tests to fail naturally if the setup is incorrect rather than explicitly checking and handling such errors within the test functions. They also recommend using `s.Require().NotNil(...)` instead of `s.T().Fatal(...)` for assertions in test functions.

Applied to files:

  • x/trigger/keeper/event_detector_test.go
  • x/trigger/keeper/event_listener_test.go
📚 Learning: 2024-10-10T23:19:54.565Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2176
File: app/upgrades_test.go:610-610
Timestamp: 2024-10-10T23:19:54.565Z
Learning: In `app/upgrades_test.go`, we prefer to keep TODO comments (like `TODO[viridian]`) to handle code deletions manually, and do not want to implement automated processes for code removal, especially for unit tests.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
  • x/trigger/keeper/migrations_v2.go
  • x/trigger/types/keys_test.go
📚 Learning: 2025-08-15T20:57:04.455Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/entries_test.go:26-31
Timestamp: 2025-08-15T20:57:04.455Z
Learning: SpicyLemon prefers string-based error assertions in tests using empty string to indicate "no error expected" and non-empty string for expected error messages. Use assertions.AssertErrorValue instead of errors.Is() comparisons because errors.Is() with s.Require().True() provides poor failure messages that only say "Should be true" without showing expected vs actual error content.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
📚 Learning: 2024-10-02T03:42:47.070Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/query_server_test.go:940-945
Timestamp: 2024-10-02T03:42:47.070Z
Learning: In the `TestScopeQuery` function, the conditional checks following the equality assertion are designed to help identify differences when `tc.expResp` and `actResp` are not equal and both are not `nil`. If they are equal, or either is `nil`, the failure message from the equality assertion suffices, and further checks are not needed.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
📚 Learning: 2024-10-02T03:37:18.486Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/msg_server_test.go:1225-1228
Timestamp: 2024-10-02T03:37:18.486Z
Learning: When test cases are run using `s.T().Run` instead of `s.Run`, use `require` with `t` instead of `s.Require()`, to ensure that assertions apply to the correct test instance.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
📚 Learning: 2025-08-15T20:01:55.078Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: app/upgrades_test.go:0-0
Timestamp: 2025-08-15T20:01:55.078Z
Learning: SpicyLemon prefers upgrade tests to fail when expected data is missing rather than skip, using s.Require().NoError() and s.Require().NotEmpty() to enforce hard requirements for upgrade data files in test scenarios.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
📚 Learning: 2024-10-10T23:13:56.722Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2176
File: x/ibchooks/types/params.go:22-26
Timestamp: 2024-10-10T23:13:56.722Z
Learning: In the file `x/ibchooks/types/params.go`, maintain the existing behavior in the `Validate` method without modifying the error messages, even if they may lack additional context.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
  • x/trigger/keeper/event_listener.go
  • x/trigger/types/keys.go
📚 Learning: 2025-09-05T19:27:01.045Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/keeper/msg_server_test.go:49-56
Timestamp: 2025-09-05T19:27:01.045Z
Learning: In test files, prefer using testutil.EventsBuilder and assertions.AssertEqualEvents over custom helper functions for event and attribute lookup to maintain consistency with established testing patterns in the Provenance codebase.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
  • x/trigger/types/keys_test.go
📚 Learning: 2024-10-02T03:39:04.537Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/msg_server_test.go:1248-1251
Timestamp: 2024-10-02T03:39:04.537Z
Learning: In subtests, it's acceptable to use `require` and `assert` functions directly instead of `s.Require()` and `s.Assert()` methods.

Applied to files:

  • x/trigger/keeper/event_listener_test.go
📚 Learning: 2024-10-08T18:12:51.935Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/migrations_v4.go:57-80
Timestamp: 2024-10-08T18:12:51.935Z
Learning: The `V3WriteNewScope` method in `x/metadata/keeper/migrations_v4.go` must exist until after the migration has been executed and will be deleted as part of the `viridian` cleanup.

Applied to files:

  • x/trigger/keeper/migrations_v2.go
  • x/trigger/keeper/keeper.go
📚 Learning: 2024-10-08T18:12:57.974Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 1874
File: cmd/dbmigrate/utils/migrator_test.go:10-10
Timestamp: 2024-10-08T18:12:57.974Z
Learning: The `cmd/dbmigrate` directory and its contents, including the `migrator_test.go` file, are planned to be removed from the project soon, as indicated by the user. Therefore, suggestions for changes or tests related to this directory are unnecessary.

Applied to files:

  • x/trigger/keeper/migrations_v2.go
📚 Learning: 2024-10-08T18:12:57.975Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/msg_server.go:262-263
Timestamp: 2024-10-08T18:12:57.975Z
Learning: In the `MigrateValueOwner` method, use `sdkerrors.ErrLogic` when representing unexpected backend errors, as the request itself is valid.

Applied to files:

  • x/trigger/keeper/migrations_v2.go
📚 Learning: 2024-10-08T18:12:51.935Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2033
File: app/upgrades.go:303-310
Timestamp: 2024-10-08T18:12:51.935Z
Learning: There are no rollback mechanisms used in the migration functions of the `app/upgrades.go` file, and error handling is managed by logging and returning errors as per the project's standards.

Applied to files:

  • x/trigger/keeper/migrations_v2.go
📚 Learning: 2025-08-15T20:34:01.110Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/codec.go:9-10
Timestamp: 2025-08-15T20:34:01.110Z
Learning: In Go Cosmos SDK projects, sdk.Msg interface embeds proto.Message interface, making it valid to copy elements from []sdk.Msg to []proto.Message even though the slice types are different. The pattern of declaring AllRequestMsgs as []sdk.Msg for type assertions while copying to []proto.Message for RegisterImplementations is a common and correct pattern.

Applied to files:

  • x/trigger/types/codec.go
📚 Learning: 2025-08-15T20:34:01.110Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/codec.go:9-10
Timestamp: 2025-08-15T20:34:01.110Z
Learning: The pattern of declaring AllRequestMsgs as []sdk.Msg and then copying to []proto.Message for RegisterImplementations is a standard, correct pattern used throughout the Provenance codebase. This works because sdk.Msg interface embeds proto.Message interface, making the copy operation valid. This pattern appears consistently across 15+ modules and compiles without errors.

Applied to files:

  • x/trigger/types/codec.go
📚 Learning: 2024-10-02T18:03:10.008Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/types/signer_utils_test.go:478-493
Timestamp: 2024-10-02T18:03:10.008Z
Learning: In Go tests, modifying byte slices directly using arithmetic operations like addition and subtraction is acceptable, even if it causes overflow, as overflow on `uint8` just wraps around. Avoid flagging such usage, and avoid suggesting bitwise operations which can be harder to read and understand.

Applied to files:

  • x/trigger/simulation/decoder.go
  • x/trigger/simulation/genesis.go
  • x/trigger/types/keys_test.go
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: The `CreateAutoResponseToAddrPrefix` function in the quarantine module should have unit tests that assert specific panic messages to ensure the tests fail for the correct reasons, specifically when the `MustLengthPrefix` function triggers a panic due to address length issues.

Applied to files:

  • x/trigger/simulation/decoder.go
  • x/trigger/types/keys.go
📚 Learning: 2025-08-15T20:59:51.949Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/keys.go:3-12
Timestamp: 2025-08-15T20:59:51.949Z
Learning: In Cosmos SDK, key prefixes only need to be unique within a given module's store, not across the entire application. Each module operates within its own isolated key-value store namespace, so the same byte prefixes can be safely reused across different modules without causing collisions.

Applied to files:

  • x/trigger/keeper/query_server.go
📚 Learning: 2025-08-15T22:52:24.461Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/keeper.go:74-173
Timestamp: 2025-08-15T22:52:24.461Z
Learning: In the ledger keeper NewKeeper function in x/ledger/keeper/keeper.go, SpicyLemon prefers to allow bankKeeper and registryKeeper dependencies to be nil rather than adding validation that would panic on nil values. The design intentionally supports nil keepers for flexibility.

Applied to files:

  • x/trigger/keeper/genesis.go
  • x/trigger/keeper/keeper_test.go
  • x/trigger/keeper/keeper.go
📚 Learning: 2025-08-15T20:59:09.909Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: The BankKeeper interface methods in Cosmos SDK v0.50.x and Provenance use `context.Context` as the context parameter type, not `sdk.Context`. This applies to methods like SendCoins, HasBalance, GetBalance, SpendableCoin, HasSupply, etc. The expected keeper interfaces should consistently use `context.Context` to match the actual Cosmos SDK bank keeper signatures.

Applied to files:

  • x/trigger/keeper/keeper_test.go
  • x/trigger/keeper/trigger.go
  • x/trigger/keeper/keeper.go
  • x/trigger/keeper/queue.go
📚 Learning: 2025-08-15T20:59:09.909Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/ledger/keeper/expected_keeper.go:11-19
Timestamp: 2025-08-15T20:59:09.909Z
Learning: In Cosmos SDK v0.50.x, the bank keeper interface methods use `context.Context` rather than `sdk.Context`. The BankKeeper interface should use `context.Context` for methods like SendCoins, HasBalance, GetBalance, etc.

Applied to files:

  • x/trigger/keeper/keeper_test.go
📚 Learning: 2025-08-15T20:39:34.095Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/types/util.go:68-102
Timestamp: 2025-08-15T20:39:34.095Z
Learning: In the Provenance codebase, functions with "Must" prefix (like MustGetMarkerAddress) can panic and should only be used when you know they won't panic. Instead, use the twin function that returns an error (like GetMarkerAddress) and handle the error properly.

Applied to files:

  • x/trigger/keeper/event_listener.go
  • x/trigger/types/keys.go
📚 Learning: 2024-06-10T19:25:28.209Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 0
File: :0-0
Timestamp: 2024-06-10T19:25:28.209Z
Learning: The `GetLockedCoins` function in `x/hold/keeper/locked_coins.go` must conform to the `banktypes.GetLockedCoinsFn` type, which does not allow returning an error. In cases where `k.GetHoldCoins` encounters an error, using `panic` is considered appropriate due to the severity of such errors.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2024-10-08T18:12:51.936Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/msg_server.go:106-109
Timestamp: 2024-10-08T18:12:51.936Z
Learning: In the `AddScopeDataAccess` method (and similar cases), redeclaring `err` with `:=` is appropriate when previous `err` variables are scoped within `if` statements, as using `=` would result in a compilation error.

Applied to files:

  • x/trigger/keeper/event_listener.go
📚 Learning: 2025-09-05T19:26:30.744Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/keeper/msg_server_test.go:38-46
Timestamp: 2025-09-05T19:26:30.744Z
Learning: In Provenance tests, prefer using testutil.EventsBuilder to construct expected events and assertions.AssertEqualEvents for precise event matching. Use assertions.AssertEventsContains only when there are many uncontrolled events from SDK modules that you don't care about. Most tests should be controlled enough to define all expected events precisely.

Applied to files:

  • x/trigger/types/keys_test.go
📚 Learning: 2024-10-02T03:06:21.997Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/migrations_v4_test.go:619-630
Timestamp: 2024-10-02T03:06:21.997Z
Learning: When unit tests exist for a key generation function, it's preferable to reuse that function in tests instead of manually constructing byte slices.

Applied to files:

  • x/trigger/types/keys_test.go
📚 Learning: 2024-10-08T18:12:51.936Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2140
File: x/metadata/keeper/migrations_v4_test.go:253-479
Timestamp: 2024-10-08T18:12:51.936Z
Learning: In Go tests within this project, it's acceptable to have similar helper functions like `newUUID`, `newScopeID`, `newSpecID`, and `newScope` defined within each test. Tests may tailor these functions to specific needs rather than sharing them, to avoid breaking other tests when changes are made and to keep them non-public.

Applied to files:

  • x/trigger/types/keys_test.go
📚 Learning: 2024-10-24T19:50:09.457Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2198
File: app/app_test.go:448-528
Timestamp: 2024-10-24T19:50:09.457Z
Learning: In `app/app_test.go`, when testing that the encoder is correctly wired to handle a governance `Proposal` with a `ParameterChangeProposal`, it's sufficient to have a basic test case without adding additional test cases, since the types and encoders are defined and maintained in external libraries outside our control.

Applied to files:

  • x/trigger/types/keys_test.go
📚 Learning: 2025-08-15T20:19:31.726Z
Learnt from: SpicyLemon
Repo: provenance-io/provenance PR: 2422
File: x/asset/simulation/decoder.go:8-14
Timestamp: 2025-08-15T20:19:31.726Z
Learning: The asset module in Provenance doesn't use collections-based storage like ledger/registry modules. Instead, it delegates storage to the NFT module, so a collections-based store decoder isn't applicable here. The module should use its own types.StoreKey rather than exchange.StoreKey in RegisterStoreDecoder.

Applied to files:

  • x/trigger/keeper/keeper.go
🧬 Code graph analysis (11)
x/trigger/simulation/decoder.go (2)
x/trigger/types/keys.go (7)
  • NextTriggerIDKey (65-65)
  • QueueStartIndexKey (67-67)
  • QueueLengthKey (69-69)
  • EventListenerKeyPrefix (61-61)
  • QueueKeyPrefix (63-63)
  • QueueIndexLength (24-24)
  • ModuleName (14-14)
x/trigger/types/trigger.pb.go (6)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
  • QueuedTrigger (106-113)
  • QueuedTrigger (117-117)
  • QueuedTrigger (118-120)
x/trigger/keeper/query_server.go (4)
x/trigger/types/errors.go (1)
  • ErrTriggerNotFound (8-8)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/query.pb.go (3)
  • QueryTriggersResponse (172-177)
  • QueryTriggersResponse (181-181)
  • QueryTriggersResponse (182-184)
x/trigger/simulation/genesis.go (1)
  • Triggers (27-27)
x/trigger/keeper/keeper_test.go (3)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • BlockHeightEvent (170-173)
  • BlockHeightEvent (177-177)
  • BlockHeightEvent (178-180)
x/trigger/types/keys.go (2)
  • ModuleName (14-14)
  • GetTriggerKey (90-97)
x/trigger/keeper/trigger.go (5)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/simulation/genesis.go (1)
  • TriggerID (23-23)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
x/trigger/types/errors.go (1)
  • ErrTriggerNotFound (8-8)
x/trigger/keeper/msg_server.go (1)
x/trigger/types/errors.go (1)
  • ErrTriggerNotFound (8-8)
x/trigger/keeper/trigger_registry.go (2)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/keeper/event_listener.go (4)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
x/trigger/types/keys.go (1)
  • GetEventListenerKey (77-82)
x/trigger/types/errors.go (2)
  • ErrEventNotFound (9-9)
  • ErrTriggerNotFound (8-8)
x/trigger/types/keys_test.go (1)
x/trigger/types/keys.go (3)
  • GetEventNameBytes (159-166)
  • GetEventListenerKey (77-82)
  • GetEventListenerPrefix (84-87)
x/trigger/keeper/keeper.go (3)
x/trigger/types/trigger.pb.go (6)
  • Trigger (33-42)
  • Trigger (46-46)
  • Trigger (47-49)
  • QueuedTrigger (106-113)
  • QueuedTrigger (117-117)
  • QueuedTrigger (118-120)
x/trigger/types/keys.go (6)
  • TriggerKeyPrefix (59-59)
  • EventListenerKeyPrefix (61-61)
  • QueueKeyPrefix (63-63)
  • NextTriggerIDKey (65-65)
  • QueueStartIndexKey (67-67)
  • QueueLengthKey (69-69)
x/trigger/types/codec.go (1)
  • EventListenerKeyCodec (115-121)
x/trigger/keeper/queue.go (3)
x/trigger/keeper/keeper.go (1)
  • Keeper (15-28)
x/trigger/types/trigger.pb.go (3)
  • QueuedTrigger (106-113)
  • QueuedTrigger (117-117)
  • QueuedTrigger (118-120)
x/trigger/types/errors.go (1)
  • ErrQueueIndexNotFound (10-10)
x/trigger/types/keys.go (2)
x/trigger/simulation/genesis.go (1)
  • TriggerID (23-23)
x/trigger/types/trigger.go (1)
  • TriggerID (16-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: tests (00)
  • GitHub Check: test-race (00)
  • GitHub Check: test-race (03)
  • GitHub Check: tests (02)
  • GitHub Check: test-race (04)
  • GitHub Check: tests (04)
  • GitHub Check: tests (03)
  • GitHub Check: test-race (01)
  • GitHub Check: test-race (02)
  • GitHub Check: tests (01)
  • GitHub Check: build-linux
  • GitHub Check: Build Linux
  • GitHub Check: Build OSX
  • GitHub Check: heighliner-docker
  • GitHub Check: ghcr-publish
  • GitHub Check: docker
  • GitHub Check: Analyze (go)
  • GitHub Check: golangci-lint
🔇 Additional comments (35)
x/trigger/keeper/event_detector_test.go (1)

261-262: LGTM! Error handling properly implemented.

The error from NewTriggerWithID is now correctly captured and asserted, ensuring test setup fails fast if trigger creation fails. This addresses the previous review feedback and follows the preferred pattern of using s.Require().NoError() for test assertions.

x/trigger/simulation/decoder.go (1)

99-115: LGTM! Helper functions are well-implemented.

Both extractTriggerIDFromEventListenerKey and safeDecodeUint64 include proper bounds checking before accessing byte slices, preventing panics. The defensive approach of returning 0 for invalid inputs is appropriate for simulation decoding.

x/trigger/types/codec.go (2)

47-111: LGTM! FixedBytes32KeyCodec is correctly implemented.

The codec properly validates key and buffer lengths before operations, preventing panics. The fixed 32-byte size is correctly enforced in Encode, Decode, and Size methods. The NonTerminal variants correctly delegate to terminal methods (appropriate for fixed-size keys), and the JSON stubs are intentionally unimplemented as documented.


113-121: LGTM! EventListenerKeyCodec correctly composes the triple key.

The codec correctly uses TripleKeyCodec with FixedBytes32KeyCodec for the event hash and two Uint64Keys for order and triggerID. The total key size (1-byte prefix + 32 + 8 + 8 = 49 bytes) aligns with the length checks in decoder.go.

x/trigger/keeper/queue.go (1)

197-229: LGTM! Helper methods correctly handle not-found cases.

The helper methods getQueueStartIndex and GetQueueLength properly default to 0 when metadata isn't found, making them safe for fresh store initialization. These should be consistently used throughout the queue implementation instead of direct .Get() calls.

x/trigger/simulation/genesis.go (1)

179-183: LGTM! Comment properly documents safe conversion.

The comment correctly explains that the uint64 to int conversion is safe because of the explicit boundary check at lines 179-182. The nolint:gosec directive is appropriate and well-justified.

x/trigger/keeper/trigger_registry.go (2)

10-25: LGTM! Improved error handling with appropriate logging.

The error handling now captures and logs failures from SetTrigger and SetEventListener with helpful context (triggerID). Continuing execution after logging is appropriate for registration operations, making this a good defensive programming improvement over the previous silent error discarding.


27-37: LGTM! Consistent error handling pattern.

The error handling for RemoveEventListener follows the same pattern as RegisterTrigger, providing good visibility into failures while allowing the operation to continue. This consistency improves maintainability.

x/trigger/keeper/keeper_test.go (1)

103-119: LGTM! Important backward compatibility test.

This test correctly validates that the new collections-based GetTrigger can read data stored with the old key format. This is crucial for the "no state migration required" requirement mentioned in the PR objectives. The test setup and assertions are appropriate.

x/trigger/keeper/query_server.go (2)

20-35: LGTM! Correct error mapping for public API.

The error handling correctly maps the internal collections.ErrNotFound to the public types.ErrTriggerNotFound, while propagating other errors unchanged. This is the appropriate pattern for query handlers.


37-62: LGTM! Correct migration to collections-based pagination.

The implementation correctly uses query.CollectionPaginate over the TriggersMap, properly sets the trigger.Id from the map key in the mapping function, and constructs the response correctly. The error handling is appropriate.

Note: Past review suggested removing explicit type parameters from the CollectionPaginate call (they can be inferred), but the code is functionally correct either way.

x/trigger/keeper/event_listener_test.go (1)

168-172: LGTM!

The error handling for RemoveEventListener has been properly addressed. The test now captures and asserts on the error value.

x/trigger/keeper/msg_server.go (2)

39-44: LGTM!

The error handling for NewTriggerWithID is properly implemented, propagating errors before proceeding with registration.


60-66: LGTM!

Good practice to map the internal collections.ErrNotFound to the public types.ErrTriggerNotFound error type for a cleaner API surface.

x/trigger/keeper/genesis.go (2)

12-21: LGTM!

Error handling for getTriggerID and getQueueStartIndex is properly implemented with panics on failure, which is appropriate for genesis operations.


33-38: Good defensive check for queue length consistency.

This warning log helps detect potential state inconsistencies during export. Consider whether this should be a panic instead of a warning if consistency is critical.

x/trigger/keeper/keeper.go (1)

30-59: LGTM! StoreService initialization issue resolved.

The StoreService field is now properly initialized at line 40, addressing the critical issue from the previous review. All collections are correctly wired with appropriate prefixes, and the schema building follows standard Cosmos SDK patterns.

x/trigger/keeper/event_listener.go (2)

16-35: LGTM! Error handling properly implemented.

All errors are appropriately wrapped with contextual information including the trigger ID, making debugging easier.


143-151: LGTM!

The implementation correctly delegates to IterateEventListeners with a simple counting callback.

x/trigger/keeper/trigger.go (6)

14-17: LGTM!

Clean delegation to the collections-based storage with proper error propagation.


34-44: LGTM!

Proper error translation from collections.ErrNotFound to the module-specific error type.


46-67: LGTM!

Proper iterator usage with defer close and error propagation.


118-128: LGTM! ID increment logic is now correct.

With the removal of the -1 offset in setTriggerID, this function now correctly returns the current ID and stores the next ID for future use. This should resolve the export/import stability issues mentioned in the PR objectives.


130-137: LGTM!

Correct implementation with proper default value handling.


90-111: LGTM! NewTriggerWithID correctly uses the ID sequence.

With the fixes to setTriggerID (removing the -1 offset), this function now correctly obtains incrementing IDs. The pattern of calling getNextTriggerID ensures proper ID allocation and should maintain export/import stability.

x/trigger/types/keys.go (10)

58-70: LGTM! Prefix declarations correctly migrated to collections.

All prefixes properly use collections.NewPrefix with appropriate numeric identifiers.


84-87: LGTM! Correctly uses .Bytes() conversion.

Properly converts the collections.Prefix to []byte for the return value.


89-97: LGTM! Correctly uses .Bytes() conversion.


99-102: LGTM! Correctly uses .Bytes() conversion.


104-107: LGTM! Correctly uses .Bytes() conversion.


109-116: LGTM! Correctly uses .Bytes() conversion.


118-121: LGTM! Correctly uses .Bytes() conversion.


123-126: LGTM! Correctly uses .Bytes() conversion.


128-134: LGTM! Good defensive length check.

The function safely handles short byte slices by returning 0.


143-149: LGTM! Good defensive length check.

The function safely handles short byte slices by returning 0. Past review verified all call sites pass exactly 8 bytes.

@nagarajdivine nagarajdivine force-pushed the feat/trigger-module-collections branch from 60dd65d to 35950e4 Compare February 12, 2026 17:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@x/trigger/keeper/queue.go`:
- Around line 47-69: Enqueue duplicates the "ErrNotFound => 0" handling; replace
the manual QueueStartIndex/Get and QueueLength/Get logic with the existing
helper calls (getQueueStartIndex and GetQueueLength) to obtain startIndex and
length, propagate any errors those helpers return, then compute position and set
the item and updated length as before using k.Queue.Set and k.QueueLength.Set;
ensure you remove the duplicated ErrNotFound checks and use the helper return
values consistently in the Enqueue function.
- Around line 14-20: QueueTrigger currently swallows errors from Enqueue which
can permanently lose triggers (UnregisterTrigger is called before QueueTrigger
in DetectBlockEvents); change QueueTrigger to return an error (update its
signature to func (k Keeper) QueueTrigger(ctx sdk.Context, trigger
types.Trigger) error), remove the silent swallow and instead return the Enqueue
error (or wrap it with context) instead of just logging, and update callers
(notably DetectBlockEvents) to handle the returned error appropriately
(propagate, retry, or panic per caller policy); keep the ctx.Logger().Error for
logging but do not suppress returning the underlying error from Enqueue.

In `@x/trigger/keeper/trigger_registry.go`:
- Around line 28-36: UnregisterTrigger currently swallows RemoveEventListener
errors; change UnregisterTrigger to return an error and propagate the
RemoveEventListener error instead of just logging it: call k.RemoveTrigger(ctx,
trigger.GetId()) after attempting to remove the listener or, better, attempt
k.RemoveEventListener(ctx, trigger) first and if it returns an error return that
error (do not silently log), otherwise call k.RemoveTrigger and return nil;
update the UnregisterTrigger signature and all callers accordingly to handle the
returned error and reference the existing RemoveEventListener and RemoveTrigger
methods.
- Around line 10-24: RegisterTrigger currently swallows errors from SetTrigger
and SetEventListener; change RegisterTrigger to return an error (e.g., func (k
Keeper) RegisterTrigger(ctx sdk.Context, trigger triggertypes.Trigger) error),
propagate and return any error from k.SetTrigger(...) or
k.SetEventListener(...), and update the caller CreateTrigger in msg_server.go to
check RegisterTrigger's error and return a failing response when an error occurs
so the transaction is aborted; reference SetTrigger, SetEventListener,
RegisterTrigger, and CreateTrigger when making these changes.

In `@x/trigger/simulation/decoder.go`:
- Around line 58-69: The EventListener branch currently slices kvA.Key[:1]
unsafely, calls extractTriggerIDFromEventListenerKey before validating key
lengths, and never compares kvA.Value/kvB.Value; fix by first checking key
lengths (ensure len(kvA.Key) and len(kvB.Key) are >= expected), use a safe
prefix check (e.g. bytes.HasPrefix or check len>0 before slicing) instead of
kvA.Key[:1], then extract trigger IDs with extractTriggerIDFromEventListenerKey,
and include comparison of the stored values (kvA.Value vs kvB.Value) — decode
them if there is a helper or include their hex/base64 representation in the
returned message so value differences are detected by the decoder.
🧹 Nitpick comments (7)
x/trigger/types/keys.go (1)

128-148: Defensive length checks are a good addition.

Returning 0 for undersized byte slices instead of panicking is a safer approach. However, silently returning 0 could mask bugs at call sites — callers won't know they got a zero-value due to bad input vs. a legitimately stored zero.

x/trigger/types/keys_test.go (1)

28-34: Prefix type comparison in assertions may be fragile.

Lines 32 and 38 compare collections.Prefix (e.g., EventListenerKeyPrefix) against []byte slices. While EqualValues should coerce types, if collections.Prefix changes its underlying representation, these tests could silently pass or fail unexpectedly. Consider comparing against .Bytes() explicitly for clarity.

x/trigger/keeper/msg_server.go (1)

60-63: Good error mapping from collections.ErrNotFound to types.ErrTriggerNotFound.

Consider wrapping the trigger ID for debuggability:

Suggestion
 	if errors.Is(err, collections.ErrNotFound) {
-		return nil, types.ErrTriggerNotFound
+		return nil, fmt.Errorf("%w: id %d", types.ErrTriggerNotFound, msg.GetId())
 	}
x/trigger/keeper/event_listener.go (1)

86-121: IterateEventListeners silently skips triggers with ErrTriggerNotFound.

This is a reasonable defensive choice for orphaned event listener keys, but it means data inconsistencies are silently ignored. Consider at least logging a warning when a trigger referenced by an event listener key doesn't exist.

x/trigger/keeper/trigger.go (1)

78-88: HasTrigger uses Get instead of Has — unnecessary deserialization overhead.

TriggersMap.Has(ctx, id) would check existence without deserializing the value.

Proposed fix
 func (k Keeper) HasTrigger(ctx sdk.Context, id uint64) (bool, error) {
-	_, err := k.TriggersMap.Get(ctx, id)
-	if errors.Is(err, collections.ErrNotFound) {
-		return false, nil
-	}
-	if err != nil {
-		return false, err
-	}
-	return true, nil
+	return k.TriggersMap.Has(ctx, id)
 }
x/trigger/keeper/queue.go (1)

120-124: Unnecessary Key() call in GetAllQueueItems — result is discarded.

Line 121 reads the key but never uses it. Remove to avoid wasted work.

Proposed fix
 	for ; iter.Valid(); iter.Next() {
-		_, err := iter.Key()
-		if err != nil {
-			return nil, err
-		}
 		value, err := iter.Value()
 		if err != nil {
 			return nil, err
x/trigger/simulation/decoder.go (1)

99-108: Magic numbers and dead code in extractTriggerIDFromEventListenerKey.

  1. The magic number 49 appears in three places (lines 62, 65, 100) and 41 at line 103. These should be derived from named constants to remain in sync if the key layout changes. For example:

    const eventListenerKeyMinLen = 1 + 32 + 8 + 8 // prefix + eventTypeHash + order + triggerID
    const triggerIDOffset = 1 + 32 + 8
  2. Line 104 (if len(triggerIDBytes) < 8) is dead code — key[41:49] always produces exactly 8 bytes after the len(key) >= 49 guard at line 100.

  3. The key structure (1 prefix + 32 hash + 8 order + 8 triggerID) should be verified against the actual collections composite key definition.

#!/bin/bash
# Verify the EventListener key structure in types/keys.go
fd 'keys.go' --type=f --exec grep -n -A20 'EventListener' {}

Comment thread x/trigger/keeper/queue.go
Comment thread x/trigger/keeper/queue.go
Comment thread x/trigger/keeper/trigger_registry.go
Comment thread x/trigger/keeper/trigger_registry.go
Comment thread x/trigger/simulation/decoder.go
@nagarajdivine nagarajdivine force-pushed the feat/trigger-module-collections branch from 35950e4 to 96ecfd0 Compare February 12, 2026 19:56
@nagarajdivine nagarajdivine force-pushed the feat/trigger-module-collections branch from 96ecfd0 to aff2541 Compare February 19, 2026 18:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Switch to collections in the trigger module.

2 participants