Skip to content

Commit

Permalink
[Feature][oracle] Add whitelist and Update reward distribution mechan…
Browse files Browse the repository at this point in the history
…ism (#250)

* removes oracle module account from blacklist of bank module; enable send to oracle module

*  * append new param whitelist to filter oracle active target denoms
* change RewardFraction to RewardDistributionPeriod which indicates (RewardPool * VotePeriod / RewardDistributionPeriod) will be distributed as a reward to the oracle ballot winners in every vote period

* fix tally to correctly collect ballot losers
  • Loading branch information
yys authored and dokwon committed Oct 15, 2019
1 parent 4f9535d commit 8042329
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 122 deletions.
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

0 comments on commit 8042329

Please sign in to comment.