diff --git a/CHANGELOG.md b/CHANGELOG.md index 4508cbbe8..34df15759 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -95,6 +95,7 @@ needed to include double quotes around the hexadecimal string. - [#2184](https://github.com/NibiruChain/nibiru/pull/2184) - test(evm): e2e tests configuration enhancements - [#2187](https://github.com/NibiruChain/nibiru/pull/2187) - fix(evm): fix eip55 address encoding - [#2188](https://github.com/NibiruChain/nibiru/pull/2188) - refactor(evm): update logs emission +- [#2192](https://github.com/NibiruChain/nibiru/pull/2192) - fix(oracle): correctly handle misscount #### Nibiru EVM | Before Audit 2 - 2024-12-06 diff --git a/x/oracle/keeper/ballot.go b/x/oracle/keeper/ballot.go index 134523136..942c8b1dd 100644 --- a/x/oracle/keeper/ballot.go +++ b/x/oracle/keeper/ballot.go @@ -159,6 +159,7 @@ func Tally( rewardSpread = standardDeviation } + missedValidators := make(map[string]bool) for _, v := range votes { // Filter votes winners & abstain voters isInsideSpread := v.ExchangeRate.GTE(weightedMedian.Sub(rewardSpread)) && @@ -173,7 +174,11 @@ func Tally( validatorPerformance.RewardWeight += v.Power validatorPerformance.WinCount++ case isMiss: - validatorPerformance.MissCount++ + // Only count a miss once per validator + if !missedValidators[v.Voter.String()] { + validatorPerformance.MissCount++ + missedValidators[v.Voter.String()] = true + } case isAbstainVote: validatorPerformance.AbstainCount++ } diff --git a/x/oracle/keeper/ballot_test.go b/x/oracle/keeper/ballot_test.go index bae2ed21a..46dee0c53 100644 --- a/x/oracle/keeper/ballot_test.go +++ b/x/oracle/keeper/ballot_test.go @@ -202,6 +202,43 @@ func TestFuzzTally(t *testing.T) { }) } +func TestTallyMissCount(t *testing.T) { + fixture := CreateTestFixture(t) + + power := int64(100) + amt := sdk.TokensFromConsensusPower(power, sdk.DefaultPowerReduction) + sh := stakingkeeper.NewMsgServerImpl(&fixture.StakingKeeper) + + // Validator created + _, err := sh.CreateValidator(fixture.Ctx, NewTestMsgCreateValidator(ValAddrs[0], ValPubKeys[0], amt)) + require.NoError(t, err) + _, err = sh.CreateValidator(fixture.Ctx, NewTestMsgCreateValidator(ValAddrs[1], ValPubKeys[1], amt)) + require.NoError(t, err) + _, err = sh.CreateValidator(fixture.Ctx, NewTestMsgCreateValidator(ValAddrs[2], ValPubKeys[2], amt)) + require.NoError(t, err) + staking.EndBlocker(fixture.Ctx, &fixture.StakingKeeper) + + pairBtc := asset.Registry.Pair(denoms.BTC, denoms.NUSD) + pairEth := asset.Registry.Pair(denoms.ETH, denoms.NUSD) + // Having validator2 to have 2 votes outside the spread + btcVotes := types.ExchangeRateVotes{ + {Pair: pairBtc, ExchangeRate: math.LegacyNewDec(17), Voter: ValAddrs[0], Power: power}, + {Pair: pairBtc, ExchangeRate: math.LegacyNewDec(10), Voter: ValAddrs[1], Power: power}, + {Pair: pairBtc, ExchangeRate: math.LegacyNewDec(20000), Voter: ValAddrs[2], Power: power}, + } + ethVotes := types.ExchangeRateVotes{ + {Pair: pairEth, ExchangeRate: math.LegacyNewDec(1_000), Voter: ValAddrs[0], Power: power}, + {Pair: pairEth, ExchangeRate: math.LegacyNewDec(1_300), Voter: ValAddrs[1], Power: power}, + {Pair: pairEth, ExchangeRate: math.LegacyNewDec(1), Voter: ValAddrs[2], Power: power}, + } + + allVotes := append(btcVotes, ethVotes...) + + claimMap := types.ValidatorPerformances{} + Tally(allVotes, math.LegacyNewDec(10), claimMap) + assert.Equal(t, int64(1), claimMap[ValAddrs[2].String()].MissCount) +} + type VoteMap = map[asset.Pair]types.ExchangeRateVotes func TestRemoveInvalidBallots(t *testing.T) {