forked from terra-money/classic-core
-
Notifications
You must be signed in to change notification settings - Fork 1
/
vote.go
146 lines (118 loc) · 3.04 KB
/
vote.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
134
135
136
137
138
139
140
141
142
143
144
145
146
package oracle
import (
"fmt"
"math/big"
"sort"
sdk "github.com/cosmos/cosmos-sdk/types"
)
//-------------------------------------------------
//-------------------------------------------------
// PriceVote - struct to store a validator's vote on the price
type PriceVote struct {
Price sdk.Dec `json:"price"` // Price of Luna in target fiat currency
Denom string `json:"denom"` // Ticker name of target fiat currency
Power sdk.Int `json:"power"` // Total bonded tokens of validator
Voter sdk.AccAddress `json:"voter"` // account address of validator
}
// NewPriceVote creates a PriceVote instance
func NewPriceVote(price sdk.Dec, denom string, power sdk.Int, voter sdk.AccAddress) PriceVote {
return PriceVote{
Price: price,
Denom: denom,
Power: power,
Voter: voter,
}
}
func (pv PriceVote) String() string {
return fmt.Sprintf("PriceVote{ Denom %s, Voter %s, Power %v, Price %v }", pv.Denom, pv.Voter, pv.Power, pv.Price)
}
type PriceBallot []PriceVote
// String implements fmt.Stringer interface
func (pb PriceBallot) String() (out string) {
for _, pv := range pb {
out += fmt.Sprintf("\n %s", pv.String())
}
return
}
// Len implements sort.Interface
func (pb PriceBallot) Len() int {
return len(pb)
}
// Less reports whether the element with
// index i should sort before the element with index j.
func (pb PriceBallot) Less(i, j int) bool {
return pb[i].Price.LTE(pb[j].Price)
}
func (pb PriceBallot) Swap(i, j int) {
pb[i], pb[j] = pb[j], pb[i]
}
func (pb PriceBallot) totalPower() sdk.Int {
sumWeight := sdk.ZeroInt()
for _, v := range pb {
sumWeight = sumWeight.Add(v.Power)
}
return sumWeight
}
func (pb PriceBallot) allVoters() []sdk.AccAddress {
allVoters := []sdk.AccAddress{}
for _, v := range pb {
allVoters = append(allVoters, v.Voter)
}
return allVoters
}
func (pb PriceBallot) weightedMedian() (i int64, mod PriceVote) {
if len(pb) == 0 {
mod = PriceVote{}
return
}
if !sort.IsSorted(pb) {
sort.Sort(pb)
}
voterTotalPower := pb.totalPower()
sumWeight := sdk.ZeroInt()
for _, v := range pb {
if sumWeight.GTE(voterTotalPower.Div(sdk.NewInt(2))) {
break
}
i++
sumWeight = sumWeight.Add(v.Power)
mod = v
}
return
}
func (pb PriceBallot) mean() (mean sdk.Dec) {
if len(pb) == 0 {
return sdk.ZeroDec()
}
sumPrice := sdk.ZeroDec()
i := 0
for _, v := range pb {
i++
sumPrice = sumPrice.Add(v.Price)
}
mean = sumPrice.QuoInt(sdk.NewInt(int64(i)))
return
}
func (pb PriceBallot) stdDev() (stdDev sdk.Dec) {
mean := pb.mean()
sumCalc := sdk.ZeroDec()
for _, v := range pb {
spread := v.Price.Sub(mean)
sumCalc = sumCalc.Add(spread.Mul(spread))
}
temp := sumCalc.Sqrt(big.NewInt(2))
stdDev = sdk.NewDecFromBigInt(temp)
return
}
func (pb PriceBallot) tally() (median sdk.Dec, rewardees []PriceVote) {
sort.Sort(pb)
_, modStub := pb.weightedMedian()
median = modStub.Price
stdDev := pb.stdDev()
for _, stub := range pb {
if stub.Price.GTE(median.Sub(stdDev)) || stub.Price.LTE(median.Add(stdDev)) {
rewardees = append(rewardees, stub)
}
}
return
}