Skip to content

[BUG] Oracle StandardDeviation uses float math - potential consensus issue #188

@coinsspor

Description

@coinsspor

[BUG] Oracle StandardDeviation uses float math - potential consensus issue

Bug Description

Found a problem in the StandardDeviation() function in x/oracle/types/ballot.go. The function uses native Go float operations (math.Sqrt, strconv.ParseFloat) instead of deterministic sdk.Dec math.

This is risky because:

  • Float math can give slightly different results on different CPU types (x86 vs ARM, different FPU modes)
  • This function runs during EndBlock in the Tally process
  • If validators get different stddev values, they might disagree on who gets rewards
  • That means potential state mismatch between nodes

The problematic code (lines 187-190):

floatNum, _ := strconv.ParseFloat(variance.String(), 64)
floatNum = math.Sqrt(floatNum)
standardDeviation, _ = sdkMath.LegacyNewDecFromStr(fmt.Sprintf("%f", floatNum))

Also both errors are silently ignored with _ which isn't great.


Steps to Reproduce

  1. Look at x/oracle/types/ballot.go line 187-190
  2. Trace the call path: EndBlockTally() (tally.go:86) → StandardDeviation()
  3. The float operations happen every vote period when calculating reward spreads

To actually trigger different results you'd need to run nodes on different architectures (like x86 Linux vs ARM Mac) and submit exchange rates that produce edge-case variance values. The float precision differences would show up in the standardDeviation result.


Expected Behavior

All validators should compute the exact same standardDeviation value regardless of their hardware. Cosmos SDK provides LegacyDec.ApproxSqrt() specifically for this - it's deterministic across all platforms.


Actual Behavior

The function converts to float64, does math.Sqrt(), then converts back to Dec. IEEE 754 float operations aren't guaranteed to be identical across:

  • Different CPU architectures
  • Different FPU implementations
  • Different compiler optimizations

This is a known issue in blockchain dev - the Cosmos Security Handbook explicitly warns against using native float in consensus code.


Environment

  • Looked at current main branch
  • File: x/oracle/types/ballot.go
  • Related: x/oracle/tally.go, x/oracle/abci.go

Impact Assessment

Severity: High

  • Could cause consensus failure if nodes disagree on standardDeviation
  • Affects validator reward calculations every vote period
  • Chain halt possible in edge cases

Realistically, most of the time the float differences would be tiny and might round to the same Dec value. But with the right input values (very large or very precise numbers), the differences could matter.

Not marking as Critical because:

  • Needs specific edge-case inputs to actually cause problems
  • Hasn't caused issues yet (that I know of)

Suggested Fix

Replace the float math with deterministic Dec operations:

variance := sum.QuoInt64(int64(len(ex)))

stdDev, err := variance.ApproxSqrt()
if err != nil {
    return sdkMath.LegacyZeroDec()
}
return stdDev

References


Reporter Declaration

By submitting this issue, you confirm that:

  • This report is NOT generated by AI
  • I personally tested and verified this issue
  • I understand that false or low-effort reports are disqualified from rewards

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions