-
Notifications
You must be signed in to change notification settings - Fork 104
Description
[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
- Look at
x/oracle/types/ballot.goline 187-190 - Trace the call path:
EndBlock→Tally()(tally.go:86) →StandardDeviation() - 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 stdDevReferences
- Cosmos Security Handbook mentions this exact issue: https://www.faulttolerant.xyz/2024-01-16-cosmos-security-1/
- Similar discussion in cosmos-sdk: Usage of float64 in Cosmos SDK fee calculation is deterministic? cosmos/cosmos-sdk#15381
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