forked from terra-money/classic-core
-
Notifications
You must be signed in to change notification settings - Fork 1
/
handler.go
130 lines (106 loc) · 3.31 KB
/
handler.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
package oracle
import (
"terra/x/oracle/tags"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// NewHandler returns a handler for "oracle" type messages.
func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case PriceFeedMsg:
return handlePriceFeedMsg(ctx, k, msg)
default:
errMsg := "Unrecognized oracle Msg type: %s" + msg.Type()
return sdk.ErrUnknownRequest(errMsg).Result()
}
}
}
// EndBlocker is called at the end of every block
func EndBlocker(ctx sdk.Context, k Keeper) (prices map[string]sdk.Dec, rewardees map[string]sdk.Int, resTags sdk.Tags) {
params := k.GetParams(ctx)
votes := k.getVotes(ctx)
// Tally vote for oracle prices
if sdk.NewInt(ctx.BlockHeight()).Mod(params.VotePeriod).Equal(sdk.ZeroInt()) {
for denom, filVotes := range votes {
votePower := filVotes.totalPower()
// Not enough validators have voted, skip
thresholdVotes := params.VoteThreshold.MulInt(k.valset.TotalBondedTokens(ctx)).TruncateInt()
if votePower.LT(thresholdVotes) {
resTags = resTags.AppendTags(
sdk.NewTags(
tags.Action, tags.ActionTallyDropped,
tags.Denom, []byte(denom),
),
)
dropCounter := k.getDropCounter(ctx, denom)
if dropCounter.GT(params.DropThreshold) {
// Too many drops, blacklist currency
k.deletePrice(ctx, denom)
k.deleteDropCounter(ctx, denom)
resTags = resTags.AppendTags(
sdk.NewTags(
tags.Action, tags.ActionBlacklist,
tags.Denom, []byte(denom),
),
)
} else {
dropCounter = dropCounter.Add(sdk.OneInt())
k.setDropCounter(ctx, denom, dropCounter)
}
continue
}
// Get weighted median prices, and faithful respondants
mod, loyalVotes := filVotes.tally()
prices[denom] = mod
for _, lv := range loyalVotes {
voterAddrStr := lv.Voter.String()
rewardees[voterAddrStr] = rewardees[voterAddrStr].Add(lv.Power)
}
// Emit whitelist tag if the price is coming in for the first time
_, err := k.GetPrice(ctx, denom)
if err != nil {
resTags = resTags.AppendTags(
sdk.NewTags(
tags.Action, tags.ActionWhitelist,
tags.Denom, []byte(denom),
),
)
}
// Set the price for the asset
k.setPrice(ctx, denom, mod)
// Emit price update tag
resTags = resTags.AppendTags(
sdk.NewTags(
tags.Action, tags.ActionPriceUpdate,
tags.Denom, []byte(denom),
tags.Price, mod.Bytes(),
),
)
}
// Clear all votes
k.iterateVotes(ctx, func(vote PriceVote) (stop bool) { k.deleteVote(ctx, vote); return false })
}
return
}
// handlePriceFeedMsg is used by other modules to handle Msg
func handlePriceFeedMsg(ctx sdk.Context, keeper Keeper, pfm PriceFeedMsg) sdk.Result {
valset := keeper.valset
signer := pfm.Feeder
// Check the feeder is a validater
val := valset.Validator(ctx, sdk.ValAddress(signer.Bytes()))
if val == nil {
return ErrNotValidator(DefaultCodespace, pfm.Feeder).Result()
}
// Add the vote to the store
vote := NewPriceVote(pfm.Price, pfm.Denom, val.GetBondedTokens(), signer)
keeper.addVote(ctx, vote)
return sdk.Result{
Tags: sdk.NewTags(
tags.Action, tags.ActionVoteSubmitted,
tags.Denom, []byte(pfm.Denom),
tags.Voter, pfm.Feeder.Bytes(),
tags.Power, val.GetBondedTokens(),
tags.Price, pfm.Price.Bytes(),
),
}
}