Skip to content

[c-930] use metrics v2 to trace relevant code paths#84

Open
dbrajovic wants to merge 6 commits intov0.50.x-injfrom
c-930/trace-everything
Open

[c-930] use metrics v2 to trace relevant code paths#84
dbrajovic wants to merge 6 commits intov0.50.x-injfrom
c-930/trace-everything

Conversation

@dbrajovic
Copy link
Copy Markdown

@dbrajovic dbrajovic commented Mar 30, 2026

Introduces injective metrics and traces code paths of interest

MERGE ONLY after InjectiveLabs/cometbft#10 has been merged and tagged and this PR's go.mod is updated

Description

Closes: #XXXX


Author Checklist

All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.

I have...

  • included the correct type prefix in the PR title
  • added ! to the type prefix if API or client breaking change
  • targeted the correct branch (see PR Targeting)
  • provided a link to the relevant issue or specification
  • followed the guidelines for building modules
  • included the necessary unit and integration tests
  • added a changelog entry to CHANGELOG.md
  • included comments for documenting Go code
  • updated the relevant documentation or specification
  • reviewed "Files changed" and left comments if necessary
  • run make lint and make test
  • confirmed all CI checks have passed

Reviewers Checklist

All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.

I have...

  • confirmed the correct type prefix in the PR title
  • confirmed ! in the type prefix if API or client breaking change
  • confirmed all author checklist items have been addressed
  • reviewed state machine logic
  • reviewed API design and naming
  • reviewed documentation is accurate
  • reviewed tests and test coverage
  • manually tested (if applicable)

Summary by CodeRabbit

  • New Features

    • Added performance metrics and observability across core modules to track operation execution times and provide detailed telemetry data.
  • Chores

    • Updated Go toolchain to version 1.23.9.
    • Upgraded core dependencies including gRPC, Protocol Buffers, and OpenTelemetry libraries.
    • Updated CometBFT dependency references.

@linear
Copy link
Copy Markdown

linear bot commented Mar 30, 2026

@github-actions
Copy link
Copy Markdown

@dbrajovic your pull request is missing a changelog!

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

📝 Walkthrough

Walkthrough

This PR adds metrics instrumentation across the codebase: modules gain a meter field and Meter(ctx) helper, many functions unwrap the SDK context (sdk.UnwrapSDKContext), and defer FuncTiming calls are added to measure execution duration. Several go.mod replace/version bumps (notably CometBFT forks) were also applied.

Changes

Cohort / File(s) Summary
Core & Node Startup
baseapp/abci.go, server/start.go, testutil/network/util.go
Small signature change in executeTxs (named returns) and added explicit nil arg to node.NewNode call; test util mirrors same node.NewNode(nil) change.
Go Module / Toolchain updates
go.mod, simapp/go.mod, store/go.mod, tests/go.mod, x/circuit/go.mod, x/evidence/go.mod, x/feegrant/go.mod, x/nft/go.mod, x/upgrade/go.mod
Bumped Go version/toolchain entries, updated many direct/indirect deps (OpenTelemetry, gRPC, protobuf, testify), added github.com/InjectiveLabs/metrics/v2, and updated CometBFT replace directives to InjectiveLabs fork revisions.
Auth & Authz
x/auth/keeper/..., x/authz/keeper/...
Added meter field and Meter(ctx) helper (authz); unwrapped SDK contexts in keeper/query/msg handlers and added deferred FuncTiming instrumentation across auth and authz code.
Bank (balances, sends, views)
x/bank/keeper/...
Extensive context unwrapping and deferred timing added to keeper, send, view, transient/virtual balances, gRPC queries, genesis and msg server paths.
Distribution (rewards, fee pool, hooks, params, store)
x/distribution/keeper/...
Introduced meter and Meter(ctx) on keeper; unwrapped contexts and added timing across allocation, delegation rewards, fee pool, hooks, stores, gRPC queries, genesis, and msg server methods.
Staking (validators, delegation, hooks, abci, queries)
x/staking/keeper/...
Added meter/Meter, unwrapped SDK contexts broadly, and added FuncTiming to many keeper, msg server, ABIs, migration, and query flows (very large set of files).
Gov (proposals, votes, deposits, legacy wiring)
x/gov/keeper/..., tests/integration/gov/keeper/keeper_test.go, x/gov/module.go
Added keeper metering, unwrapped contexts, deferred timing across submit/cancel/vote/deposit/tally flows; updated legacy v1beta1 MsgServer wiring to accept keeper reference.
Group & Proposals
x/group/keeper/...
Added meter, unwrapped contexts, and deferred timing to group creation, policy, proposals, voting, execution, and related helpers.
NFT (classes, tokens, queries, batches)
x/nft/keeper/...
Added meter support and unwrapped SDK contexts across class/nft operations, gRPC handlers, msg server, batch ops, and genesis/export.
Upgrade, Evidence, Crisis, Consensus, Circuit, Params, Mint, Feegrant
x/upgrade/keeper/..., x/evidence/keeper/..., x/crisis/keeper/..., x/consensus/keeper/..., x/circuit/keeper/..., x/params/keeper/..., x/mint/keeper/..., x/feegrant/keeper/...
Per-module additions: meter field/Meter(ctx) where applicable; unwrapped SDK contexts and added deferred FuncTiming in keeper/query/msg/genesis paths; some go.mod/toolchain updates in related modules.
Slashing & Evidence infractions/hooks/queries
x/slashing/keeper/..., x/evidence/keeper/...
Added keeper metering, unwrapped contexts, and timing instrumentation to signing-info, infractions, hooks, msg server, and ABCl/query handlers.
Misc: Params, Crisis, Aliases, Helpers
x/params/keeper/..., x/crisis/keeper/..., x/distribution/keeper/alias_functions.go, x/group/...
Smaller, focused changes adding SDK context unwrapping and deferred timing to parameter getters, genesis/ export, alias functions, and helper utilities.
Whitespace/migrations & small test adjustments
various migrations.go, test files
Minor whitespace additions in several migration functions and updated test wiring to pass keeper into legacy msg server implementations.

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

A rabbit scurries through the code with glee, 🐰
Unwrapping contexts, adding meters with speed,
Deferring timings so each keeper can chime,
Tracing durations, one function at a time.
Hops of ⏱️ and joy — instrumentation, hooray!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title '[c-930] use metrics v2 to trace relevant code paths' directly summarizes the main change: integration of metrics v2 for code-path tracing across multiple modules.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch c-930/trace-everything

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

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (4)
go.mod (1)

64-64: ⚠️ Potential issue | 🔴 Critical

Critical: Pipeline failing due to confirmed vulnerable dependencies.

The following confirmed vulnerabilities must be addressed before merging:

  1. google.golang.org/grpc@1.75.0 (Critical - GHSA-p77j-4mvh-x3m3): Authorization bypass via missing leading slash in :path header. Upgrade to v1.79.3 or later.

  2. go.opentelemetry.io/otel/sdk@1.38.0 (High - GHSA-9h8m-3fm2-qjrq): Arbitrary code execution via PATH hijacking on macOS/Darwin systems. Upgrade to v1.40.0 or later.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@go.mod` at line 64, Update the vulnerable module versions in go.mod: bump
google.golang.org/grpc to v1.79.3 or later and go.opentelemetry.io/otel/sdk to
v1.40.0 or later, then run dependency resolution (e.g., use go get
google.golang.org/grpc@v1.79.3 and go get go.opentelemetry.io/otel/sdk@v1.40.0)
and go mod tidy to ensure the upgraded transitive deps are resolved; verify the
updates affect any references to these modules in build/test pipelines and
re-run CI to confirm the vulnerabilities are resolved.
x/slashing/keeper/signing_info.go (2)

261-289: ⚠️ Potential issue | 🟠 Major

Return immediately when the callback asks to stop.

The contract says iteration halts when the callback returns true, but Lines 283-285 only break the inner bit loop. The outer chunk loop keeps running, so callers can still receive more callbacks after requesting stop.

Suggested fix
-			if cb(index, bs.Test(i)) {
-				break
-			}
+			if cb(index, bs.Test(i)) {
+				return nil
+			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/slashing/keeper/signing_info.go` around lines 261 - 289,
IterateMissedBlockBitmap isn't stopping the outer chunk loop when the callback
cb(index, ...) returns true — change the control flow so that when cb returns
true you return immediately (e.g., return nil) instead of breaking only the
inner bit loop; update the inner loop in IterateMissedBlockBitmap to return upon
cb true so the function exits and no further chunks/callbacks are processed,
keeping the existing deferred iter.Close and error handling intact.

197-230: ⚠️ Potential issue | 🟠 Major

Propagate the bitmap write error.

Line 229 ignores the result of setMissedBlockBitmapChunk. A failed store write currently returns nil, so missed-block state can silently diverge from slashing state.

Suggested fix
-	k.setMissedBlockBitmapChunk(sdkCtx, addr, chunkIndex, updatedChunk)
-	return nil
+	if err := k.setMissedBlockBitmapChunk(sdkCtx, addr, chunkIndex, updatedChunk); err != nil {
+		return errors.Wrapf(err, "failed to persist bitmap chunk; index: %d", index)
+	}
+	return nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/slashing/keeper/signing_info.go` around lines 197 - 230, In
SetMissedBlockBitmapValue, the return value from
k.setMissedBlockBitmapChunk(sdkCtx, addr, chunkIndex, updatedChunk) is ignored
so storage errors are dropped; capture its error, wrap it (e.g., with context
like "failed to set bitmap chunk; index: %d") and return it from
SetMissedBlockBitmapValue so write failures propagate to the caller (referencing
the function SetMissedBlockBitmapValue and the method
setMissedBlockBitmapChunk).
x/staking/keeper/validator.go (1)

329-351: ⚠️ Potential issue | 🟠 Major

Close both iterators before returning.

GetValidators and IterateLastValidatorPowers each open a store iterator but never close it. Any decode failure, handler stop, or normal return leaks the iterator.

Suggested fix
 	iterator, err := store.Iterator(types.ValidatorsKey, storetypes.PrefixEndBytes(types.ValidatorsKey))
 	if err != nil {
 		return nil, err
 	}
+	defer iterator.Close()
@@
 	iter, err := k.LastValidatorsIterator(sdkCtx)
 	if err != nil {
 		return err
 	}
+	defer iter.Close()

Also applies to: 453-475

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/staking/keeper/validator.go` around lines 329 - 351, GetValidators (and
similarly IterateLastValidatorPowers) opens a KV iterator that is never closed,
leaking resources on errors or normal returns; fix by ensuring the iterator is
closed on all paths (use a defer iterator.Close() immediately after successful
store.Iterator(...) return, and ensure any early returns still run the defer)
for functions GetValidators and IterateLastValidatorPowers so the iterator is
always released even when UnmarshalValidator or other steps return an error.
🟠 Major comments (18)
x/crisis/keeper/keeper.go-21-21 (1)

21-21: ⚠️ Potential issue | 🟠 Major

Guard lazy meter initialization against concurrent access.

k.meter is mutated in the hot path without synchronization. Concurrent calls can race on first initialization and produce undefined behavior under race conditions.

Proposed fix
 import (
 	"context"
 	"fmt"
+	"sync"
 	"time"
@@
 type Keeper struct {
 	meter          metrics.Meter
+	meterOnce      sync.Once
 	routes         []types.InvarRoute
@@
 func (k *Keeper) Meter(ctx context.Context) metrics.Meter {
-	if k.meter == nil {
-		k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(types.ModuleName, metrics.Tag("svc", types.ModuleName))
-	}
+	k.meterOnce.Do(func() {
+		k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(types.ModuleName, metrics.Tag("svc", types.ModuleName))
+	})
 
 	return k.meter
 }

Also applies to: 138-144

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/crisis/keeper/keeper.go` at line 21, The lazy initialization of k.meter on
the keeper struct is racy; add a synchronization primitive to the keeper (e.g.,
a sync.Mutex field or a sync.Once) and use it when checking/setting k.meter so
concurrent callers cannot race on first initialization; update all places that
lazily mutate k.meter (the hot-path methods that reference k.meter) to acquire
the mutex or use the Once to perform initialization and then read k.meter,
ensuring a double-check (nil check before and after locking if using a mutex) to
avoid unnecessary locking.
x/gov/keeper/keeper.go-202-208 (1)

202-208: ⚠️ Potential issue | 🟠 Major

Same Meter caching issue as staking keeper.

The Meter method uses a pointer receiver, but Logger (line 155) uses a value receiver. This has the same ineffective caching problem described in x/staking/keeper/keeper.go—the meter will be re-created on each call from value receiver methods.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/gov/keeper/keeper.go` around lines 202 - 208, The Meter method is using a
pointer receiver while Logger uses a value receiver, causing cached fields to be
ineffective for value-receiver methods; change the Meter signature to use a
value receiver (func (k Keeper) Meter(...)) so it matches Logger and prevents
the meter from being re-created when called from value-receiver methods, keeping
the caching behavior consistent for the Keeper type.
x/staking/keeper/keeper.go-191-197 (1)

191-197: ⚠️ Potential issue | 🟠 Major

Meter caching ineffective due to value receivers.

The Meter method uses a pointer receiver (*Keeper) to cache the meter, but most keeper methods that call it (e.g., Logger, GetLastTotalPower, SetLastTotalPower) use value receivers (Keeper). When a value receiver method calls k.Meter(sdkCtx), the implicit address-of creates a copy, so the cached k.meter assignment doesn't persist across calls. This results in creating a new SubMeter on every invocation, defeating the lazy initialization purpose.

Consider either:

  1. Change all keeper methods to pointer receivers, or
  2. Initialize the meter in NewKeeper instead of lazy initialization
Proposed fix: Initialize meter in NewKeeper
 func NewKeeper(
 	cdc codec.BinaryCodec,
 	storeService storetypes.KVStoreService,
 	ak types.AccountKeeper,
 	bk types.BankKeeper,
 	authority string,
 	validatorAddressCodec addresscodec.Codec,
 	consensusAddressCodec addresscodec.Codec,
+	meter metrics.Meter,
 ) *Keeper {
 	// ... existing validation ...
 
 	return &Keeper{
+		meter:                 meter,
 		storeService:          storeService,
 		cdc:                   cdc,
 		// ... rest of fields ...
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/staking/keeper/keeper.go` around lines 191 - 197, The Meter method's lazy
cache on *Keeper is ineffective because many callers use value receivers (e.g.,
Logger, GetLastTotalPower, SetLastTotalPower) so the cached k.meter doesn't
persist; fix by initializing the meter once in NewKeeper instead of lazy init:
in NewKeeper compute and assign k.meter using ctx.Meter().SubMeter(...)
(referencing Keeper.Meter and NewKeeper) and simplify/remove the lazy set in
Meter so it just returns k.meter (or keep a nil-check that does not attempt to
set); this avoids relying on pointer receiver semantics and ensures a single
SubMeter instance is used.
x/auth/keeper/keeper.go-305-311 (1)

305-311: ⚠️ Potential issue | 🟠 Major

Data race in lazy meter initialization.

The Meter method has a potential data race: multiple goroutines can concurrently read and write ak.meter without synchronization. While the functional outcome may be equivalent (all goroutines would create similar sub-meters), this is a race condition that can be detected by the Go race detector.

Consider either:

  1. Initialize the meter in NewAccountKeeper (if context is available at that point), or
  2. Use sync.Once for thread-safe lazy initialization.
🔒 Proposed fix using sync.Once

Add a sync.Once field to the struct:

type AccountKeeper struct {
	// ... existing fields ...
	meter     metrics.Meter
	meterOnce sync.Once
}

Then update the Meter method:

 func (ak *AccountKeeper) Meter(ctx context.Context) metrics.Meter {
-	if ak.meter == nil {
-		ak.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(types.ModuleName, metrics.Tag("svc", types.ModuleName))
-	}
-
-	return ak.meter
+	ak.meterOnce.Do(func() {
+		ak.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(types.ModuleName, metrics.Tag("svc", types.ModuleName))
+	})
+	return ak.meter
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/auth/keeper/keeper.go` around lines 305 - 311, The Meter method has a data
race on ak.meter; add a sync.Once field (e.g. meterOnce sync.Once) to the
AccountKeeper struct and change Meter to perform thread-safe lazy init via
ak.meterOnce.Do(func(){ ak.meter =
sdk.UnwrapSDKContext(ctx).Meter().SubMeter(types.ModuleName, metrics.Tag("svc",
types.ModuleName)) }), returning ak.meter afterwards; alternatively, if a
suitable context is available when constructing the keeper, initialize ak.meter
in NewAccountKeeper instead. Ensure you import sync and remove unsynchronized
writes to ak.meter elsewhere.
x/authz/keeper/keeper.go-486-492 (1)

486-492: ⚠️ Potential issue | 🟠 Major

Data race in lazy meter initialization (same issue as x/auth/keeper/keeper.go).

This Meter method has the same race condition: concurrent goroutines can simultaneously check k.meter == nil and write to it without synchronization.

Apply the same fix pattern using sync.Once:

🔒 Proposed fix using sync.Once

Add a sync.Once field to the struct:

 type Keeper struct {
 	storeService corestoretypes.KVStoreService
 	cdc          codec.Codec
 	router       baseapp.MessageRouter
 	authKeeper   authz.AccountKeeper
 	bankKeeper   authz.BankKeeper
 	meter        metrics.Meter
+	meterOnce    sync.Once
 }

Then update the Meter method:

 func (k *Keeper) Meter(ctx context.Context) metrics.Meter {
-	if k.meter == nil {
-		k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(authz.ModuleName, metrics.Tag("svc", authz.ModuleName))
-	}
-
-	return k.meter
+	k.meterOnce.Do(func() {
+		k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(authz.ModuleName, metrics.Tag("svc", authz.ModuleName))
+	})
+	return k.meter
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/authz/keeper/keeper.go` around lines 486 - 492, The Meter method on Keeper
has a data race when lazily initializing k.meter; add a sync.Once field (e.g.,
meterOnce) to the Keeper struct and use meterOnce.Do(...) inside Keeper.Meter to
perform the one-time initialization using
sdk.UnwrapSDKContext(ctx).Meter().SubMeter(authz.ModuleName, metrics.Tag("svc",
authz.ModuleName)), leaving the method to simply return k.meter after the Do
call; follow the same pattern used in x/auth/keeper/keeper.go to ensure
thread-safe lazy init.
x/nft/keeper/keeper.go-17-18 (1)

17-18: ⚠️ Potential issue | 🟠 Major

Remove the mutable meter field and eliminate lazy initialization to prevent cache corruption and race conditions.

The Meter method uses a pointer receiver while all other keeper methods use value receivers. When value-receiver methods call k.Meter(), Go implicitly creates a temporary pointer to the copy (&k), and any mutations to k.meter are lost when the method returns. This causes:

  1. Receiver-copy loss: The lazy-initialized cache is never persisted back to the original keeper.
  2. Race condition: Concurrent calls to Meter() from different value copies race to initialize k.meter on separate copies.
Recommended fix: Remove mutable cache
 type Keeper struct {
-	meter        metrics.Meter
 	cdc          codec.BinaryCodec
 	storeService store.KVStoreService
 	bk           nft.BankKeeper
 	ac           address.Codec
 }
@@
-func (k *Keeper) Meter(ctx context.Context) metrics.Meter {
-	if k.meter == nil {
-		k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(nft.ModuleName, metrics.Tag("svc", nft.ModuleName))
-	}
-
-	return k.meter
+func (k Keeper) Meter(ctx context.Context) metrics.Meter {
+	return sdk.UnwrapSDKContext(ctx).Meter().SubMeter(nft.ModuleName, metrics.Tag("svc", nft.ModuleName))
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/nft/keeper/keeper.go` around lines 17 - 18, Remove the mutable meter field
from the keeper struct and stop lazy-initializing it in Meter; instead make
Meter a non-mutating accessor that returns a fresh or globally initialized
metrics.Meter without writing to k.meter. Concretely: delete the meter field
declaration, change Meter to use a metrics provider/factory (or a package-level
singleton initialized with sync.Once in an init/path-safe place) and return that
meter directly from Meter (keep Meter as a value receiver and do not assign to
any keeper fields). Update any references to k.meter to call k.Meter() and
ensure no code mutates the keeper inside Meter to avoid the receiver-copy loss
and race conditions.
x/distribution/keeper/validator.go-181-181 (1)

181-181: ⚠️ Potential issue | 🟠 Major

Propagate incrementReferenceCount failure.

Line 181 drops the returned error. If the reference-count update fails, execution still writes slash state, which can leave historical rewards tracking inconsistent.

💡 Proposed fix
-	k.incrementReferenceCount(sdkCtx, valAddr, newPeriod)
+	if err := k.incrementReferenceCount(sdkCtx, valAddr, newPeriod); err != nil {
+		return err
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/distribution/keeper/validator.go` at line 181, The call to
k.incrementReferenceCount(sdkCtx, valAddr, newPeriod) currently ignores its
returned error; update the caller (the function containing that call) to check
the returned error and propagate it (return the error) instead of dropping it so
execution halts when the reference-count update fails and avoids writing slash
state inconsistently. Locate the incrementReferenceCount(...) call in
validator.go and change the call to capture its error (err :=
k.incrementReferenceCount(...)); if err != nil { return err } (or wrap and
return the error consistent with the surrounding function's error handling).
x/evidence/keeper/grpc_query.go-48-50 (1)

48-50: ⚠️ Potential issue | 🟠 Major

Handle Evidences.Get errors instead of discarding them.

At line 48, the returned error is ignored, which converts all failures—including storage and codec errors—into codes.NotFound. Other call sites in the module properly check this error (see keeper.go:107 and genesis.go:26).

The proposed fix captures the error and checks it. Note that if the error is not collections.ErrNotFound, it should return an appropriate error code (e.g., codes.Internal) rather than codes.NotFound to accurately reflect the failure type.

💡 Proposed fix
-	evidence, _ := k.k.Evidences.Get(sdkCtx, decodedHash)
-	if evidence == nil {
+	evidence, err := k.k.Evidences.Get(sdkCtx, decodedHash)
+	if err != nil {
+		return nil, status.Errorf(codes.NotFound, "evidence %s not found", req.Hash)
+	}
+	if evidence == nil {
 		return nil, status.Errorf(codes.NotFound, "evidence %s not found", req.Hash)
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/evidence/keeper/grpc_query.go` around lines 48 - 50, The call to
k.k.Evidences.Get currently discards the returned error which causes all
failures to be reported as codes.NotFound; change the code in the handler that
calls k.k.Evidences.Get(sdkCtx, decodedHash) to capture the error (e.g.,
evidence, err := k.k.Evidences.Get(...)), check if err != nil and if
errors.Is(err, collections.ErrNotFound) return status.Errorf(codes.NotFound,
...), otherwise return status.Errorf(codes.Internal, "failed to get evidence:
%v", err); keep the existing nil-evidence check but only treat it as NotFound
when the collection error indicates absence.
x/evidence/keeper/keeper.go-128-134 (1)

128-134: ⚠️ Potential issue | 🟠 Major

Data race on lazy meter initialization.

The Meter() method has a race condition when accessed concurrently. Multiple goroutines can simultaneously observe k.meter == nil, causing redundant SubMeter allocations and potential data races on field assignment.

This pattern is systemic—identical implementations exist across 22 keepers in the codebase (bank, auth, staking, gov, feegrant, slashing, etc.). If fixing this in evidence keeper, consider applying the same fix across all affected keepers.

Proposed fix using sync.Once

Add a sync.Once field to Keeper:

 type Keeper struct {
+	meterOnce      sync.Once
 	meter          metrics.Meter
 	cdc            codec.BinaryCodec

Update the Meter method:

 func (k *Keeper) Meter(ctx context.Context) metrics.Meter {
-	if k.meter == nil {
-		k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(types.ModuleName, metrics.Tag("svc", types.ModuleName))
-	}
-
+	k.meterOnce.Do(func() {
+		k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(types.ModuleName, metrics.Tag("svc", types.ModuleName))
+	})
 	return k.meter
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/evidence/keeper/keeper.go` around lines 128 - 134, The Meter method has a
race on lazy initialization of k.meter; add a sync.Once field (e.g., meterOnce
sync.Once) to the Keeper struct and change Meter(ctx context.Context) to
initialize k.meter inside meterOnce.Do(func() { ... }) so only one goroutine
calls sdk.UnwrapSDKContext(ctx).Meter().SubMeter(...) and assigns k.meter;
update evidence Keeper and apply the same pattern to other keepers with
identical lazy initialization (bank, auth, staking, gov, feegrant, slashing,
etc.).
x/circuit/keeper/keeper.go-18-19 (1)

18-19: ⚠️ Potential issue | 🟠 Major

Meter() has a race-prone lazy init on shared keeper state.
k.meter is written without synchronization on lines 85–86. Concurrent access from query handlers, message handlers, and genesis operations can race. Use sync.Once to safely initialize.

Suggested fix
 import (
 	context "context"
+	"sync"
@@
 type Keeper struct {
 	meter        metrics.Meter
+	meterOnce    sync.Once
 	cdc          codec.BinaryCodec
@@
 func (k *Keeper) Meter(ctx context.Context) metrics.Meter {
-	if k.meter == nil {
-		k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(types.ModuleName, metrics.Tag("svc", types.ModuleName))
-	}
+	k.meterOnce.Do(func() {
+		k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(types.ModuleName, metrics.Tag("svc", types.ModuleName))
+	})
 
 	return k.meter
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/circuit/keeper/keeper.go` around lines 18 - 19, The Meter() method
currently lazily assigns k.meter without synchronization, causing races; add a
sync.Once field (e.g., meterOnce sync.Once) to the keeper struct and change
Meter() to initialize k.meter inside meterOnce.Do(func(){ ... }) so the
assignment to k.meter happens exactly once; also add the sync import and keep
the rest of Meter() behavior the same (returning k.meter) to ensure thread-safe
lazy initialization.
x/feegrant/keeper/msg_server.go-47-48 (1)

47-48: ⚠️ Potential issue | 🟠 Major

Don't ignore GetAllowance failures.

Line 47 drops the read error and treats it as "no allowance". If that lookup fails because the stored grant is unreadable, this handler can still proceed as if nothing exists.

Suggested fix
-	if f, _ := k.GetAllowance(sdkCtx, granter, grantee); f != nil {
+	if f, err := k.GetAllowance(sdkCtx, granter, grantee); err != nil {
+		return nil, err
+	} else if f != nil {
 		return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "fee allowance already exists")
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/feegrant/keeper/msg_server.go` around lines 47 - 48, Replace the
ignored-error call to GetAllowance with an explicit check: call f, err :=
k.GetAllowance(sdkCtx, granter, grantee); if err != nil return nil,
errorsmod.Wrap(err, "failed to read existing allowance") to propagate
storage/read errors, then if f != nil return nil,
errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "fee allowance already exists");
this ensures GetAllowance failures are not treated as “no allowance” and are
surfaced correctly.
x/staking/keeper/delegation.go-947-954 (1)

947-954: ⚠️ Potential issue | 🟠 Major

Bug: currTime parameter is ignored, using sdkCtx.HeaderInfo().Time instead.

The function accepts currTime time.Time as a parameter but line 954 passes sdkCtx.HeaderInfo().Time to RedelegationQueueIterator instead of the provided currTime. This could cause incorrect behavior if a caller passes a different time value.

🐛 Proposed fix
 	// gets an iterator for all timeslices from time 0 until the current Blockheader time
-	redelegationTimesliceIterator, err := k.RedelegationQueueIterator(sdkCtx, sdkCtx.HeaderInfo().Time)
+	redelegationTimesliceIterator, err := k.RedelegationQueueIterator(sdkCtx, currTime)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/staking/keeper/delegation.go` around lines 947 - 954, The function
DequeueAllMatureRedelegationQueue currently ignores its currTime parameter and
calls RedelegationQueueIterator with sdkCtx.HeaderInfo().Time; change the call
to use the provided currTime so the iterator covers times up to the passed-in
value (i.e., replace sdkCtx.HeaderInfo().Time with currTime when invoking
RedelegationQueueIterator and ensure any subsequent logic that relies on the
iteration uses currTime consistently).
x/distribution/keeper/keeper.go-237-243 (1)

237-243: ⚠️ Potential issue | 🟠 Major

Race condition in lazy initialization of meter field.

The Meter method has a check-then-write race condition. If multiple goroutines call Meter() concurrently when k.meter == nil, they can both pass the nil check and create separate meter instances. While this may not cause crashes, it leads to inconsistent meter state.

Consider using sync.Once for thread-safe initialization:

🔒 Proposed fix using sync.Once

Add a sync.Once field to the Keeper struct:

 type Keeper struct {
     // ... existing fields ...
     meter metrics.Meter
+    meterOnce sync.Once
 }

Then update the Meter method:

 func (k *Keeper) Meter(ctx context.Context) metrics.Meter {
-    if k.meter == nil {
-        k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(types.ModuleName, metrics.Tag("svc", types.ModuleName))
-    }
-
-    return k.meter
+    k.meterOnce.Do(func() {
+        k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(types.ModuleName, metrics.Tag("svc", types.ModuleName))
+    })
+    return k.meter
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/distribution/keeper/keeper.go` around lines 237 - 243, The lazy
initialization of k.meter in Keeper.Meter has a race: multiple goroutines can
pass the nil check and create separate meters; add a sync.Once field (e.g.,
meterOnce) to the Keeper struct and move the initialization into meterOnce.Do to
ensure the creation of k.meter happens exactly once; update Meter(ctx
context.Context) to call k.meterOnce.Do(func(){ k.meter =
sdk.UnwrapSDKContext(ctx).Meter().SubMeter(types.ModuleName, metrics.Tag("svc",
types.ModuleName)) }) and then return k.meter.
x/bank/keeper/msg_server.go-38-42 (1)

38-42: ⚠️ Potential issue | 🟠 Major

NewMsgServerImpl advertises a Keeper interface but all four message handlers require the concrete BaseKeeper type.

The constructor accepts keeper Keeper at the API level, yet Send, MultiSend, UpdateParams, and SetSendEnabled each assert k.Keeper.(BaseKeeper) and fail at runtime if the dynamic type is not BaseKeeper. This breaks the interface contract: any implementation of Keeper other than BaseKeeper will be rejected at runtime instead of at the type-check level.

Either tighten the constructor and field types to BaseKeeper to make the requirement explicit, or expose the required metering and address-codec methods on the Keeper interface instead of asserting the concrete type.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/bank/keeper/msg_server.go` around lines 38 - 42, NewMsgServerImpl's
constructor and its msg server type take a generic Keeper but every handler
(Send, MultiSend, UpdateParams, SetSendEnabled) asserts k.Keeper.(BaseKeeper),
causing runtime failures; either change the API to require BaseKeeper explicitly
or add the needed methods to the Keeper interface. Fix by updating
NewMsgServerImpl to accept and store BaseKeeper (replace Keeper with BaseKeeper
in the constructor and the msg server struct) so the metering and address-codec
methods are available without type assertions, or alternatively extend the
Keeper interface to declare the metering and address codec methods used by
base.Meter(...) and other handlers and remove the concrete BaseKeeper
assertions. Ensure all references to k.Keeper and the constructor signature
(NewMsgServerImpl) and usages in Send, MultiSend, UpdateParams, SetSendEnabled
are updated accordingly.
x/feegrant/keeper/keeper.go-25-25 (1)

25-25: ⚠️ Potential issue | 🟠 Major

Value-receiver methods calling pointer-receiver Meter() break the lazy cache.

Line 421 implements a pointer-receiver Meter() that mutates k.meter at line 423, but all methods in this file are value receivers (func (k Keeper)). When a value-receiver method calls k.Meter(), the assignment mutates a temporary copy, so the sub-meter is recreated on every invocation instead of being cached. Either switch all metered keeper methods to pointer receivers, or make Meter stateless by deriving the sub-meter directly from sdkCtx without caching.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/feegrant/keeper/keeper.go` at line 25, The keeper currently caches a
sub-meter in the Keeper.meter field via the pointer-receiver Meter() (which
assigns k.meter), but all keeper methods in this file use value receivers (func
(k Keeper)), so calls to k.Meter() mutate a copy and the cache is lost; to fix
either: (A) switch all keeper methods in this file from value receivers to
pointer receivers (change func (k Keeper) ... to func (k *Keeper) ...) so
k.Meter() mutates the real Keeper and the cached k.meter is reused, or (B) make
Meter() stateless by removing the k.meter cache and deriving/returning the
sub-meter directly from the sdk.Context (or relevant sdkCtx) inside Meter() so
it no longer mutates Keeper; update references to Meter(), Keeper.meter, and any
colocated methods accordingly.
x/slashing/keeper/keeper.go-21-21 (1)

21-21: ⚠️ Potential issue | 🟠 Major

The lazy meter cache is ineffective with value receivers calling pointer receiver method.

Value receiver methods (Logger, AddPubkey, GetPubkey, Slash, SlashWithInfractionReason, Jail, deleteAddrPubkeyRelation) call k.Meter(), which is a pointer receiver. Each value receiver call creates a struct copy, so the pointer receiver's lazy initialization writes to the copy's meter field, which is discarded after the call. On the next invocation, a new copy is created and the cache is rebuilt. Either make all keeper methods pointer receivers or drop the struct-level cache.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/slashing/keeper/keeper.go` at line 21, The lazy meter cache on the keeper
is broken because methods like Logger, AddPubkey, GetPubkey, Slash,
SlashWithInfractionReason, Jail, and deleteAddrPubkeyRelation use value
receivers but call the pointer-receiver Meter(), causing the cached meter to be
written into a copy; fix by making these methods use pointer receivers (change
their receiver from k keeper to k *keeper) so Meter() writes to the real struct,
or alternatively remove the struct-level meter cache and compute/return the
meter directly in Meter(); update all affected method signatures (Logger,
AddPubkey, GetPubkey, Slash, SlashWithInfractionReason, Jail,
deleteAddrPubkeyRelation) and any call sites accordingly to eliminate the copy
bug.
x/gov/keeper/msg_server.go-314-317 (1)

314-317: ⚠️ Potential issue | 🟠 Major

Use distinct timing names for the legacy wrappers.

These methods immediately delegate to the v1 server, and the keeper-backed v1 implementation already records SubmitProposal, Vote, VoteWeighted, and Deposit. Reusing the same metric names here double-counts every legacy request and skews latency/throughput dashboards.

Suggested direction
-	defer k.keeper.Meter(sdkCtx).FuncTiming(&sdkCtx, "SubmitProposal")()
+	defer k.keeper.Meter(sdkCtx).FuncTiming(&sdkCtx, "LegacySubmitProposal")()

-	defer k.keeper.Meter(sdkCtx).FuncTiming(&sdkCtx, "Vote")()
+	defer k.keeper.Meter(sdkCtx).FuncTiming(&sdkCtx, "LegacyVote")()

-	defer k.keeper.Meter(sdkCtx).FuncTiming(&sdkCtx, "VoteWeighted")()
+	defer k.keeper.Meter(sdkCtx).FuncTiming(&sdkCtx, "LegacyVoteWeighted")()

-	defer k.keeper.Meter(sdkCtx).FuncTiming(&sdkCtx, "Deposit")()
+	defer k.keeper.Meter(sdkCtx).FuncTiming(&sdkCtx, "LegacyDeposit")()

Also applies to: 355-358, 370-373, 393-396

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/gov/keeper/msg_server.go` around lines 314 - 317, The legacy wrapper
methods (legacyMsgServer methods SubmitProposal, Vote, VoteWeighted, Deposit)
are reusing the same metric names as the keeper-backed v1 implementations
causing double-counting; change the timing metric names in the legacy wrappers
to distinct identifiers (for example prefix with "legacy_" or "_legacy") when
calling k.keeper.Meter(sdkCtx).FuncTiming(&sdkCtx, ...) so the keeper-backed v1
metrics remain unchanged and dashboards are not skewed; update each legacy
method (legacyMsgServer.SubmitProposal, legacyMsgServer.Vote,
legacyMsgServer.VoteWeighted, legacyMsgServer.Deposit) to use the new unique
timing string.
x/upgrade/keeper/keeper.go-641-646 (1)

641-646: ⚠️ Potential issue | 🟠 Major

Don't memoize meter on a copied keeper.

Meter caches on *Keeper, but most instrumented methods in this file use func (k Keeper) receivers. In Go that means k.Meter(...) writes the cache to the local copy, so the sub-meter is rebuilt on every call. The first lazy init can also race if multiple goroutines call Meter() concurrently on the same keeper. Either move the instrumented methods to pointer receivers or stop storing the meter on the keeper.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/upgrade/keeper/keeper.go` around lines 641 - 646, The Keeper.Meter method
currently memoizes the meter on k.meter which is unsafe because many
instrumented methods use value receivers (func (k Keeper)) so the cache is
stored on copies and can race; fix by removing the cached field usage: make
Meter(ctx context.Context) return
sdk.UnwrapSDKContext(ctx).Meter().SubMeter(types.ModuleName, metrics.Tag("svc",
types.ModuleName)) unconditionally (i.e., do not assign to k.meter), or
alternatively convert the instrumented methods to pointer receivers and keep
k.meter with proper synchronization; update references to Meter, the k.meter
field, and any funcs with receivers (methods declared as func (k Keeper))
accordingly.
🟡 Minor comments (1)
x/nft/keeper/nft_batch.go-96-98 (1)

96-98: ⚠️ Potential issue | 🟡 Minor

Potentially misleading error wrapping.

When transferWithNoCheck fails, wrapping the error with nft.ErrNFTNotExists may mask the actual transfer failure reason. The NFT existence is already verified on line 93, so a transfer failure would be due to a different issue.

Consider returning the original error or using a more appropriate error type:

Proposed fix
-		if err := k.transferWithNoCheck(sdkCtx, classID, nftID, receiver); err != nil {
-			return errors.Wrap(nft.ErrNFTNotExists, nftID)
-		}
+		if err := k.transferWithNoCheck(sdkCtx, classID, nftID, receiver); err != nil {
+			return err
+		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/nft/keeper/nft_batch.go` around lines 96 - 98, The current error handling
in nft_batch.go wraps any error from transferWithNoCheck(sdkCtx, classID, nftID,
receiver) with nft.ErrNFTNotExists which can mask the real cause; instead,
return the original err or wrap the returned err with a contextual message
(e.g., errors.Wrap(err, "transferWithNoCheck failed") or fmt.Errorf("transfer
failed: %w", err)) so the true failure from transferWithNoCheck is preserved;
update the return in the transferWithNoCheck error branch to propagate or
appropriately wrap the actual err rather than nft.ErrNFTNotExists.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 727af76a-19ac-44a8-a8e6-942f1b434dd6

📥 Commits

Reviewing files that changed from the base of the PR and between c09c5bf and bf689f2.

⛔ Files ignored due to path filters (9)
  • go.sum is excluded by !**/*.sum
  • simapp/go.sum is excluded by !**/*.sum
  • store/go.sum is excluded by !**/*.sum
  • tests/go.sum is excluded by !**/*.sum
  • x/circuit/go.sum is excluded by !**/*.sum
  • x/evidence/go.sum is excluded by !**/*.sum
  • x/feegrant/go.sum is excluded by !**/*.sum
  • x/nft/go.sum is excluded by !**/*.sum
  • x/upgrade/go.sum is excluded by !**/*.sum
📒 Files selected for processing (121)
  • baseapp/abci.go
  • go.mod
  • server/start.go
  • simapp/go.mod
  • store/go.mod
  • tests/go.mod
  • tests/integration/gov/keeper/keeper_test.go
  • x/auth/keeper/account.go
  • x/auth/keeper/genesis.go
  • x/auth/keeper/grpc_query.go
  • x/auth/keeper/keeper.go
  • x/auth/keeper/msg_server.go
  • x/authz/keeper/genesis.go
  • x/authz/keeper/grpc_query.go
  • x/authz/keeper/keeper.go
  • x/authz/keeper/msg_server.go
  • x/bank/keeper/genesis.go
  • x/bank/keeper/grpc_query.go
  • x/bank/keeper/keeper.go
  • x/bank/keeper/msg_server.go
  • x/bank/keeper/send.go
  • x/bank/keeper/transient_balances.go
  • x/bank/keeper/view.go
  • x/bank/keeper/virtual_balances.go
  • x/circuit/go.mod
  • x/circuit/keeper/genesis.go
  • x/circuit/keeper/keeper.go
  • x/circuit/keeper/msg_server.go
  • x/circuit/keeper/query.go
  • x/consensus/keeper/keeper.go
  • x/crisis/keeper/genesis.go
  • x/crisis/keeper/keeper.go
  • x/crisis/keeper/msg_server.go
  • x/distribution/keeper/alias_functions.go
  • x/distribution/keeper/allocation.go
  • x/distribution/keeper/delegation.go
  • x/distribution/keeper/fee_pool.go
  • x/distribution/keeper/genesis.go
  • x/distribution/keeper/grpc_query.go
  • x/distribution/keeper/hooks.go
  • x/distribution/keeper/keeper.go
  • x/distribution/keeper/msg_server.go
  • x/distribution/keeper/params.go
  • x/distribution/keeper/store.go
  • x/distribution/keeper/validator.go
  • x/evidence/go.mod
  • x/evidence/keeper/abci.go
  • x/evidence/keeper/grpc_query.go
  • x/evidence/keeper/infraction.go
  • x/evidence/keeper/keeper.go
  • x/evidence/keeper/msg_server.go
  • x/feegrant/go.mod
  • x/feegrant/keeper/grpc_query.go
  • x/feegrant/keeper/keeper.go
  • x/feegrant/keeper/migrations.go
  • x/feegrant/keeper/msg_server.go
  • x/gov/keeper/deposit.go
  • x/gov/keeper/grpc_query.go
  • x/gov/keeper/keeper.go
  • x/gov/keeper/keeper_test.go
  • x/gov/keeper/migrations.go
  • x/gov/keeper/msg_server.go
  • x/gov/keeper/proposal.go
  • x/gov/keeper/tally.go
  • x/gov/keeper/vote.go
  • x/gov/module.go
  • x/group/keeper/genesis.go
  • x/group/keeper/grpc_query.go
  • x/group/keeper/keeper.go
  • x/group/keeper/migrations.go
  • x/group/keeper/msg_server.go
  • x/group/keeper/proposal_executor.go
  • x/group/keeper/tally.go
  • x/mint/keeper/genesis.go
  • x/mint/keeper/grpc_query.go
  • x/mint/keeper/keeper.go
  • x/mint/keeper/migrator.go
  • x/mint/keeper/msg_server.go
  • x/nft/go.mod
  • x/nft/keeper/class.go
  • x/nft/keeper/genesis.go
  • x/nft/keeper/grpc_query.go
  • x/nft/keeper/keeper.go
  • x/nft/keeper/msg_server.go
  • x/nft/keeper/nft.go
  • x/nft/keeper/nft_batch.go
  • x/params/keeper/grpc_query.go
  • x/params/keeper/keeper.go
  • x/slashing/keeper/genesis.go
  • x/slashing/keeper/grpc_query.go
  • x/slashing/keeper/hooks.go
  • x/slashing/keeper/infractions.go
  • x/slashing/keeper/keeper.go
  • x/slashing/keeper/migrations.go
  • x/slashing/keeper/msg_server.go
  • x/slashing/keeper/params.go
  • x/slashing/keeper/signing_info.go
  • x/slashing/keeper/unjail.go
  • x/staking/keeper/abci.go
  • x/staking/keeper/alias_functions.go
  • x/staking/keeper/delegation.go
  • x/staking/keeper/delegation_receiver.go
  • x/staking/keeper/genesis.go
  • x/staking/keeper/grpc_query.go
  • x/staking/keeper/historical_info.go
  • x/staking/keeper/keeper.go
  • x/staking/keeper/migrations.go
  • x/staking/keeper/msg_server.go
  • x/staking/keeper/params.go
  • x/staking/keeper/pool.go
  • x/staking/keeper/power_reduction.go
  • x/staking/keeper/query_utils.go
  • x/staking/keeper/slash.go
  • x/staking/keeper/unbonding.go
  • x/staking/keeper/val_state_change.go
  • x/staking/keeper/validator.go
  • x/upgrade/go.mod
  • x/upgrade/keeper/grpc_query.go
  • x/upgrade/keeper/keeper.go
  • x/upgrade/keeper/migrations.go
  • x/upgrade/keeper/msg_server.go

golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8
google.golang.org/grpc v1.71.0
google.golang.org/protobuf v1.36.5
google.golang.org/grpc v1.75.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

❓ Verification inconclusive

Script executed:

#!/bin/bash
set -euo pipefail

echo "OSV query for google.golang.org/grpc@v1.75.0"
curl -sS https://api.osv.dev/v1/query \
  -H 'Content-Type: application/json' \
  -d '{"package":{"name":"google.golang.org/grpc","ecosystem":"Go"},"version":"1.75.0"}'

echo
echo "Latest published grpc version from Go proxy"
curl -sS https://proxy.golang.org/google.golang.org/grpc/@latest

Repository: InjectiveLabs/cosmos-sdk


Repository: InjectiveLabs/cosmos-sdk
Exit code: 0

stdout:

OSV query for google.golang.org/grpc@v1.75.0
{"vulns":[{"id":"GHSA-p77j-4mvh-x3m3","summary":"gRPC-Go has an authorization bypass via missing leading slash in :path","details":"### Impact\n_What kind of vulnerability is it? Who is impacted?_\n\nIt is an **Authorization Bypass** resulting from **Improper Input Validation** of the HTTP/2 `:path` pseudo-header.\n\nThe gRPC-Go server was too lenient in its routing logic, accepting requests where the `:path` omitted the mandatory leading slash (e.g., `Service/Method` instead of `/Service/Method`). While the server successfully routed these requests to the correct handler, authorization interceptors (including the official `grpc/authz` package) evaluated the raw, non-canonical path string. Consequently, \"deny\" rules defined using canonical paths (starting with `/`) failed to match the incoming request, allowing it to bypass the policy if a fallback \"allow\" rule was present.\n\n**Who is impacted?**\nThis affects gRPC-Go servers that meet both of the following criteria:\n1. They use path-based authorization interceptors, such as the official RBAC implementation in `google.golang.org/grpc/authz` or custom interceptors relying on `info.FullMethod` or `grpc.Method(ctx)`.\n2. Their security policy contains specific \"deny\" rules for canonical paths but allows other requests by default (a fallback \"allow\" rule).\n\nThe vulnerability is exploitable by an attacker who can send raw HTTP/2 frames with malformed `:path` headers directly to the gRPC server.\n\n### Patches\n_Has the problem been patched? What versions should users upgrade to?_\n\nYes, the issue has been patched. The fix ensures that any request with a `:path` that does not start with a leading slash is immediately rejected with a `codes.Unimplemented` error, preventing it from reaching authorization interceptors or handlers with a non-canonical path string.\n\nUsers should upgrade to the following versions (or newer):\n* **v1.79.3**\n* The latest **master** branch.\n\nIt is recommended that all users employing path-based authorization (especially `grpc/authz`) upgrade as soon as the patch is available in a tagged release.\n\n### Workarounds\n_Is there a way for users to fix or remediate the vulnerability without upgrading?_\n\nWhile upgrading is the most secure and recommended path, users can mitigate the vulnerability using one of the following methods:\n\n#### 1. Use a Validating Interceptor (Recommended Mitigation)\nAdd an \"outermost\" interceptor to your server that validates the path before any other authorization logic runs:\n\n```go\nfunc pathValidationInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {\n    if info.FullMethod == \"\" || info.FullMethod[0] != '/' {\n        return nil, status.Errorf(codes.Unimplemented, \"malformed method name\")\n    }   \n    return handler(ctx, req)\n}\n\n// Ensure this is the FIRST interceptor in your chain\ns := grpc.NewServer(\n    grpc.ChainUnaryInterceptor(pathValidationInterceptor, authzInterceptor),\n)\n```\n\n#### 2. Infrastructure-Level Normalization\nIf your gRPC server is behind a reverse proxy or load balancer (such as Envoy, NGINX, or an L7 Cloud Load Balancer), ensure it is configured to enforce strict HTTP/2 compliance for pseudo-headers and reject or normalize requests where the `:path` header does not start with a leading slash.\n\n#### 3. Policy Hardening\nSwitch to a \"default deny\" posture in your authorization policies (explicitly listing all allowed paths and denying everything else) to reduce the risk of bypasses via malformed inputs.","aliases":["CVE-2026-33186","GO-2026-4762"],"modified":"2026-03-27T19:11:22.629776Z","published":"2026-03-18T20:10:29Z","related":["CGA-8qwg-3gv7-2fp3"],"database_specific":{"cwe_ids":["CWE-285"],"github_reviewed_at":"2026-03-18T20:10:29Z","github_reviewed":true,"nvd_published_at":"2026-03-20T23:16:45Z","severity":"CRITICAL"},"references":[{"type":"WEB","url":"https://github.com/grpc/grpc-go/security/advisories/GHSA-p77j-4mvh-x3m3"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-33186"},{"type":"PACKAGE","url":"https://github.com/grpc/grpc-go"}],"affected":[{"package":{"name":"google.golang.org/grpc","ecosystem":"Go","purl":"pkg:golang/google.golang.org/grpc"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"1.79.3"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-p77j-4mvh-x3m3/GHSA-p77j-4mvh-x3m3.json"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N"}]},{"id":"GO-2026-4762","summary":"Authorization bypass in gRPC-Go via missing leading slash in :path in google.golang.org/grpc","details":"Authorization bypass in gRPC-Go via missing leading slash in :path in google.golang.org/grpc","aliases":["CVE-2026-33186","GHSA-p77j-4mvh-x3m3"],"modified":"2026-03-27T19:00:25.589382Z","published":"2026-03-27T18:39:44Z","database_specific":{"url":"https://pkg.go.dev/vuln/GO-2026-4762","review_status":"REVIEWED"},"references":[{"type":"ADVISORY","url":"https://github.com/grpc/grpc-go/security/advisories/GHSA-p77j-4mvh-x3m3"}],"affected":[{"package":{"name":"google.golang.org/grpc","ecosystem":"Go","purl":"pkg:golang/google.golang.org/grpc"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"1.79.3"}]}],"ecosystem_specific":{"imports":[{"symbols":["Server.Serve","Server.ServeHTTP","Server.handleStream"],"path":"google.golang.org/grpc"}]},"database_specific":{"source":"https://vuln.go.dev/ID/GO-2026-4762.json"}}],"schema_version":"1.7.5"}]}
Latest published grpc version from Go proxy
{"Version":"v1.79.3","Time":"2026-03-17T23:13:23Z","Origin":{"VCS":"git","URL":"https://github.com/grpc/grpc-go","Hash":"dda86dbd9cecb8b35b58c73d507d81d67761205f","Ref":"refs/tags/v1.79.3"}}

Blocker: google.golang.org/grpc v1.75.0 contains a critical authorization bypass vulnerability (GHSA-p77j-4mvh-x3m3).

This dependency must be bumped to v1.79.3 or later before merge. The vulnerability is an authorization bypass resulting from improper validation of the HTTP/2 :path pseudo-header and affects gRPC servers with path-based authorization interceptors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@store/go.mod` at line 24, Replace the vulnerable dependency entry
"google.golang.org/grpc v1.75.0" with a secure release (at least v1.79.3), e.g.
update the module version to "google.golang.org/grpc v1.79.3"; then run the Go
module update commands (go get google.golang.org/grpc@v1.79.3 and go mod tidy)
to regenerate go.sum and ensure the project builds and tests pass. Ensure the
final go.mod no longer references v1.75.0 anywhere and commit the updated go.mod
and go.sum.

google.golang.org/grpc v1.71.0
github.com/stretchr/testify v1.11.1
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5
google.golang.org/grpc v1.75.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and examine its contents around the specified lines
find . -name "go.mod" -path "*/circuit/*" -type f

Repository: InjectiveLabs/cosmos-sdk

Length of output: 86


🏁 Script executed:

# Check the specific lines mentioned in the review
if [ -f "x/circuit/go.mod" ]; then
  echo "=== Line 22 (grpc) ==="
  sed -n '20,24p' "x/circuit/go.mod"
  echo ""
  echo "=== Line 159 (otel/sdk) ==="
  sed -n '157,161p' "x/circuit/go.mod"
fi

Repository: InjectiveLabs/cosmos-sdk

Length of output: 566


🌐 Web query:

GHSA-p77j-4mvh-x3m3 google.golang.org/grpc v1.75.0

💡 Result:

GHSA-p77j-4mvh-x3m3 (CVE-2026-33186) is a critical authorization bypass vulnerability (CVSS 9.1) in google.golang.org/grpc, affecting versions before 1.79.3, including v1.75.0 (released around August 2025). It allows attackers to bypass path-based authorization (e.g., grpc/authz deny rules) by sending HTTP/2 requests with malformed :path headers missing the leading slash (e.g., "Service/Method" instead of "/Service/Method"). The server routes these to handlers but interceptors see the non-canonical path, failing to match deny rules with a fallback allow. Patched in v1.79.3 (March 17, 2026), which rejects such requests with codes.Unimplemented. Upgrade to v1.79.3 or later. Mitigations: validating interceptors, strict HTTP/2 proxies, or method-based auth.

Citations:


🌐 Web query:

GHSA-9h8m-3fm2-qjrq go.opentelemetry.io/otel v1.38.0

💡 Result:

GHSA-9h8m-3fm2-qjrq is a high-severity vulnerability (CVSS 7.0) in the OpenTelemetry Go SDK, specifically affecting go.opentelemetry.io/otel/sdk/resource on macOS/Darwin systems. It allows arbitrary code execution via local PATH hijacking. The resource detection code in sdk/resource/host_id.go executes the 'ioreg' system command without a secure PATH, enabling an attacker who can modify the PATH environment variable to execute malicious code in the application's context. Affected versions: >=1.21.0 to <=1.39.0 (including v1.38.0). Patched in v1.40.0 via commit d45961b. Aliases: CVE-2026-24051, GO-2026-4394. CWE-426 (Untrusted Search Path). Published February 2, 2026.

Citations:


Upgrade google.golang.org/grpc to v1.79.3+ and go.opentelemetry.io/otel/sdk to v1.40.0+.

Both versions contain known vulnerabilities:

  • google.golang.org/grpc v1.75.0 is vulnerable to GHSA-p77j-4mvh-x3m3 (CVE-2026-33186, CVSS 9.1)—a critical authorization bypass via malformed HTTP/2 :path headers that bypasses authz deny rules. Fixed in v1.79.3.
  • go.opentelemetry.io/otel/sdk v1.38.0 is vulnerable to GHSA-9h8m-3fm2-qjrq (CVE-2026-24051, CVSS 7.0)—arbitrary code execution via local PATH hijacking on macOS. Fixed in v1.40.0.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/circuit/go.mod` at line 22, Update the vulnerable module versions in
go.mod: change the google.golang.org/grpc dependency to v1.79.3 (or a newer
compatible release) and change go.opentelemetry.io/otel/sdk to v1.40.0 (or
newer); locate these by the module path strings "google.golang.org/grpc" and
"go.opentelemetry.io/otel/sdk" in go.mod, update their version tags, then run go
get ./... (or go get google.golang.org/grpc@v1.79.3
go.opentelemetry.io/otel/sdk@v1.40.0) and go mod tidy to update the lockfile and
resolve transitive changes, then run the test suite/build to verify nothing
broke.

Comment on lines +25 to +27
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5
google.golang.org/grpc v1.75.0
google.golang.org/protobuf v1.36.8
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, check if x/evidence/go.mod exists and view lines 25-27
if [ -f "x/evidence/go.mod" ]; then
  echo "=== x/evidence/go.mod lines 20-30 ==="
  sed -n '20,30p' x/evidence/go.mod
  echo ""
  echo "=== x/evidence/go.mod lines 150-165 ==="
  sed -n '150,165p' x/evidence/go.mod
else
  echo "x/evidence/go.mod not found"
  git ls-files | grep -i "go.mod"
fi

Repository: InjectiveLabs/cosmos-sdk

Length of output: 1351


🏁 Script executed:

# Search for any go.mod files to understand structure
fd -name "go.mod" -type f

Repository: InjectiveLabs/cosmos-sdk

Length of output: 298


🌐 Web query:

GHSA-p77j-4mvh-x3m3 google.golang.org/grpc vulnerability

💡 Result:

GHSA-p77j-4mvh-x3m3 is a critical authorization bypass vulnerability (CVE-2026-33186) in google.golang.org/grpc (gRPC-Go), affecting versions prior to 1.79.3. It results from improper input validation of the HTTP/2 :path pseudo-header, where the server accepts requests missing the leading slash (e.g., "Service/Method" instead of "/Service/Method"). This allows bypassing path-based authorization interceptors like those in grpc/authz if they have deny rules for canonical paths but a fallback allow rule. CVSS score: 9.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N). CWE-285 (Improper Authorization). Published around March 17-20, 2026. Fixed in v1.79.3 by rejecting invalid :path headers with codes.Unimplemented. Impacts gRPC-Go servers using path-based auth interceptors. Upgrade to v1.79.3 or later.

Citations:


🌐 Web query:

GHSA-9h8m-3fm2-qjrq opentelemetry otel sdk vulnerability

💡 Result:

GHSA-9h8m-3fm2-qjrq is a high-severity vulnerability (CVSS 7.0) in the OpenTelemetry Go SDK (go.opentelemetry.io/otel/sdk/resource), affecting versions >=1.21.0 and <=1.39.0. It allows arbitrary code execution via local PATH hijacking on macOS/Darwin systems. The resource detection code in sdk/resource/host_id.go executes the 'ioreg' system command without a secure PATH, enabling an attacker with local PATH modification ability to execute arbitrary code in the application's context. Patched in v1.40.0 (commit d45961b). Also tracked as CVE-2026-24051 and GO-2026-4394. Published February 2, 2026.

Citations:


This module includes two critical dependency vulnerabilities requiring immediate patching.

google.golang.org/grpc v1.75.0 is vulnerable to GHSA-p77j-4mvh-x3m3 (authorization bypass, CVE-2026-33186, CVSS 9.1) and requires upgrade to v1.79.3 or later. go.opentelemetry.io/otel/sdk v1.38.0 is vulnerable to GHSA-9h8m-3fm2-qjrq (arbitrary code execution via PATH hijacking, CVE-2026-24051, CVSS 7.0) and requires upgrade to v1.40.0 or later. Upgrade both dependencies before merging.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/evidence/go.mod` around lines 25 - 27, The go.mod currently pins vulnerable
versions; update the listed modules to secure releases: bump
google.golang.org/grpc to v1.79.3 or later and go.opentelemetry.io/otel/sdk to
v1.40.0 or later, then run `go get`/`go mod tidy` to update go.sum and ensure no
other indirect deps force older versions; verify build and run tests to confirm
compatibility (look for occurrences of google.golang.org/grpc and
go.opentelemetry.io/otel/sdk to locate the entries to change).

google.golang.org/protobuf v1.36.5
github.com/stretchr/testify v1.11.1
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5
google.golang.org/grpc v1.75.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "go.mod" -path "*/feegrant/*" | head -5

Repository: InjectiveLabs/cosmos-sdk

Length of output: 87


🏁 Script executed:

cat -n x/feegrant/go.mod | sed -n '20,30p'

Repository: InjectiveLabs/cosmos-sdk

Length of output: 478


🏁 Script executed:

cat -n x/feegrant/go.mod | sed -n '170,178p'

Repository: InjectiveLabs/cosmos-sdk

Length of output: 676


🌐 Web query:

GHSA-p77j-4mvh-x3m3 google.golang.org/grpc vulnerability

💡 Result:

GHSA-p77j-4mvh-x3m3 is a critical authorization bypass vulnerability (CVE-2026-33186, CVSS 9.1) in google.golang.org/grpc (gRPC-Go) versions prior to 1.79.3. The gRPC-Go server improperly validates the HTTP/2 :path pseudo-header, accepting requests missing the mandatory leading slash (e.g., "Service/Method" instead of "/Service/Method"). While routing succeeds, authorization interceptors (like google.golang.org/grpc/authz) evaluate the raw path without the slash, bypassing path-based "deny" rules if a fallback "allow" exists. Impacts gRPC-Go servers using path-based authorization with explicit denies and default allows. Attackers with HTTP/2 access can send malformed :path headers to bypass restrictions. Fixed in v1.79.3 (released 2026-03-17) by rejecting non-canonical paths with codes.Unimplemented before interceptors (PR #8981). Upgrade to v1.79.3 or later. No known workarounds besides avoiding path-based auth or disabling HTTP/2.

Citations:


🌐 Web query:

GHSA-9h8m-3fm2-qjrq go.opentelemetry.io/otel/sdk vulnerability

💡 Result:

GHSA-9h8m-3fm2-qjrq is a high-severity vulnerability (CVSS 7.0) in the go.opentelemetry.io/otel/sdk package, also known as CVE-2026-24051 and GO-2026-4394. It affects versions from v1.21.0 (or v1.20.0 per some sources) up to but not including v1.40.0, specifically on macOS/Darwin systems. The vulnerability is due to untrusted search path (CWE-426) in the resource detection code at sdk/resource/host_id.go. It executes the 'ioreg' system command without a fully qualified path, allowing an attacker who can modify the PATH environment variable to hijack the command and achieve arbitrary code execution (ACE) in the application's context. Attack requires local access with low privileges but high attack complexity (AV:L/AC:H/PR:L). The issue was published on 2026-02-02 and patched in commit d45961b, released in v1.40.0 on the same day. Users should upgrade to v1.40.0 or later. As of 2026-03-30, later versions like v1.42.0 are available. Workarounds: Do not use resource detection on untrusted macOS environments or ensure PATH cannot be modified by untrusted parties.

Citations:


Upgrade blocked dependencies to patch known vulnerabilities.

google.golang.org/grpc v1.75.0 contains a critical authorization bypass vulnerability (GHSA-p77j-4mvh-x3m3, CVE-2026-33186, CVSS 9.1) that allows path-based authorization bypass in gRPC servers. Upgrade to v1.79.3 or later.

go.opentelemetry.io/otel/sdk v1.38.0 contains a high-severity code execution vulnerability (GHSA-9h8m-3fm2-qjrq, CVE-2026-24051, CVSS 7.0) on macOS due to unsafe command execution. Upgrade to v1.40.0 or later.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/feegrant/go.mod` at line 25, Update the vulnerable module versions in
go.mod: bump google.golang.org/grpc to v1.79.3 (or later) and
go.opentelemetry.io/otel/sdk to v1.40.0 (or later); then run go get
google.golang.org/grpc@v1.79.3 and go get go.opentelemetry.io/otel/sdk@v1.40.0
(or use your chosen newer versions) and run go mod tidy to update the lockfile
and ensure dependent packages are resolved; target the module names
google.golang.org/grpc and go.opentelemetry.io/otel/sdk in the file and verify
tests/build succeed.

Comment on lines +23 to +24
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5
google.golang.org/grpc v1.75.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n x/nft/go.mod | head -30

Repository: InjectiveLabs/cosmos-sdk

Length of output: 1294


🏁 Script executed:

cat -n x/nft/go.mod | sed -n '154,160p'

Repository: InjectiveLabs/cosmos-sdk

Length of output: 565


🏁 Script executed:

wc -l x/nft/go.mod

Repository: InjectiveLabs/cosmos-sdk

Length of output: 84


🌐 Web query:

GHSA-p77j-4mvh-x3m3 google.golang.org/grpc

💡 Result:

GHSA-p77j-4mvh-x3m3 is a critical authorization bypass vulnerability (CVE-2026-33186) in google.golang.org/grpc (gRPC-Go). Summary: The gRPC-Go server improperly validates the HTTP/2 :path pseudo-header, accepting requests without a leading slash (e.g., "Service/Method" instead of "/Service/Method"). This allows bypassing path-based authorization interceptors like those in grpc/authz, as they match on the raw non-canonical path. Affected: Versions < 1.79.3 of google.golang.org/grpc. Fixed: Upgrade to v1.79.3 or later. Severity: CVSS 3.1 9.1 (Critical) - AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N. CWE-285 (Improper Authorization). Impact: Affects servers using path-based auth interceptors with deny rules for canonical paths and fallback allow. Exploitable remotely by sending malformed HTTP/2 frames. Published: March 17-18, 2026. As of 2026-03-30, patched and recent. Mitigation: Upgrade immediately. Optionally, add custom unary interceptor to reject non-/ paths with codes.Unimplemented.

Citations:


🌐 Web query:

GHSA-9h8m-3fm2-qjrq go.opentelemetry.io/otel

💡 Result:

GHSA-9h8m-3fm2-qjrq is a high-severity security vulnerability (CVSS 7.0) in the OpenTelemetry Go SDK package go.opentelemetry.io/otel/sdk, affecting versions >=1.21.0 and <=1.39.0. It allows arbitrary code execution via local PATH hijacking on macOS/Darwin systems. The resource detection code in sdk/resource/host_id.go executes the 'ioreg' system command without a secure PATH, enabling an attacker with local access to modify PATH to achieve ACE in the application's context. Patched in v1.40.0 (commit d45961b). Aliases: CVE-2026-24051, GO-2026-4394. Published Feb 2, 2026.

Citations:


Resolve critical security vulnerabilities in dependencies.

This module pins versions with known high-severity and critical vulnerabilities that are flagged in dependency review:

  • google.golang.org/grpc v1.75.0 (GHSA-p77j-4mvh-x3m3): Critical authorization bypass—affects versions < 1.79.3. Upgrade to v1.79.3 or later.
  • go.opentelemetry.io/otel/sdk v1.38.0 (GHSA-9h8m-3fm2-qjrq): High-severity arbitrary code execution via local PATH hijacking—affects versions 1.21.0 to 1.39.0. Upgrade to v1.40.0 or later.

Both packages require bumping before merge (lines 24 and 154–160).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/nft/go.mod` around lines 23 - 24, Update the vulnerable dependency versions
in go.mod: bump google.golang.org/grpc from v1.75.0 to v1.79.3 (or later) and
bump go.opentelemetry.io/otel/sdk from v1.38.0 to v1.40.0 (or later); adjust any
other referenced otel modules in the same section (lines around the otel/sdk
block) to compatible versions, then run go get ./... or go get
<module>@<version> and go mod tidy and run tests/build to ensure no breakage.
Ensure the module names exactly match google.golang.org/grpc and
go.opentelemetry.io/otel/sdk so the dependency review picks up the fixed
versions.

Comment on lines +81 to +87
func (k *Keeper) Meter(ctx context.Context) metrics.Meter {
if k.meter == nil {
k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(proposal.ModuleName, metrics.Tag("svc", proposal.ModuleName))
}

return k.meter
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Data race on lazy meter initialization.

The Meter() method performs unsynchronized read-write on k.meter. Concurrent gRPC requests can race on the nil check and assignment, leading to undefined behavior.

Since keepers are typically long-lived and shared across goroutines handling concurrent requests, this needs synchronization.

🔒 Proposed fix using sync.Once
 import (
 	"context"
+	"sync"
 	"cosmossdk.io/log"
 	storetypes "cosmossdk.io/store/types"
 	metrics "github.com/InjectiveLabs/metrics/v2"
 ...
 
 type Keeper struct {
+	meterOnce   sync.Once
 	meter       metrics.Meter
 	cdc         codec.BinaryCodec
 ...

 func (k *Keeper) Meter(ctx context.Context) metrics.Meter {
-	if k.meter == nil {
-		k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(proposal.ModuleName, metrics.Tag("svc", proposal.ModuleName))
-	}
-
+	k.meterOnce.Do(func() {
+		k.meter = sdk.UnwrapSDKContext(ctx).Meter().SubMeter(proposal.ModuleName, metrics.Tag("svc", proposal.ModuleName))
+	})
 	return k.meter
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@x/params/keeper/keeper.go` around lines 81 - 87, The Meter method has a data
race: it reads and writes k.meter without synchronization (nil check +
assignment) causing races for concurrent gRPC requests; fix by making lazy
initialization thread-safe—add a sync.Once (or a mutex) field on Keeper (e.g.,
meterOnce or meterMu) and use it in Meter to ensure the SubMeter creation and
assignment to k.meter happens exactly once, while subsequent calls simply return
k.meter; reference the Meter method, k.meter, and Keeper type when applying this
change.

case comet.LightClientAttack, comet.DuplicateVote:
evidence := types.FromABCIEvidence(evidences.Get(i), k.stakingKeeper.ConsensusAddressCodec())
err := k.handleEquivocationEvidence(ctx, evidence)
err := k.handleEquivocationEvidence(sdkCtx, evidence)

Check warning

Code scanning / CodeQL

Panic in BeginBock or EndBlock consensus methods

path flow from Begin/EndBlock to a panic call path flow from Begin/EndBlock to a panic call path flow from Begin/EndBlock to a panic call path flow from Begin/EndBlock to a panic call path flow from Begin/EndBlock to a panic call

defer telemetry.ModuleMeasureSince(types.ModuleName, telemetry.Now(), telemetry.MetricKeyBeginBlocker)
return k.TrackHistoricalInfo(ctx)
return k.TrackHistoricalInfo(sdkCtx)

Check warning

Code scanning / CodeQL

Panic in BeginBock or EndBlock consensus methods

path flow from Begin/EndBlock to a panic call path flow from Begin/EndBlock to a panic call path flow from Begin/EndBlock to a panic call path flow from Begin/EndBlock to a panic call path flow from Begin/EndBlock to a panic call

defer telemetry.ModuleMeasureSince(types.ModuleName, telemetry.Now(), telemetry.MetricKeyEndBlocker)
return k.BlockValidatorUpdates(ctx)
return k.BlockValidatorUpdates(sdkCtx)

Check warning

Code scanning / CodeQL

Panic in BeginBock or EndBlock consensus methods

path flow from Begin/EndBlock to a panic call path flow from Begin/EndBlock to a panic call path flow from Begin/EndBlock to a panic call path flow from Begin/EndBlock to a panic call path flow from Begin/EndBlock to a panic call
@socket-security
Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn Critical
Critical CVE: HashiCorp go-getter Vulnerable to Argument Injection When Fetching Remote Default Git Branches in golang github.com/hashicorp/go-getter

CVE: GHSA-q64h-39hv-4cf7 HashiCorp go-getter Vulnerable to Argument Injection When Fetching Remote Default Git Branches (CRITICAL)

Affected versions: >= 1.5.9 < 1.7.4

Patched version: 1.7.4

From: ?golang/cosmossdk.io/x/upgrade@v0.0.0-20230614103911-b3da8bb4e801golang/github.com/hashicorp/go-getter@v1.7.1

ℹ Read more on: This package | This alert | What is a critical CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known critical CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore golang/github.com/hashicorp/go-getter@v1.7.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Critical
Critical CVE: Misuse of ServerConfig.PublicKeyCallback may cause authorization bypass in golang golang.org/x/crypto

CVE: GHSA-v778-237x-gjrc Misuse of ServerConfig.PublicKeyCallback may cause authorization bypass in golang.org/x/crypto (CRITICAL)

Affected versions: < 0.31.0

Patched version: 0.31.0

From: ?golang/cosmossdk.io/depinject@v1.0.0-alpha.4golang/cosmossdk.io/errors@v1.0.1golang/cosmossdk.io/errors@v1.0.1golang/cosmossdk.io/collections@v0.4.0golang/cosmossdk.io/core@v0.11.0golang/cosmossdk.io/core@v0.11.0golang/cosmossdk.io/api@v0.7.5golang/cosmossdk.io/x/feegrant@v0.1.1golang/github.com/spf13/viper@v1.19.0golang/cosmossdk.io/x/upgrade@v0.0.0-20230614103911-b3da8bb4e801golang/cosmossdk.io/x/evidence@v0.1.1golang/cosmossdk.io/x/upgrade@v0.1.4golang/github.com/cosmos/cosmos-sdk@v0.50.6golang/github.com/cosmos/cosmos-sdk@v0.50.6golang/cosmossdk.io/api@v0.7.4golang/cosmossdk.io/core@v0.11.1golang/cosmossdk.io/x/nft@v0.1.1golang/github.com/cosmos/cosmos-sdk@v0.50.8golang/github.com/cosmos/gogoproto@v1.7.0golang/github.com/cosmos/gogoproto@v1.7.0golang/cosmossdk.io/store@v1.1.1golang/cosmossdk.io/x/circuit@v0.1.1golang/cosmossdk.io/client/v2@v2.0.0-beta.5.0.20241121152743-3dad36d9a29egolang/github.com/grpc-ecosystem/grpc-gateway@v1.16.0golang/golang.org/x/crypto@v0.26.0

ℹ Read more on: This package | This alert | What is a critical CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known critical CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore golang/golang.org/x/crypto@v0.26.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@dbrajovic dbrajovic requested review from kakysha and maxim-inj March 31, 2026 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants