-
Notifications
You must be signed in to change notification settings - Fork 291
/
end_blocker.go
133 lines (104 loc) · 3.75 KB
/
end_blocker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package oracle
import (
"github.com/terra-project/core/types/util"
"sort"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/terra-project/core/types"
"github.com/terra-project/core/x/oracle/tags"
)
// Calculates the median and returns the set of voters to be rewarded, i.e. voted within
// a reasonable spread from the weighted median.
func tally(ctx sdk.Context, k Keeper, pb PriceBallot) (weightedMedian sdk.Dec, ballotWinners types.ClaimPool) {
if !sort.IsSorted(pb) {
sort.Sort(pb)
}
ballotWinners = types.ClaimPool{}
weightedMedian = pb.weightedMedian(ctx, k.valset)
rewardSpread := k.GetParams(ctx).OracleRewardBand.QuoInt64(2)
for _, vote := range pb {
if vote.Price.GTE(weightedMedian.Sub(rewardSpread)) && vote.Price.LTE(weightedMedian.Add(rewardSpread)) {
valAddr := sdk.ValAddress(vote.Voter)
if validator := k.valset.Validator(ctx, valAddr); validator != nil {
bondSize := validator.GetBondedTokens()
ballotWinners = append(ballotWinners, types.Claim{
Recipient: vote.Voter,
Weight: bondSize,
Class: types.OracleClaimClass,
})
}
}
}
return
}
// Drop the ballot. If the ballot drops params.DropThreshold times sequentially, then blacklist
func dropBallot(ctx sdk.Context, k Keeper, denom string, params Params) sdk.Tags {
actionTag := tags.ActionTallyDropped
// Not enough votes received
dropCounter := k.incrementDropCounter(ctx, denom)
if dropCounter.GTE(params.DropThreshold) {
// Too many drops, blacklist currency
k.deletePrice(ctx, denom)
k.resetDropCounter(ctx, denom)
actionTag = tags.ActionBlacklist
}
return sdk.NewTags(
tags.Action, actionTag,
tags.Denom, denom,
)
}
// ballot for the asset is passing the threshold amount of voting power
func ballotIsPassing(totalBondedTokens sdk.Int, voteThreshold sdk.Dec, ballotPower sdk.Int) bool {
thresholdVotes := voteThreshold.MulInt(totalBondedTokens).RoundInt()
return ballotPower.GTE(thresholdVotes)
}
// EndBlocker is called at the end of every block
func EndBlocker(ctx sdk.Context, k Keeper) (rewardees types.ClaimPool, resTags sdk.Tags) {
params := k.GetParams(ctx)
// Not yet time for a tally
if !util.IsPeriodLastBlock(ctx, params.VotePeriod) {
return
}
actives := k.getActiveDenoms(ctx)
votes := k.collectVotes(ctx)
// Iterate through active oracle assets and drop assets that have no votes received.
for _, activeDenom := range actives {
if _, found := votes[activeDenom]; !found {
dropTags := dropBallot(ctx, k, activeDenom, params)
resTags = resTags.AppendTags(dropTags)
}
}
rewardees = types.ClaimPool{}
totalBondedTokens := k.valset.TotalBondedTokens(ctx)
// Iterate through votes and update prices; drop if not enough votes have been achieved.
for denom, filteredVotes := range votes {
if ballotIsPassing(totalBondedTokens, params.VoteThreshold, filteredVotes.power(ctx, k.valset)) {
// Get weighted median prices, and faithful respondants
mod, ballotWinners := tally(ctx, k, filteredVotes)
// Append ballot winners for the denom
rewardees = append(rewardees, ballotWinners...)
actionTag := tags.ActionPriceUpdate
if _, err := k.GetLunaSwapRate(ctx, denom); err != nil {
actionTag = tags.ActionWhitelist
}
// Set price to the store
k.SetLunaSwapRate(ctx, denom, mod)
// Reset drop counter for the passed ballot
k.resetDropCounter(ctx, denom)
resTags = resTags.AppendTags(
sdk.NewTags(
tags.Action, actionTag,
tags.Denom, denom,
tags.Price, mod.String(),
),
)
} else {
dropTags := dropBallot(ctx, k, denom, params)
resTags = resTags.AppendTags(dropTags)
}
// Clear all votes
k.iterateVotes(ctx, func(vote PriceVote) (stop bool) { k.deleteVote(ctx, vote); return false })
}
// Sort rewardees before we return
rewardees.Sort()
return
}