Skip to content

Commit

Permalink
feat(oracle): default vote threshold and min voters (#1187)
Browse files Browse the repository at this point in the history
* refactor(oracle): validator performance map

* refactor(oracle): whitelisted pairs set

* chore: update changelog

* refactor(oracle): update whitelist

* refactor(oracle): rename vote_target to whitelist

* chore: update changelog

* refactor: remove redundant getWhitelistedPairs method

* refactor: move updateWhitelist to whitelist.go

* refactor: merge tally.go with ballot.go

* chore: update changelog

* feat(oracle): change default vote threshold to 33%

* feat(oracle): add min voters param

* feat(oracle): add minimum number of voters

* chore: update changelog
  • Loading branch information
k-yang authored Feb 8, 2023
1 parent 0ed0a3b commit 63f1892
Show file tree
Hide file tree
Showing 16 changed files with 468 additions and 229 deletions.
3 changes: 1 addition & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ issues:
max-same-issues: 0 # infinite
fix: true
exclude:
# see https://github.com/stretchr/testify/issues/1199#issuecomment-1215592618, testify shows vet problem
# see https://github.com/stretchr/testify/issues/1199#issuecomment-1215592618, testify shows vet problem
- 'github.com\/stretchr\/testify\/suite\.Suite contains sync\.RWMutex'
linters:
enable:
Expand All @@ -22,4 +22,3 @@ linters-settings:
local-prefixes: github.com/NibiruChain/nibiru
severity:
default-severity: error

4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Features

* [#1187](https://github.com/NibiruChain/nibiru/pull/1187) - feat(oracle): default vote threshold and min voters

### API Breaking

* [#1158](https://github.com/NibiruChain/nibiru/pull/1158) - feat(asset-registry)!: Add `AssetRegistry`
Expand Down
6 changes: 6 additions & 0 deletions proto/oracle/v1beta1/oracle.proto
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ message Params {
(gogoproto.jsontag) = "twap_lookback_window,omitempty",
(gogoproto.moretags) = "yaml:\"twap_lookback_window\""
];

// The minimum number of voters (i.e. oracle validators) per pair for it to be considered a passing ballot.
// Recommended at least 4.
uint64 min_voters = 9 [
(gogoproto.moretags) = "yaml:\"min_voters\""
];
}

// Struct for aggregate prevoting on the ExchangeRateVote.
Expand Down
21 changes: 17 additions & 4 deletions x/oracle/keeper/ballot.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,21 @@ func (k Keeper) clearVotesAndPreVotes(ctx sdk.Context, votePeriod uint64) {
}

// isPassingVoteThreshold ballot is passing the threshold amount of voting power
func isPassingVoteThreshold(ballots types.ExchangeRateBallots, thresholdVotes sdk.Int) bool {
func isPassingVoteThreshold(ballots types.ExchangeRateBallots, thresholdVotingPower sdk.Int, minVoters uint64) bool {
ballotPower := sdk.NewInt(ballots.Power())
return !ballotPower.IsZero() && ballotPower.GTE(thresholdVotes)
if ballotPower.IsZero() {
return false
}

if ballotPower.LT(thresholdVotingPower) {
return false
}

if ballots.NumValidVoters() < minVoters {
return false
}

return true
}

// removeInvalidBallots removes the ballots which have not reached the vote threshold
Expand All @@ -89,7 +101,8 @@ func (k Keeper) removeInvalidBallots(
whitelistedPairs := set.New(k.GetWhitelistedPairs(ctx)...)

totalBondedPower := sdk.TokensToConsensusPower(k.StakingKeeper.TotalBondedTokens(ctx), k.StakingKeeper.PowerReduction(ctx))
voteThreshold := k.VoteThreshold(ctx).MulInt64(totalBondedPower).RoundInt()
thresholdVotingPower := k.VoteThreshold(ctx).MulInt64(totalBondedPower).RoundInt()
minVoters := k.MinVoters(ctx)

for pair, ballots := range pairBallotsMap {
// If pair is not whitelisted, or the ballot for it has failed, then skip
Expand All @@ -101,7 +114,7 @@ func (k Keeper) removeInvalidBallots(

// If the ballot is not passed, remove it from the whitelistedPairs set
// to prevent slashing validators who did valid vote.
if !isPassingVoteThreshold(ballots, voteThreshold) {
if !isPassingVoteThreshold(ballots, thresholdVotingPower, minVoters) {
delete(whitelistedPairs, pair)
delete(pairBallotsMap, pair)
continue
Expand Down
3 changes: 1 addition & 2 deletions x/oracle/keeper/ballot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import (
"sort"
"testing"

"github.com/NibiruChain/collections"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking"
fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/NibiruChain/collections"

"github.com/NibiruChain/nibiru/x/common/asset"
"github.com/NibiruChain/nibiru/x/common/denoms"
"github.com/NibiruChain/nibiru/x/common/set"
Expand Down
10 changes: 5 additions & 5 deletions x/oracle/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ package keeper
import (
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/stretchr/testify/require"

"github.com/NibiruChain/nibiru/x/common/asset"
"github.com/NibiruChain/nibiru/x/common/denoms"

"github.com/NibiruChain/nibiru/x/oracle/types"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

func TestParams(t *testing.T) {
Expand All @@ -26,6 +24,7 @@ func TestParams(t *testing.T) {
// Test custom params setting
votePeriod := uint64(10)
voteThreshold := sdk.NewDecWithPrec(33, 2)
minVoters := uint64(4)
oracleRewardBand := sdk.NewDecWithPrec(1, 2)
slashFraction := sdk.NewDecWithPrec(1, 2)
slashWindow := uint64(1000)
Expand All @@ -39,6 +38,7 @@ func TestParams(t *testing.T) {
newParams := types.Params{
VotePeriod: votePeriod,
VoteThreshold: voteThreshold,
MinVoters: minVoters,
RewardBand: oracleRewardBand,
Whitelist: whitelist,
SlashFraction: slashFraction,
Expand Down
8 changes: 7 additions & 1 deletion x/oracle/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ func (k Keeper) VoteThreshold(ctx sdk.Context) (res sdk.Dec) {
return
}

// VoteThreshold returns the minimum percentage of votes that must be received for a ballot to pass.
func (k Keeper) MinVoters(ctx sdk.Context) (res uint64) {
k.paramSpace.Get(ctx, types.KeyMinVoters, &res)
return
}

// RewardBand returns a maxium divergence that a price vote can have from the
// weighted median in the ballot. If a vote lies within the valid range
// defined by:
Expand All @@ -28,7 +34,7 @@ func (k Keeper) VoteThreshold(ctx sdk.Context) (res sdk.Dec) {
//
// then rewards are added to the validator performance.
// Note that if the reward band is smaller than 1 standard
// deviation, the band is taken to be 1 standard deviation.a price
// deviation, the band is taken to be 1 standard deviation.
func (k Keeper) RewardBand(ctx sdk.Context) (res sdk.Dec) {
k.paramSpace.Get(ctx, types.KeyRewardBand, &res)
return
Expand Down
4 changes: 2 additions & 2 deletions x/oracle/keeper/querier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ import (
"testing"
"time"

"github.com/NibiruChain/collections"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"

"github.com/NibiruChain/collections"

"github.com/NibiruChain/nibiru/x/common/asset"
"github.com/NibiruChain/nibiru/x/common/denoms"
"github.com/NibiruChain/nibiru/x/oracle/types"
Expand Down Expand Up @@ -155,6 +154,7 @@ func TestCalcTwap(t *testing.T) {
newParams := types.Params{
VotePeriod: types.DefaultVotePeriod,
VoteThreshold: types.DefaultVoteThreshold,
MinVoters: types.DefaultMinVoters,
RewardBand: types.DefaultRewardBand,
Whitelist: types.DefaultWhitelist,
SlashFraction: types.DefaultSlashFraction,
Expand Down
6 changes: 5 additions & 1 deletion x/oracle/keeper/reward_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ func setup(t *testing.T) (keeper.TestInput, types.MsgServer) {
require.NoError(t, err)
_, err = sh(input.Ctx, keeper.NewTestMsgCreateValidator(keeper.ValAddrs[2], keeper.ValPubKeys[2], stakingAmt))
require.NoError(t, err)
_, err = sh(input.Ctx, keeper.NewTestMsgCreateValidator(keeper.ValAddrs[3], keeper.ValPubKeys[3], stakingAmt))
require.NoError(t, err)
_, err = sh(input.Ctx, keeper.NewTestMsgCreateValidator(keeper.ValAddrs[4], keeper.ValPubKeys[4], stakingAmt))
require.NoError(t, err)
staking.EndBlocker(input.Ctx, input.StakingKeeper)

return input, h
Expand All @@ -50,7 +54,7 @@ func TestKeeper_RewardsDistributionMultiVotePeriods(t *testing.T) {
// then we simulate that after the 5 voting periods are
// finished no more rewards distribution happen.
const periods uint64 = 5
const validators = 3
const validators = 4
input, h := setup(t) // set vote threshold
params := input.OracleKeeper.GetParams(input.Ctx)
input.OracleKeeper.SetParams(input.Ctx, params)
Expand Down
Loading

0 comments on commit 63f1892

Please sign in to comment.