fix: resolve division-by-zero chain halt#299
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughReplaces second-precision time math in Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 1 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
x/rewards/types/reward.go (1)
22-39: Add an explicit sub-second regression test (e.g., 500ms total duration).Given issue
#267, a test whereEndTime - LastReleaseTimeis in the 1–999ms range would lock in this fix and prevent reintroduction of second-truncation behavior.Example test case shape (in
x/rewards/types/reward_test.go)+{ + name: "sub-second duration does not divide by zero", + blockTime: now.Add(250 * time.Millisecond), + schedule: types.ReleaseSchedule{ + TotalAmount: sdk.NewCoin(denom, math.NewInt(1000)), + ReleasedAmount: sdk.NewCoin(denom, math.NewInt(0)), + LastReleaseTime: now, + EndTime: now.Add(500 * time.Millisecond), + Active: true, + }, + expectedError: false, +},🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x/rewards/types/reward.go` around lines 22 - 39, Add a regression test in x/rewards/types/reward_test.go that covers a sub-second total duration (e.g., set schedule.LastReleaseTime and schedule.EndTime 500ms apart) to ensure the nanosecond-based math (use of schedule.EndTime.Sub(schedule.LastReleaseTime).Nanoseconds(), totalDurationNs, timeElapsedNs and the calculation of releaseProportion) prevents division-by-zero from second-truncation; the test should simulate blockTime between LastReleaseTime and EndTime, call the code path that computes releaseProportion, and assert a valid non-zero proportion and no error when totalDurationNs > 0.x/oracle/types/ballot.go (1)
25-25: Add a regression test with distinctpowerandweightvalues.The assignment fix is correct, but current coverage (e.g.,
power == weight) won’t catch this bug if it regresses. Please add a case likepower=10, weight=0inx/oracle/types/ballot_test.go.Proposed test adjustment
func TestNewClaim(t *testing.T) { power := int64(10) - weight := int64(10) + weight := int64(0) winCount := int64(0) didVote := false recipient := sdk.ValAddress([]byte("validator1"))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@x/oracle/types/ballot.go` at line 25, Add a regression test in x/oracle/types/ballot_test.go that constructs a Ballot using distinct power and weight values (e.g., power=10, weight=0) and asserts that the Ballot.Weight field (or method) equals the provided weight rather than power; locate the Ballot constructor/struct in ballot.go (referencing the Weight field assignment) and in the new test create the ballot, then assert both ballot.Power == 10 and ballot.Weight == 0 to ensure the previous bug (using power instead of weight) is covered and cannot regress.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@x/oracle/types/ballot.go`:
- Line 25: Add a regression test in x/oracle/types/ballot_test.go that
constructs a Ballot using distinct power and weight values (e.g., power=10,
weight=0) and asserts that the Ballot.Weight field (or method) equals the
provided weight rather than power; locate the Ballot constructor/struct in
ballot.go (referencing the Weight field assignment) and in the new test create
the ballot, then assert both ballot.Power == 10 and ballot.Weight == 0 to ensure
the previous bug (using power instead of weight) is covered and cannot regress.
In `@x/rewards/types/reward.go`:
- Around line 22-39: Add a regression test in x/rewards/types/reward_test.go
that covers a sub-second total duration (e.g., set schedule.LastReleaseTime and
schedule.EndTime 500ms apart) to ensure the nanosecond-based math (use of
schedule.EndTime.Sub(schedule.LastReleaseTime).Nanoseconds(), totalDurationNs,
timeElapsedNs and the calculation of releaseProportion) prevents
division-by-zero from second-truncation; the test should simulate blockTime
between LastReleaseTime and EndTime, call the code path that computes
releaseProportion, and assert a valid non-zero proportion and no error when
totalDurationNs > 0.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4de32c24-3172-4121-aae0-36cdce1a27dc
📒 Files selected for processing (2)
x/oracle/types/ballot.gox/rewards/types/reward.go
Thaleszh
left a comment
There was a problem hiding this comment.
There are a couple changes needed, there is also a merge conflict with main that needs to be addressed
|
@Thaleszh done, good call. Changed the guard so that when @adambalogh this is now pushed to the branch if you want to take a look. |
|
@Thaleszh cleaned up. Removed the inline fix annotation comments from reward.go, the code is self-explanatory and the PR description covers the reasoning. @adambalogh both commits are in, ready for another pass. |
|
@Thaleszh good to know, stripped the trailing comment from the @adambalogh flagging this in case you want to verify against the other PR before merging. |
…nd precision to prevent division by zero Addresses reviewer feedback from @Thaleszh: - When EndTime <= LastReleaseTime the remaining reward is released immediately rather than returning an error - Removed inline fix-comment annotations from the code - Added sub-second duration regression test - Removed ballot.go change (already merged in a separate PR)
aa4ae8f to
1c6ba35
Compare
|
Hey @Thaleszh, thanks for the review! I've addressed all three of your points in the latest push: 1. Zero-duration guard now releases the full remaining reward Changed the 2. Inline fix-comment annotations removed Stripped all the 3. ballot.go change dropped Since the Also added a sub-second duration regression test ( @adambalogh happy to make any further adjustments if something still looks off. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
@amathxbt Just missing changelog with fixes, under unreleased |
…coverage CI check
|
@Thaleszh just pushed fixes for both CI warnings blocking this PR: Fix 1 — Docstring Coverage (was 66.67%, needs ≥ 80%)
Fix 2 — Out of Scope Changes warning Updated the PR description to clarify that the Both CI warnings should now clear on the next check run. Please let me know if anything else is needed before merge. Thanks! |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 1026 🏁 Script executed: Length of output: 765
Fix 1 — Docstring ✅ Fix 2 — PR Description ✅ Changelog ## Unreleased
### Fixed
- Fix division-by-zero panic in `CalculateReward` when `EndTime - LastReleaseTime` is sub-second; switch time calculations to nanosecond precision and release remaining amount when total duration is zero or negative (x/rewards/types/reward.go, closes `#267`)That should be the last remaining item before merge. Let me know once it's in! |
|
@Thaleszh @coderabbitai — changelog entry added as requested. Pushed to - Fix division-by-zero chain halt in `CalculateReward` caused by sub-second schedule durations; replace `Seconds()` truncation with `Nanoseconds()` precision and release full remaining reward when `EndTime <= LastReleaseTime` ([#267](https://github.com/KiiChain/kiichain/issues/267))Commit: The entry is inserted as the first item in the |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 1175
All three previously flagged items are now resolved:
The PR looks good to go from my side. 🚀 🐇✨ |
) # Description Adds missing regression tests for changes done in #299 ## Type of change - [x] test (For updates on tests) # PR Checklist: Make sure each step was done: - [ ] Updated changelog with PR's intent - [x] Lint with `make lint-fix`
Summary
This PR fixes 2 confirmed bugs found in the KiiChain codebase:
🔴 Fix 1: Division-by-Zero Chain Halt in
CalculateReward(Closes #267)Severity: Critical — Chain Halt
File:
x/rewards/types/reward.goRoot Cause:
The
CalculateRewardfunction usedSeconds()(which truncates to whole seconds) for its division check, but compared equality using nanosecond precision. WhenEndTime - LastReleaseTimeis between 1–999 milliseconds:Equal()returnsfalse(they differ by sub-second)Seconds()returns0(truncation)Fix:
Replaced
Seconds()withNanoseconds()throughoutCalculateRewardto maintain full precision and prevent truncation-induced division by zero. WhenEndTime <= LastReleaseTime, the full remaining amount is now released immediately instead of returning an error.Also improved the guard check from
Equal()to<= 0nanoseconds for full precision safety.Testing
go test -v -run TestCalculateReward ./x/rewards/types/...EndTime == LastReleaseTimeedge caseRelated Issues