Skip to content

Conversation

@Winnode
Copy link
Contributor

@Winnode Winnode commented Jan 7, 2026

Summary

This PR fixes a critical bug where pickReferenceDenom in x/oracle/tally.go uses panic(err) which can cause chain halt when called from EndBlocker.

Changes

  • Return error from pickReferenceDenom instead of panic(err)`n- Handle error in EndBlocker caller with proper logging
  • Update test to handle new return value

Impact

Prevents potential chain halt if k.Params.Get(ctx) fails during EndBlocker execution.

Fixes #203

Copilot AI review requested due to automatic review settings January 7, 2026 22:50
@Winnode Winnode requested a review from jhelison as a code owner January 7, 2026 22:50
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 7, 2026

Walkthrough

The pull request changes pickReferenceDenom to return an error (signature now returns (referenceDenom string, belowThresholdVoteMap map[string]types.ExchangeRateBallot, err error)) and removes the panic on params retrieval failure by logging and returning the error. EndBlocker in x/oracle/abci.go now captures and handles the error from pickReferenceDenom (logs and early-returns). Tests in x/oracle/tally_test.go were updated to assert the success path and added a test for the error path. CHANGELOG.md was updated to document the fix.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: handling error in pickReferenceDenom instead of panic, which is the primary objective of this PR.
Description check ✅ Passed The description is directly related to the changeset, explaining the bug fix, changes made, and impact of preventing chain halt from panic in pickReferenceDenom.
Linked Issues check ✅ Passed The PR successfully addresses all requirements from issue #203: pickReferenceDenom now returns error instead of panicking, EndBlocker handles the error with logging, and tests are updated to verify the new behavior.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the critical bug in issue #203: function signature update, error handling in tally.go, EndBlocker caller update, and test updates. No out-of-scope changes detected.

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

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 60d8d31 and bbd6acf.

📒 Files selected for processing (1)
  • x/oracle/tally_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
x/oracle/tally_test.go (4)
x/oracle/keeper/test_utils.go (1)
  • CreateTestInput (130-294)
x/oracle/types/params.pb.go (6)
  • Params (28-50)
  • Params (53-53)
  • Params (54-56)
  • Denom (113-116)
  • Denom (119-119)
  • Denom (120-122)
x/oracle/utils/assets.go (1)
  • AtomDenom (6-6)
x/oracle/types/ballot.go (1)
  • ExchangeRateBallot (51-51)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: liveness-test
  • GitHub Check: tests
  • GitHub Check: test-e2e
  • GitHub Check: Analyze
  • GitHub Check: golangci-lint
🔇 Additional comments (2)
x/oracle/tally_test.go (2)

100-101: LGTM! Correctly handles the new error return.

The test properly captures and asserts the error from pickReferenceDenom's updated signature, following standard Go testing patterns.


199-222: Well-structured error path test with good coverage.

The test correctly verifies that pickReferenceDenom returns an error when params are missing, preventing the panic that could cause chain halt. The setup (removing params) and assertions (error presence, empty/nil returns) are appropriate.

Note: Line 219 checks for "collections" in the error message, which is implementation-specific. While this could be brittle if the underlying collections package changes its error messages, it provides valuable specificity for this critical error path. The tradeoff is acceptable for preventing chain halts.


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

❤️ Share

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

Copy link
Contributor

@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: 1

🤖 Fix all issues with AI agents
In @CHANGELOG.md:
- Line 49: Remove the duplicate changelog entry "Handle error in
pickReferenceDenom instead of panic to prevent chain halt
[#203](https://github.com/KiiChain/kiichain/issues/203)" from the released
version section (v6.0.0) so it remains only under the UNRELEASED heading; locate
the string in CHANGELOG.md (the entry under v6.0.0) and delete that single line,
leaving the UNRELEASED entry intact.
🧹 Nitpick comments (2)
x/oracle/tally_test.go (1)

100-100: Consider adding a test for the error case.

The test correctly captures the new third return value. However, the fix for issue #203 specifically addresses the panic when k.Params.Get(ctx) fails, yet there's no test verifying that pickReferenceDenom returns an error gracefully in that scenario.

Adding a negative test case would validate the fix and prevent regressions.

x/oracle/tally.go (1)

21-23: Consider handling the error from TotalBondedTokens.

The error from TotalBondedTokens is silently ignored. If this call fails, totalBondedTokens could be zero or invalid, potentially causing incorrect thresholdVotes calculation. Since this PR focuses on robust error handling to prevent chain issues, consider propagating this error as well.

Proposed fix
 	// Get total bonded power
 	powerReductionFactor := k.StakingKeeper.PowerReduction(ctx)                             // get the power reduction
-	totalBondedTokens, _ := k.StakingKeeper.TotalBondedTokens(ctx)                          // total of tokens in staking
+	totalBondedTokens, err := k.StakingKeeper.TotalBondedTokens(ctx)                        // total of tokens in staking
+	if err != nil {
+		return "", nil, err
+	}
 	totalBondedPower := sdk.TokensToConsensusPower(totalBondedTokens, powerReductionFactor) // Get the blockchain vote power
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67de973 and 955caa2.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • x/oracle/abci.go
  • x/oracle/tally.go
  • x/oracle/tally_test.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: CodeQL analysis (go)
  • GitHub Check: Agent
  • GitHub Check: tests
  • GitHub Check: golangci-lint
  • GitHub Check: Analyze
  • GitHub Check: test-e2e
🔇 Additional comments (2)
x/oracle/abci.go (1)

78-82: LGTM!

The error handling correctly addresses the critical issue #203. The implementation:

  • Properly captures and checks the error from pickReferenceDenom
  • Logs the error with context before returning, which aids debugging
  • Returns the error to prevent proceeding with invalid state

This prevents the chain halt that would occur from a panic in EndBlocker.

x/oracle/tally.go (1)

15-15: LGTM! This correctly addresses the critical panic issue.

The function signature now returns an error, and the k.Params.Get(ctx) failure path gracefully returns an error instead of panicking. This prevents the chain halt scenario described in issue #203.

Also applies to: 26-29, 67-67

@codecov
Copy link

codecov bot commented Jan 7, 2026

Codecov Report

❌ Patch coverage is 71.42857% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
x/oracle/abci.go 33.33% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a critical bug where pickReferenceDenom in the oracle module uses panic(err) instead of returning an error, which can cause chain halt when called from EndBlocker.

Key changes:

  • Modified pickReferenceDenom to return an error instead of panicking
  • Updated EndBlocker to properly handle and log errors from pickReferenceDenom
  • Updated test to handle the new error return value

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
x/oracle/tally.go Changed function signature to return error and replaced panic(err) with return "", nil, err
x/oracle/abci.go Added error handling for pickReferenceDenom call with logging
x/oracle/tally_test.go Updated test to handle new error return value from pickReferenceDenom
CHANGELOG.md Added changelog entry documenting the fix

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@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: 0

Caution

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

⚠️ Outside diff range comments (1)
x/oracle/tally.go (1)

176-284: Critical syntax error: duplicate import statement and function definitions.

Lines 176-284 contain duplicate import statements and function definitions that already appear at lines 1-174. In Go, import statements must appear before other declarations. This syntax error is causing all pipeline failures (E2E tests, liveness tests, and golangci-lint).

🔧 Fix: Remove duplicate content

Delete lines 176-284 entirely. The correct package declaration, imports, and function definitions already exist at lines 1-174.

-import (
-	"cosmossdk.io/math"
-
-	sdk "github.com/cosmos/cosmos-sdk/types"
-
-	"github.com/kiichain/kiichain/v7/x/oracle/keeper"
-	"github.com/kiichain/kiichain/v7/x/oracle/types"
-)
-
-// pickReferenceDenom selects a denom with the highest vote power as reference denom.
-// If the power of 2 denominations is the same, select the reference denom
-// in alphabetical order
-func pickReferenceDenom(ctx sdk.Context, k keeper.Keeper, voteTargets map[string]types.Denom, voteMap map[string]types.ExchangeRateBallot) (string, map[string]types.ExchangeRateBallot, error) {
-	highestBallotPower := int64(0)
-	referenceDenom := ""
-	belowThresholdVoteMap := map[string]types.ExchangeRateBallot{}
-
-	// Get total bonded power
-	powerReductionFactor := k.StakingKeeper.PowerReduction(ctx)                             // get the power reduction
-	totalBondedTokens, _ := k.StakingKeeper.TotalBondedTokens(ctx)                          // total of tokens in staking
-	totalBondedPower := sdk.TokensToConsensusPower(totalBondedTokens, powerReductionFactor) // Get the blockchain vote power
-
-	// Get threshold (minimum power necessary to considerate a successful ballot)
-	params, err := k.Params.Get(ctx)
-	if err != nil {
-		return "", nil, err
-	}
-
-	voteThreshold := params.VoteThreshold                                 // Get vote threshold from params
-	thresholdVotes := voteThreshold.MulInt64(totalBondedPower).RoundInt() // Threshold to allow a ballot
-
-	// Iterate the voting map
-	for denom, ballot := range voteMap {
-
-		// If a denom is not in the vote targets or the ballot for it has failed
-		// that denom is removed from votemap (for efficiency)
-		_, exists := voteTargets[denom]
-		if !exists {
-			delete(voteMap, denom)
-			continue
-		}
-
-		// Get ballot power and check if is greater than the threshold
-		ballotPower, ok := ballotIsPassing(ballot, thresholdVotes)
-
-		// if the ballot power is lower than threshold, add denom in below
-		// threshold map to separe for tally evaluation
-		if !ok {
-			belowThresholdVoteMap[denom] = voteMap[denom]
-			delete(voteTargets, denom)
-			delete(voteMap, denom)
-			continue
-		}
-
-		if ballotPower.Int64() > highestBallotPower || highestBallotPower == 0 {
-			referenceDenom = denom
-			highestBallotPower = ballotPower.Int64()
-		}
-
-		// If the power is equal, select the the denom by alphabetical order
-		if ballotPower.Int64() == highestBallotPower && referenceDenom > denom {
-			referenceDenom = denom
-		}
-	}
-	return referenceDenom, belowThresholdVoteMap, nil
-}
-
-// ballotIsPassing calculate the sum of each vote power per denom, then check
-// if the ballot power is greater than the threshold
-func ballotIsPassing(ballot types.ExchangeRateBallot, thresholdVotes math.Int) (math.Int, bool) {
-	ballotPower := math.NewInt(ballot.Power()) // Get the validator power
-
-	// return ballot power and if the ballot is greater than the threshold
-	return ballotPower, !ballotPower.IsZero() && ballotPower.GTE(thresholdVotes)
-}
-
-// Tally calculates the median and returns it. Sets the set of voters to be rewarded, i.e. voted within
-// a reasonable spread from the weighted median to the store
-// CONTRACT: ex must be sorted
-func Tally(_ sdk.Context, ex types.ExchangeRateBallot, rewardBand math.LegacyDec, validatorClaimMap map[string]types.Claim) (weightedMedian math.LegacyDec) {
-	weightedMedian = ex.WeightedMedianWithAssertion() // Get weighted median
-
-	// Check if result is on the reward interval
-	standardDeviation := ex.StandardDeviation(weightedMedian)
-	rewardSpread := weightedMedian.Mul(rewardBand.QuoInt64(2)) // this is the interval that will be added around weightedMedian
-
-	if standardDeviation.GT(rewardSpread) { // if rewardSpread > deviation means the data is disperse
-		rewardSpread = standardDeviation
-	}
-
-	// Check each vote and reward
-	for _, vote := range ex {
-		// Filter ballot winners
-		voter := vote.Voter.String()
-		claim := validatorClaimMap[voter]
-
-		// If exchange rate is in the interval reward the validator
-		if vote.ExchangeRate.GTE(weightedMedian.Sub(rewardSpread)) && // lower limit
-			vote.ExchangeRate.LTE(weightedMedian.Add(rewardSpread)) { // upper limit
-
-			claim.Weight += vote.Power
-			claim.WinCount++
-		}
-		claim.DidVote = true
-		validatorClaimMap[voter] = claim
-	}
-
-	return
-}
🧹 Nitpick comments (1)
x/oracle/tally.go (1)

12-174: Address excessive blank lines throughout the file.

Lines 12-174 contain excessive blank lines between nearly every line of code. This significantly reduces readability. The code should follow standard Go formatting conventions with appropriate spacing.

♻️ Proposed fix: Remove excessive blank lines

Run gofmt or goimports on this file to normalize the spacing, or manually remove the extra blank lines between statements within functions.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 955caa2 and b9cd73b.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • x/oracle/abci.go
  • x/oracle/tally.go
  • x/oracle/tally_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md
🧰 Additional context used
🪛 GitHub Actions: E2E
x/oracle/tally.go

[error] 176-176: syntax error: imports must appear before other declarations

🪛 GitHub Actions: Liveness check
x/oracle/tally.go

[error] 176-176: syntax error: imports must appear before other declarations

🪛 GitHub Check: golangci-lint
x/oracle/tally.go

[failure] 176-176:
syntax error: imports must appear before other declarations) (typecheck)


[failure] 176-176:
imports must appear before other declarations (typecheck)


[failure] 176-176:
syntax error: imports must appear before other declarations (typecheck)


[failure] 176-176:
syntax error: imports must appear before other declarations) (typecheck)


[failure] 176-176:
syntax error: imports must appear before other declarations) (typecheck)


[failure] 176-176:
syntax error: imports must appear before other declarations) (typecheck)

🪛 GitHub Check: liveness-test
x/oracle/tally.go

[failure] 176-176:
syntax error: imports must appear before other declarations

🪛 GitHub Check: test-e2e
x/oracle/tally.go

[failure] 176-176:
syntax error: imports must appear before other declarations

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: tests
  • GitHub Check: test-e2e
  • GitHub Check: liveness-test
  • GitHub Check: Analyze
  • GitHub Check: golangci-lint
🔇 Additional comments (3)
x/oracle/tally_test.go (1)

100-101: LGTM! Test correctly updated for new error return.

The test properly captures and asserts the new error return value from pickReferenceDenom, aligning with the updated function signature.

x/oracle/abci.go (1)

78-81: LGTM! Proper error handling prevents chain halt.

The error capture and early return correctly addresses the critical issue where a panic in pickReferenceDenom could halt the chain. The error is properly propagated up the call stack.

x/oracle/tally.go (1)

18-18: Core error handling changes look correct.

The functional changes to return an error instead of panicking are correct and address the critical issue #203:

  • Line 18: Function signature updated to return error
  • Lines 38-42: Returns error instead of calling panic(err)
  • Line 104: Returns nil error on success

These changes prevent chain halt when k.Params.Get(ctx) fails during EndBlocker execution.

Also applies to: 40-40, 104-104

Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
x/oracle/tally_test.go (1)

199-221: Good test coverage for the error path.

This test properly validates the error scenario when params are missing, which directly addresses the critical issue #203. The test correctly:

  • Removes params to simulate the failure condition
  • Verifies all three return values (error, empty string, nil map)
  • Ensures no panic occurs
Optional: Verify error message for better diagnostics

Consider adding an assertion to verify the error message contains relevant context about the param retrieval failure. This helps ensure the logged error provides useful debugging information:

 	referenceDenom, belowThresholdVoteMap, err := pickReferenceDenom(ctx, oracleKeeper, votingTarget, voteMap)
 	require.Error(t, err)
+	require.Contains(t, err.Error(), "param")
 	require.Empty(t, referenceDenom)
 	require.Nil(t, belowThresholdVoteMap)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a8302f and 60d8d31.

📒 Files selected for processing (1)
  • x/oracle/tally_test.go
🧰 Additional context used
🧬 Code graph analysis (1)
x/oracle/tally_test.go (3)
x/oracle/keeper/test_utils.go (1)
  • CreateTestInput (130-294)
x/oracle/types/params.pb.go (6)
  • Params (28-50)
  • Params (53-53)
  • Params (54-56)
  • Denom (113-116)
  • Denom (119-119)
  • Denom (120-122)
x/oracle/utils/assets.go (1)
  • AtomDenom (6-6)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: tests
  • GitHub Check: liveness-test
  • GitHub Check: Analyze
  • GitHub Check: golangci-lint
  • GitHub Check: test-e2e
🔇 Additional comments (1)
x/oracle/tally_test.go (1)

100-101: LGTM! Error handling properly added to happy path test.

The update correctly captures the new error return value and asserts no error occurs in the success scenario.

@Thaleszh Thaleszh merged commit f14fe42 into KiiChain:main Jan 9, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Oracle EndBlocker: Unhandled panic in pickReferenceDenom can cause consensus failure

2 participants