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

[Feature][oracle] Add whitelist and Update reward distribution mechanism #250

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ func NewTerraApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest

// add keepers
app.accountKeeper = auth.NewAccountKeeper(app.cdc, keys[auth.StoreKey], authSubspace, auth.ProtoBaseAccount)
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace, app.ModuleAccountAddrs())
sendBlackListAddrs := app.ModuleAccountAddrs()
delete(sendBlackListAddrs, oracle.ModuleName)
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace, sendBlackListAddrs)
app.supplyKeeper = supply.NewKeeper(app.cdc, keys[supply.StoreKey], app.accountKeeper, app.bankKeeper, maccPerms)
stakingKeeper := staking.NewKeeper(app.cdc, keys[staking.StoreKey], tkeys[staking.TStoreKey],
app.supplyKeeper, stakingSubspace, staking.DefaultCodespace)
Expand Down
66 changes: 37 additions & 29 deletions x/oracle/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func EndBlocker(ctx sdk.Context, k Keeper) {
actives := k.GetActiveDenoms(ctx)
votes := k.CollectVotes(ctx)

// Clear swap rates
// Clear prices
for _, activeDenom := range actives {
k.DeletePrice(ctx, activeDenom)
}
Expand All @@ -33,43 +33,51 @@ func EndBlocker(ctx sdk.Context, k Keeper) {
return false
})

// Changes whitelist array to map for fast lookup
whitelistMap := make(map[string]bool)
for _, denom := range k.Whitelist(ctx) {
whitelistMap[denom] = true
}

// Iterate through votes and update prices; drop if not enough votes have been achieved.
claimMap := make(map[string]types.Claim)
for denom, ballot := range votes {
if ballotIsPassing(ctx, ballot, k) {

// Get weighted median prices, and faithful respondants
mod, ballotWinners, ballotLosers := tally(ctx, ballot, k)
// Check whitelist; if denom is not exists or exists but the ballot is not passed, then skip
if _, exists := whitelistMap[denom]; !exists || !ballotIsPassing(ctx, ballot, k) {
continue
}

for _, loser := range ballotLosers {
key := loser.String()
if _, exists := ballotAttendees[key]; exists {
ballotAttendees[key] = false // inproper vote
}
}
// Get weighted median prices, and faithful respondants
mod, ballotWinners, ballotLosers := tally(ctx, ballot, k)

// Collect claims of ballot winners
for _, winner := range ballotWinners {
key := winner.Recipient.String()
claim, exists := claimMap[key]
if exists {
claim.Weight += winner.Weight
claimMap[key] = claim
} else {
claimMap[key] = winner
}
for _, loser := range ballotLosers {
key := loser.String()
if _, exists := ballotAttendees[key]; exists {
ballotAttendees[key] = false // inproper vote
}
}

// Set price to the store
k.SetLunaPrice(ctx, denom, mod)

ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EventTypePriceUpdate,
sdk.NewAttribute(types.AttributeKeyDenom, denom),
sdk.NewAttribute(types.AttributeKeyPrice, mod.String()),
),
)
// Collect claims of ballot winners
for _, winner := range ballotWinners {
key := winner.Recipient.String()
claim, exists := claimMap[key]
if exists {
claim.Weight += winner.Weight
claimMap[key] = claim
} else {
claimMap[key] = winner
}
}

// Set price to the store
k.SetLunaPrice(ctx, denom, mod)
ctx.EventManager().EmitEvent(
sdk.NewEvent(types.EventTypePriceUpdate,
sdk.NewAttribute(types.AttributeKeyDenom, denom),
sdk.NewAttribute(types.AttributeKeyPrice, mod.String()),
),
)
}

// Convert map to array
Expand Down
8 changes: 5 additions & 3 deletions x/oracle/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ func TestOracleRewardDistribution(t *testing.T) {

EndBlocker(input.Ctx.WithBlockHeight(1), input.OracleKeeper)

expectedRewardAmt := input.OracleKeeper.RewardFraction(input.Ctx).MulInt(stakingAmt.MulRaw(50)).TruncateInt()
expectedRewardAmt := sdk.NewDecFromInt(stakingAmt.MulRaw(50)).MulInt64(input.OracleKeeper.VotePeriod(input.Ctx)).QuoInt64(input.OracleKeeper.RewardDistributionPeriod(input.Ctx)).TruncateInt()
rewards := input.DistrKeeper.GetValidatorOutstandingRewards(input.Ctx.WithBlockHeight(2), keeper.ValAddrs[0])
require.Equal(t, expectedRewardAmt, rewards.AmountOf(core.MicroSDRDenom).TruncateInt())
rewards = input.DistrKeeper.GetValidatorOutstandingRewards(input.Ctx.WithBlockHeight(2), keeper.ValAddrs[1])
Expand Down Expand Up @@ -361,8 +361,10 @@ func TestOracleMultiRewardDistribution(t *testing.T) {

EndBlocker(input.Ctx.WithBlockHeight(1), input.OracleKeeper)

expectedRewardAmt := input.OracleKeeper.RewardFraction(input.Ctx).MulInt(stakingAmt.MulRaw(50)).TruncateInt()
expectedRewardAmt2 := input.OracleKeeper.RewardFraction(input.Ctx).MulInt(stakingAmt.MulRaw(25)).TruncateInt()
votePeriod := input.OracleKeeper.VotePeriod(input.Ctx)
rewardDistributedPeriod := input.OracleKeeper.RewardDistributionPeriod(input.Ctx)
expectedRewardAmt := sdk.NewDecFromInt(stakingAmt.MulRaw(50)).MulInt64(votePeriod).QuoInt64(rewardDistributedPeriod).TruncateInt()
expectedRewardAmt2 := sdk.NewDecFromInt(stakingAmt.MulRaw(25)).MulInt64(votePeriod).QuoInt64(rewardDistributedPeriod).TruncateInt()
rewards := input.DistrKeeper.GetValidatorOutstandingRewards(input.Ctx.WithBlockHeight(2), keeper.ValAddrs[0])
require.Equal(t, expectedRewardAmt, rewards.AmountOf(core.MicroSDRDenom).TruncateInt())
rewards = input.DistrKeeper.GetValidatorOutstandingRewards(input.Ctx.WithBlockHeight(2), keeper.ValAddrs[1])
Expand Down
40 changes: 21 additions & 19 deletions x/oracle/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,25 +87,27 @@ var (
NewQuerier = keeper.NewQuerier

// variable aliases
ModuleCdc = types.ModuleCdc
PrevoteKey = types.PrevoteKey
VoteKey = types.VoteKey
PriceKey = types.PriceKey
FeederDelegationKey = types.FeederDelegationKey
MissedVoteBitArrayKey = types.MissedVoteBitArrayKey
VotingInfoKey = types.VotingInfoKey
ParamStoreKeyVotePeriod = types.ParamStoreKeyVotePeriod
ParamStoreKeyVoteThreshold = types.ParamStoreKeyVoteThreshold
ParamStoreKeyRewardBand = types.ParamStoreKeyRewardBand
ParamStoreKeyRewardFraction = types.ParamStoreKeyRewardFraction
ParamStoreKeyVotesWindow = types.ParamStoreKeyVotesWindow
ParamStoreKeyMinValidVotesPerWindow = types.ParamStoreKeyMinValidVotesPerWindow
ParamStoreKeySlashFraction = types.ParamStoreKeySlashFraction
DefaultVoteThreshold = types.DefaultVoteThreshold
DefaultRewardBand = types.DefaultRewardBand
DefaultRewardFraction = types.DefaultRewardFraction
DefaultMinValidVotesPerWindow = types.DefaultMinValidVotesPerWindow
DefaultSlashFraction = types.DefaultSlashFraction
ModuleCdc = types.ModuleCdc
PrevoteKey = types.PrevoteKey
VoteKey = types.VoteKey
PriceKey = types.PriceKey
FeederDelegationKey = types.FeederDelegationKey
MissedVoteBitArrayKey = types.MissedVoteBitArrayKey
VotingInfoKey = types.VotingInfoKey
ParamStoreKeyVotePeriod = types.ParamStoreKeyVotePeriod
ParamStoreKeyVoteThreshold = types.ParamStoreKeyVoteThreshold
ParamStoreKeyRewardBand = types.ParamStoreKeyRewardBand
ParamStoreKeyRewardDistributionPeriod = types.ParamStoreKeyRewardDistributionPeriod
ParamStoreKeyVotesWindow = types.ParamStoreKeyVotesWindow
ParamStoreKeyMinValidVotesPerWindow = types.ParamStoreKeyMinValidVotesPerWindow
ParamStoreKeySlashFraction = types.ParamStoreKeySlashFraction
ParamStoreKeyWhitelist = types.ParamStoreKeyWhitelist
DefaultVoteThreshold = types.DefaultVoteThreshold
DefaultRewardBand = types.DefaultRewardBand
DefaultRewardDistributionPeriod = types.DefaultRewardDistributionPeriod
DefaultMinValidVotesPerWindow = types.DefaultMinValidVotesPerWindow
DefaultSlashFraction = types.DefaultSlashFraction
DefaultWhitelist = types.DefaultWhitelist
)

type (
Expand Down
16 changes: 8 additions & 8 deletions x/oracle/internal/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,17 +209,17 @@ func TestParams(t *testing.T) {
votesWindow := int64(2000)
minValidVotesPerWindow := sdk.NewDecWithPrec(1, 2)
slashFraction := sdk.NewDecWithPrec(5, 2)
rewardFraction := sdk.NewDecWithPrec(1, 2)
rewardDistributionPeriod := int64(10000000000000)

// Should really test validateParams, but skipping because obvious
newParams := types.Params{
VotePeriod: votePeriod,
VoteThreshold: voteThreshold,
RewardBand: oracleRewardBand,
VotesWindow: votesWindow,
MinValidVotesPerWindow: minValidVotesPerWindow,
SlashFraction: slashFraction,
RewardFraction: rewardFraction,
VotePeriod: votePeriod,
VoteThreshold: voteThreshold,
RewardBand: oracleRewardBand,
VotesWindow: votesWindow,
MinValidVotesPerWindow: minValidVotesPerWindow,
SlashFraction: slashFraction,
RewardDistributionPeriod: rewardDistributionPeriod,
}
input.OracleKeeper.SetParams(input.Ctx, newParams)

Expand Down
26 changes: 16 additions & 10 deletions x/oracle/internal/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,42 @@ import (
"github.com/terra-project/core/x/oracle/internal/types"
)

// ParamTable for staking module
// ParamKeyTable for staking module
func ParamKeyTable() params.KeyTable {
return params.NewKeyTable().RegisterParamSet(&types.Params{})
}

// VotePeriod
// VotePeriod returns the number of blocks during which voting takes place.
func (k Keeper) VotePeriod(ctx sdk.Context) (res int64) {
k.paramSpace.Get(ctx, types.ParamStoreKeyVotePeriod, &res)
return
}

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

// RewardBand
// RewardBand returns the ratio of allowable price error that can be rewared
func (k Keeper) RewardBand(ctx sdk.Context) (res sdk.Dec) {
k.paramSpace.Get(ctx, types.ParamStoreKeyRewardBand, &res)
return
}

// RewardFraction
func (k Keeper) RewardFraction(ctx sdk.Context) (res sdk.Dec) {
k.paramSpace.Get(ctx, types.ParamStoreKeyRewardFraction, &res)
// RewardDistributionPeriod returns the number of blocks of the the period during which seigiornage reward comes in and then is distributed.
func (k Keeper) RewardDistributionPeriod(ctx sdk.Context) (res int64) {
k.paramSpace.Get(ctx, types.ParamStoreKeyRewardDistributionPeriod, &res)
return
}

// VotesWindow
// VotesWindow returns the number of block units on which the penalty is based
func (k Keeper) VotesWindow(ctx sdk.Context) (res int64) {
k.paramSpace.Get(ctx, types.ParamStoreKeyVotesWindow, &res)
return
}

// MinValidVotesPerWindow
// MinValidVotesPerWindow returns the minimum number of blocks to avoid slashing in a window
func (k Keeper) MinValidVotesPerWindow(ctx sdk.Context) (res int64) {
var minValidVotesPerWindow sdk.Dec
k.paramSpace.Get(ctx, types.ParamStoreKeyMinValidVotesPerWindow, &minValidVotesPerWindow)
Expand All @@ -51,12 +51,18 @@ func (k Keeper) MinValidVotesPerWindow(ctx sdk.Context) (res int64) {
return minValidVotesPerWindow.MulInt64(signedBlocksWindow).RoundInt64()
}

// SlashFraction
// SlashFraction returns the slashing ratio on the delegated token
func (k Keeper) SlashFraction(ctx sdk.Context) (res sdk.Dec) {
k.paramSpace.Get(ctx, types.ParamStoreKeySlashFraction, &res)
return
}

// Whitelist returns the denom list that can be acitivated
func (k Keeper) Whitelist(ctx sdk.Context) (res types.DenomList) {
k.paramSpace.Get(ctx, types.ParamStoreKeyWhitelist, &res)
return
}

// GetParams returns the total set of oracle parameters.
func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
k.paramSpace.GetParamSet(ctx, &params)
Expand Down
15 changes: 10 additions & 5 deletions x/oracle/internal/keeper/reward.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,22 @@ func (k Keeper) RewardBallotWinners(ctx sdk.Context, ballotWinners types.ClaimPo
if prevBallotWeightSum != 0 {
rewardPool := k.getRewardPool(ctx)
if !rewardPool.Empty() {
// In case rewardFraction = 1%; 1/100 module balance will be distributed
rewardFraction := k.RewardFraction(ctx)
// rewardCoin = (oraclePool / rewardDistributionPeriod) * votePeriod
rewardDistributionPeriod := k.RewardDistributionPeriod(ctx)
votePeriod := k.VotePeriod(ctx)

// Dole out rewards
var distributedReward sdk.Coins
for _, winner := range ballotWinners {
rewardCoins := sdk.NewCoins()
rewardeeVal := k.StakingKeeper.Validator(ctx, winner.Recipient)
for _, feeCoin := range rewardPool {
rewardAmt := sdk.NewDecFromInt(feeCoin.Amount).Mul(rewardFraction).QuoInt64(prevBallotWeightSum).MulInt64(winner.Weight).TruncateInt()
rewardCoins = append(rewardCoins, sdk.NewCoin(feeCoin.Denom, rewardAmt))

for _, poolCoin := range rewardPool {
// The amount of the coin will be distributed in this vote period
totalRewardAmt := sdk.NewDecFromInt(poolCoin.Amount).MulInt64(votePeriod).QuoInt64(rewardDistributionPeriod)
// Reflects contribution
rewardAmt := totalRewardAmt.QuoInt64(prevBallotWeightSum).MulInt64(winner.Weight).TruncateInt()
rewardCoins = append(rewardCoins, sdk.NewCoin(poolCoin.Denom, rewardAmt))
}

// In case absence of the validator, we just skip distribution
Expand Down
14 changes: 9 additions & 5 deletions x/oracle/internal/keeper/reward_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,22 @@ func TestRewardBallotWinners(t *testing.T) {
claimPool := types.ClaimPool{claim, claim2}

// Prepare reward pool
givingAmt := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 3000))
givingAmt := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 3000000))
acc := input.SupplyKeeper.GetModuleAccount(ctx, types.ModuleName)
err := acc.SetCoins(givingAmt)
require.NoError(t, err)
input.SupplyKeeper.SetModuleAccount(ctx, acc)

votePeriod := input.OracleKeeper.VotePeriod(input.Ctx)
rewardDistributionPeriod := input.OracleKeeper.RewardDistributionPeriod(input.Ctx)
input.OracleKeeper.RewardBallotWinners(ctx, claimPool)
outstandingRewards := input.DistrKeeper.GetValidatorOutstandingRewards(ctx, addr)
require.Equal(t, sdk.NewDecFromInt(givingAmt.AmountOf(core.MicroLunaDenom)).Mul(input.OracleKeeper.RewardFraction(ctx)).QuoInt64(3),
outstandingRewardsDec := input.DistrKeeper.GetValidatorOutstandingRewards(ctx, addr)
outstandingRewards, _ := outstandingRewardsDec.TruncateDecimal()
require.Equal(t, sdk.NewDecFromInt(givingAmt.AmountOf(core.MicroLunaDenom)).QuoInt64(rewardDistributionPeriod).MulInt64(votePeriod).QuoInt64(3).TruncateInt(),
outstandingRewards.AmountOf(core.MicroLunaDenom))

outstandingRewards1 := input.DistrKeeper.GetValidatorOutstandingRewards(ctx, addr1)
require.Equal(t, sdk.NewDecFromInt(givingAmt.AmountOf(core.MicroLunaDenom)).Mul(input.OracleKeeper.RewardFraction(ctx)).QuoInt64(3).MulInt64(2),
outstandingRewardsDec1 := input.DistrKeeper.GetValidatorOutstandingRewards(ctx, addr1)
outstandingRewards1, _ := outstandingRewardsDec1.TruncateDecimal()
require.Equal(t, sdk.NewDecFromInt(givingAmt.AmountOf(core.MicroLunaDenom)).QuoInt64(rewardDistributionPeriod).MulInt64(votePeriod).QuoInt64(3).MulInt64(2).TruncateInt(),
outstandingRewards1.AmountOf(core.MicroLunaDenom))
}
Loading