Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: individual reward bands per denom #98

Merged
merged 11 commits into from
Feb 15, 2023
19 changes: 16 additions & 3 deletions proto/ojo/oracle/v1/oracle.proto
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ message Params {
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
string reward_band = 3 [
(gogoproto.moretags) = "yaml:\"reward_band\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
repeated RewardBand reward_bands = 3 [
(gogoproto.moretags) = "yaml:\"reward_bands\"",
(gogoproto.castrepeated) = "RewardBandList",
(gogoproto.nullable) = false
];
uint64 reward_distribution_window = 4
Expand Down Expand Up @@ -73,6 +73,19 @@ message Denom {
uint32 exponent = 3 [ (gogoproto.moretags) = "yaml:\"exponent\"" ];
}

// RewardBand - the object to hold the reward band configuration for a given denom.
message RewardBand {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;
string symbol_denom = 1 [ (gogoproto.moretags) = "yaml:\"symbol_denom\"" ];
string reward_band = 2 [
(gogoproto.moretags) = "yaml:\"reward_band\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}

// AggregateExchangeRatePrevote -
// struct for aggregate prevoting on the ExchangeRateVote.
// The purpose of aggregate prevote is to hide vote exchange rates with hash
Expand Down
4 changes: 3 additions & 1 deletion x/oracle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ Validators must first pre-commit to a set of exchange rates, then in the subsequ

### Reward Band

Let `M` be the median, `𝜎` be the standard deviation of the votes in the ballot, and `R` be the RewardBand parameter. The band around the median is set to be `𝜀 = max(𝜎, R/2)`. All valid (i.e. bonded and non-jailed) validators that submitted an exchange rate vote in the interval `[M - 𝜀, M + 𝜀]` should be included in the set of winners.
Each asset has a unique RewardBand when it's being added to the Oracle Parameters. For some assets this needs to be smaller or larger in order to account for expected price flux / stability.

Let `M` be the median, `𝜎` be the standard deviation of the votes in the ballot, and `R` be the RewardBand for a given asset. The band around the median is set to be `𝜀 = max(𝜎, R/2)`. All valid (i.e. bonded and non-jailed) validators that submitted an exchange rate vote in the interval `[M - 𝜀, M + 𝜀]` should be included in the set of winners.

### Reward Pool

Expand Down
17 changes: 15 additions & 2 deletions x/oracle/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,22 @@ func CalcPrices(ctx sdk.Context, params types.Params, k keeper.Keeper) error {
}

// Increment Mandatory Win count if Denom in Mandatory list
incrementWin := params.MandatoryList.Contains(ballotDenom.Denom)
isMandatory := params.MandatoryList.Contains(ballotDenom.Denom)
incrementWin := isMandatory

// Get the currently active denom list
denomList := params.AcceptList
if isMandatory {
denomList = params.MandatoryList
}
// Get the current denom's reward band
rewardBand, err := denomList.GetRewardBand(params.RewardBands)
if err != nil {
return err
}

// Get median of exchange rates
exchangeRate, err := Tally(ballotDenom.Ballot, params.RewardBand, validatorClaimMap, incrementWin)
exchangeRate, err := Tally(ballotDenom.Ballot, rewardBand, validatorClaimMap, incrementWin)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion x/oracle/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ func (s *IntegrationTestSuite) TestEndblockerHistoracle() {
blockHeight += historicStampPeriod
ctx = ctx.WithBlockHeight(blockHeight)

var decCoins = sdk.DecCoins{}
decCoins := sdk.DecCoins{}
for denom, prices := range exchangeRates {
decCoins = append(decCoins, sdk.DecCoin{
Denom: denom,
Expand Down
5 changes: 3 additions & 2 deletions x/oracle/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func (k Keeper) VoteThreshold(ctx sdk.Context) (res sdk.Dec) {
// RewardBand returns the ratio of allowable exchange rate error that a validator
// can be rewarded.
func (k Keeper) RewardBand(ctx sdk.Context) (res sdk.Dec) {
k.paramSpace.Get(ctx, types.KeyRewardBand, &res)
k.paramSpace.Get(ctx, types.KeyRewardBands, &res)
return
}

Expand Down Expand Up @@ -54,7 +54,8 @@ func (k Keeper) MandatoryList(ctx sdk.Context) (res types.DenomList) {
// SetMandatoryList updates the mandatory list of assets supported by the x/oracle
// module.
func (k Keeper) SetMandatoryList(ctx sdk.Context,
mandatoryList types.DenomList) {
mandatoryList types.DenomList,
) {
k.paramSpace.Set(ctx, types.KeyMandatoryList, mandatoryList)
}

Expand Down
2 changes: 0 additions & 2 deletions x/oracle/keeper/slash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ func (s *IntegrationTestSuite) TestSlashAndResetMissCounters() {
s.Require().Equal(tc.jailedAfter, validator.Jailed)
})
}

}

func (s *IntegrationTestSuite) TestPossibleWinsPerSlashWindow() {
Expand Down Expand Up @@ -129,5 +128,4 @@ func (s *IntegrationTestSuite) TestPossibleWinsPerSlashWindow() {
s.Require().Equal(tc.possibleWinsPerSlashWindow, actual)
})
}

}
28 changes: 22 additions & 6 deletions x/oracle/simulations/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
const (
votePeriodKey = "vote_period"
voteThresholdKey = "vote_threshold"
rewardBandKey = "reward_band"
rewardBandsKey = "reward_bands"
rewardDistributionWindowKey = "reward_distribution_window"
slashFractionKey = "slash_fraction"
slashWindowKey = "slash_window"
Expand Down Expand Up @@ -83,6 +83,8 @@ func GenMaximumMedianStamps(r *rand.Rand) uint64 {

// RandomizedGenState generates a random GenesisState for oracle
func RandomizedGenState(simState *module.SimulationState) {
oracleGenesis := types.DefaultGenesisState()

var votePeriod uint64
simState.AppParams.GetOrGenerate(
simState.Cdc, votePeriodKey, &votePeriod, simState.Rand,
Expand All @@ -95,10 +97,25 @@ func RandomizedGenState(simState *module.SimulationState) {
func(r *rand.Rand) { voteThreshold = GenVoteThreshold(r) },
)

var rewardBand sdk.Dec
var rewardBands types.RewardBandList
simState.AppParams.GetOrGenerate(
simState.Cdc, rewardBandKey, &rewardBand, simState.Rand,
func(r *rand.Rand) { rewardBand = GenRewardBand(r) },
simState.Cdc, rewardBandsKey, &rewardBands, simState.Rand,
func(r *rand.Rand) {
for _, denom := range oracleGenesis.Params.MandatoryList {
rb := types.RewardBand{
RewardBand: GenRewardBand(r),
SymbolDenom: denom.SymbolDenom,
}
rewardBands = append(rewardBands, rb)
}
for _, denom := range oracleGenesis.Params.AcceptList {
rb := types.RewardBand{
RewardBand: GenRewardBand(r),
SymbolDenom: denom.SymbolDenom,
}
rewardBands = append(rewardBands, rb)
}
},
)

var rewardDistributionWindow uint64
Expand Down Expand Up @@ -149,11 +166,10 @@ func RandomizedGenState(simState *module.SimulationState) {
func(r *rand.Rand) { maximumMedianStamps = GenMaximumMedianStamps(r) },
)

oracleGenesis := types.DefaultGenesisState()
oracleGenesis.Params = types.Params{
VotePeriod: votePeriod,
VoteThreshold: voteThreshold,
RewardBand: rewardBand,
RewardBands: rewardBands,
RewardDistributionWindow: rewardDistributionWindow,
AcceptList: types.DenomList{
{SymbolDenom: types.OjoSymbol, BaseDenom: types.OjoDenom},
Expand Down
2 changes: 1 addition & 1 deletion x/oracle/simulations/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func ParamChanges(_ *rand.Rand) []simtypes.ParamChange {
return fmt.Sprintf("\"%s\"", GenVoteThreshold(r))
},
),
simulation.NewSimParamChange(types.ModuleName, string(types.KeyRewardBand),
simulation.NewSimParamChange(types.ModuleName, string(types.KeyRewardBands),
func(r *rand.Rand) string {
return fmt.Sprintf("\"%s\"", GenRewardBand(r))
},
Expand Down
14 changes: 14 additions & 0 deletions x/oracle/types/denom.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package types
import (
"strings"

sdk "github.com/cosmos/cosmos-sdk/types"
"gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -57,3 +58,16 @@ func (dl DenomList) ContainDenoms(d DenomList) bool {

return true
}

// GetRewardBand returns the reward band of a given asset in the DenomList.
// It will return an error if it can not find it.
func (dl DenomList) GetRewardBand(rbl RewardBandList) (sdk.Dec, error) {
adamewozniak marked this conversation as resolved.
Show resolved Hide resolved
for _, d := range dl {
for _, rb := range rbl {
if strings.EqualFold(d.SymbolDenom, rb.SymbolDenom) {
return rb.RewardBand, nil
}
}
}
return sdk.ZeroDec(), ErrNoRewardBand
}
1 change: 1 addition & 0 deletions x/oracle/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ var (
ErrNoHistoricPrice = errors.Register(ModuleName, 19, "no historic price for this denom at this block")
ErrNoMedian = errors.Register(ModuleName, 20, "no median for this denom at this block")
ErrNoMedianDeviation = errors.Register(ModuleName, 21, "no median deviation for this denom at this block")
ErrNoRewardBand = errors.Register(ModuleName, 22, "unable to find the reward band the given asset")
)
4 changes: 2 additions & 2 deletions x/oracle/types/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ func TestGenesisValidation(t *testing.T) {

// Invalid Rewardband
genState = DefaultGenesisState()
genState.Params.RewardBand = sdk.NewDec(2)
genState.Params.RewardBands[0].RewardBand = sdk.NewDec(2)
require.Error(t, ValidateGenesis(genState))
genState.Params.RewardBand = sdk.NewDec(-1)
genState.Params.RewardBands[0].RewardBand = sdk.NewDec(-1)
require.Error(t, ValidateGenesis(genState))

// Invalid RewardDistributionWindow
Expand Down
Loading