Skip to content

Commit

Permalink
feat: individual reward bands per denom (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamewozniak authored Feb 15, 2023
1 parent 7c4f3a4 commit ad5a237
Show file tree
Hide file tree
Showing 15 changed files with 521 additions and 111 deletions.
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
9 changes: 8 additions & 1 deletion x/oracle/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,15 @@ 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)

// Get the current denom's reward band
rewardBand, err := params.RewardBands.GetBandFromDenom(ballotDenom.Denom)
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
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

0 comments on commit ad5a237

Please sign in to comment.