fix(provider): move MsgChangeRewardDenoms validation to msg server handler#2627
fix(provider): move MsgChangeRewardDenoms validation to msg server handler#2627Aboudjem wants to merge 1 commit intocosmos:mainfrom
Conversation
Greptile SummaryThis PR moves the Confidence Score: 5/5Safe to merge — the refactoring is correct, the panic fix is an improvement, and all remaining findings are non-blocking style/coverage suggestions. All P0/P1 behaviour is preserved: authority check still runs first, all three validation rules are enforced in the handler, and sdk.ValidateDenom is a strict superset of the old IsValid() denom check. The two findings (missing t.Run and a missing DenomsToRemove test case) are P2 quality-of-life improvements that do not affect correctness. msg_server_test.go — minor test-isolation and coverage gaps, no correctness impact.
|
| Filename | Overview |
|---|---|
| x/ccv/provider/keeper/msg_server.go | Validation logic (empty check, denom format via sdk.ValidateDenom, overlap check) correctly moved into handler before the keeper call; order with the authority check is preserved. |
| x/ccv/provider/types/msg.go | ValidateBasic stubbed to nil per cosmos-sdk v0.50 pattern; sdk.HasValidateBasic interface assertion retained; no import issues. |
| x/ccv/provider/keeper/msg_server_test.go | New TestChangeRewardDenomsValidation covers 4 paths but lacks t.Run isolation and a test for invalid denom in DenomsToRemove. |
| CHANGELOG.md | Entry correctly added under Unreleased > IMPROVEMENTS with issue reference. |
Sequence Diagram
sequenceDiagram
participant Client
participant MsgServer as ChangeRewardDenoms Handler
participant Keeper
Client->>MsgServer: MsgChangeRewardDenoms
MsgServer->>MsgServer: Check authority
alt authority mismatch
MsgServer-->>Client: ErrUnauthorized
end
MsgServer->>MsgServer: Check DenomsToAdd and DenomsToRemove not both empty
alt both empty
MsgServer-->>Client: ErrInvalidMsgChangeRewardDenoms
end
MsgServer->>MsgServer: sdk.ValidateDenom each denom in DenomsToAdd
alt invalid denom
MsgServer-->>Client: ErrInvalidMsgChangeRewardDenoms
end
MsgServer->>MsgServer: sdk.ValidateDenom each denom in DenomsToRemove + overlap check
alt invalid denom or overlap
MsgServer-->>Client: ErrInvalidMsgChangeRewardDenoms
end
MsgServer->>Keeper: ChangeRewardDenoms(ctx, add, remove)
Keeper-->>MsgServer: eventAttributes
MsgServer-->>Client: MsgChangeRewardDenomsResponse
Reviews (1): Last reviewed commit: "fix(provider): move MsgChangeRewardDenom..." | Re-trigger Greptile
| for _, tc := range testCases { | ||
| _, err := msgServer.ChangeRewardDenoms(ctx, tc.msg) | ||
| if tc.expectError { | ||
| require.Error(t, err, tc.name) | ||
| } else { | ||
| require.NoError(t, err, tc.name) | ||
| } | ||
| } |
There was a problem hiding this comment.
Test cases lack subtest isolation
Without t.Run, a require failure in any earlier test case halts the entire function, meaning later cases (including the success path) are silently skipped. This also means the -run flag and test output can't identify individual cases. Consider wrapping each case:
| for _, tc := range testCases { | |
| _, err := msgServer.ChangeRewardDenoms(ctx, tc.msg) | |
| if tc.expectError { | |
| require.Error(t, err, tc.name) | |
| } else { | |
| require.NoError(t, err, tc.name) | |
| } | |
| } | |
| for _, tc := range testCases { | |
| t.Run(tc.name, func(t *testing.T) { | |
| _, err := msgServer.ChangeRewardDenoms(ctx, tc.msg) | |
| if tc.expectError { | |
| require.Error(t, err) | |
| } else { | |
| require.NoError(t, err) | |
| } | |
| }) | |
| } |
| testCases := []struct { | ||
| name string | ||
| msg *providertypes.MsgChangeRewardDenoms | ||
| expectError bool | ||
| }{ | ||
| { | ||
| name: "both sets empty", | ||
| msg: &providertypes.MsgChangeRewardDenoms{Authority: authority}, | ||
| expectError: true, | ||
| }, | ||
| { | ||
| name: "invalid denom", | ||
| msg: &providertypes.MsgChangeRewardDenoms{ | ||
| Authority: authority, | ||
| DenomsToAdd: []string{"!!!bad"}, | ||
| }, | ||
| expectError: true, | ||
| }, | ||
| { | ||
| name: "denom in both add and remove", | ||
| msg: &providertypes.MsgChangeRewardDenoms{ | ||
| Authority: authority, | ||
| DenomsToAdd: []string{"uatom"}, | ||
| DenomsToRemove: []string{"uatom"}, | ||
| }, | ||
| expectError: true, | ||
| }, | ||
| { | ||
| name: "valid", | ||
| msg: &providertypes.MsgChangeRewardDenoms{ | ||
| Authority: authority, | ||
| DenomsToAdd: []string{"uatom"}, | ||
| }, | ||
| expectError: false, | ||
| }, | ||
| } |
There was a problem hiding this comment.
Missing test case for invalid denom in
DenomsToRemove
The "invalid denom" case only exercises the DenomsToAdd path. The symmetric DenomsToRemove validation branch (lines 123–126 of msg_server.go) has no dedicated test, leaving a gap in coverage. Consider adding:
{
name: "invalid denom in remove",
msg: &providertypes.MsgChangeRewardDenoms{
Authority: authority,
DenomsToRemove: []string{"!!!bad"},
},
expectError: true,
},Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Closes #2149
Moves the denom validation logic from
MsgChangeRewardDenoms.ValidateBasic()into theChangeRewardDenomsmsg server handler, following the cosmos-sdk v0.50 pattern whereValidateBasicis soft-deprecated.The validation checks (empty denoms, invalid denom format, denom in both add and remove sets) are now performed in the handler.
ValidateBasicstubs tonil. Also swappedsdk.NewCoin().IsValid()forsdk.ValidateDenom()to avoid a panic on malformed input.Tests added to
msg_server_test.gocovering the moved validation paths.