-
Notifications
You must be signed in to change notification settings - Fork 39
/
tally.go
85 lines (70 loc) · 2.87 KB
/
tally.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
package oracle
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/classic-terra/core/v2/x/oracle/keeper"
"github.com/classic-terra/core/v2/x/oracle/types"
)
// Tally calculates the median and returns it. Sets the set of voters to be rewarded, i.e. voted within
// a reasonable spread from the weighted median to the store
// CONTRACT: pb must be sorted
func Tally(pb types.ExchangeRateBallot, rewardBand sdk.Dec, validatorClaimMap map[string]types.Claim) (weightedMedian sdk.Dec) {
weightedMedian = pb.WeightedMedian()
standardDeviation := pb.StandardDeviation(weightedMedian)
rewardSpread := weightedMedian.Mul(rewardBand.QuoInt64(2))
if standardDeviation.GT(rewardSpread) {
rewardSpread = standardDeviation
}
for _, vote := range pb {
// Filter ballot winners & abstain voters
if (vote.ExchangeRate.GTE(weightedMedian.Sub(rewardSpread)) &&
vote.ExchangeRate.LTE(weightedMedian.Add(rewardSpread))) ||
!vote.ExchangeRate.IsPositive() {
key := vote.Voter.String()
claim := validatorClaimMap[key]
claim.Weight += vote.Power
claim.WinCount++
validatorClaimMap[key] = claim
}
}
return weightedMedian
}
// ballot for the asset is passing the threshold amount of voting power
func ballotIsPassing(ballot types.ExchangeRateBallot, thresholdVotes sdk.Int) (sdk.Int, bool) {
ballotPower := sdk.NewInt(ballot.Power())
return ballotPower, !ballotPower.IsZero() && ballotPower.GTE(thresholdVotes)
}
// PickReferenceTerra choose Reference Terra with the highest voter turnout
// If the voting power of the two denominations is the same,
// select reference Terra in alphabetical order.
func PickReferenceTerra(ctx sdk.Context, k keeper.Keeper, voteTargets map[string]sdk.Dec, voteMap map[string]types.ExchangeRateBallot) string {
largestBallotPower := int64(0)
referenceTerra := ""
totalBondedPower := sdk.TokensToConsensusPower(k.StakingKeeper.TotalBondedTokens(ctx), k.StakingKeeper.PowerReduction(ctx))
voteThreshold := k.VoteThreshold(ctx)
thresholdVotes := voteThreshold.MulInt64(totalBondedPower).RoundInt()
for denom, ballot := range voteMap {
// If denom is not in the voteTargets, or the ballot for it has failed, then skip
// and remove it from voteMap for iteration efficiency
if _, exists := voteTargets[denom]; !exists {
delete(voteMap, denom)
continue
}
ballotPower := int64(0)
// If the ballot is not passed, remove it from the voteTargets array
// to prevent slashing validators who did valid vote.
if power, ok := ballotIsPassing(ballot, thresholdVotes); ok {
ballotPower = power.Int64()
} else {
delete(voteTargets, denom)
delete(voteMap, denom)
continue
}
if ballotPower > largestBallotPower || largestBallotPower == 0 {
referenceTerra = denom
largestBallotPower = ballotPower
} else if largestBallotPower == ballotPower && referenceTerra > denom {
referenceTerra = denom
}
}
return referenceTerra
}