-
Notifications
You must be signed in to change notification settings - Fork 41
/
ante.go
143 lines (127 loc) · 4.15 KB
/
ante.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
package ante
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/authz"
lru "github.com/hashicorp/golang-lru"
"github.com/bandprotocol/chain/v2/x/oracle/keeper"
"github.com/bandprotocol/chain/v2/x/oracle/types"
)
var (
firstBlockSeen *lru.Cache
nextRepOnlyBlock int64
)
func init() {
var err error
firstBlockSeen, err = lru.New(20000)
if err != nil {
panic(err)
}
}
func checkValidReportMsg(ctx sdk.Context, oracleKeeper keeper.Keeper, r *types.MsgReportData) error {
validator, err := sdk.ValAddressFromBech32(r.Validator)
if err != nil {
return err
}
report := types.NewReport(validator, false, r.RawReports)
return oracleKeeper.CheckValidReport(ctx, r.RequestID, report)
}
func updateCache(val string, rid types.RequestID, block int64) (trigger bool) {
key := fmt.Sprintf("%s:%d", val, rid)
value, ok := firstBlockSeen.Get(key)
// Check if we already seen this report
if ok {
start := value.(int64)
// If the report has been seen more than 10 then make the next block will be only reporter
return block-start > 10
} else {
firstBlockSeen.Add(key, block)
return false
}
}
// NewFeelessReportsAnteHandler returns a new ante handler that waives minimum gas price
// requirement if the incoming tx is a valid report transaction.
func NewFeelessReportsAnteHandler(ante sdk.AnteHandler, oracleKeeper keeper.Keeper) sdk.AnteHandler {
return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
if ctx.IsCheckTx() && !simulate {
isRepOnlyBlock := ctx.BlockHeight() == nextRepOnlyBlock
isValidReportTx := true
for _, msg := range tx.GetMsgs() {
// Check direct report msg
if dr, ok := msg.(*types.MsgReportData); ok {
// Check if it's not valid report msg, discard this transaction
if err := checkValidReportMsg(ctx, oracleKeeper, dr); err != nil {
return ctx, err
}
if !isRepOnlyBlock {
if updateCache(dr.Validator, dr.RequestID, ctx.BlockHeight()) {
nextRepOnlyBlock = ctx.BlockHeight() + 1
}
}
} else {
// Check is the MsgExec from reporter
me, ok := msg.(*authz.MsgExec)
if !ok {
isValidReportTx = false
break
}
// If cannot get message, then pretend as non-free transaction
msgs, err := me.GetMessages()
if err != nil {
isValidReportTx = false
break
}
grantee, err := sdk.AccAddressFromBech32(me.Grantee)
if err != nil {
isValidReportTx = false
break
}
allValidReportMsg := true
for _, m := range msgs {
r, ok := m.(*types.MsgReportData)
// If this is not report msg, skip other msgs on this exec msg
if !ok {
allValidReportMsg = false
break
}
// Fail to parse validator, then discard this transaction
validator, err := sdk.ValAddressFromBech32(r.Validator)
if err != nil {
return ctx, err
}
// If this grantee is not a reporter of validator, then discard this transaction
if !oracleKeeper.IsReporter(ctx, validator, grantee) {
return ctx, sdkerrors.ErrUnauthorized.Wrap("authorization not found")
}
// Check if it's not valid report msg, discard this transaction
if err := checkValidReportMsg(ctx, oracleKeeper, r); err != nil {
return ctx, err
}
// Update cache in case it's a valid report
if !isRepOnlyBlock {
if updateCache(r.Validator, r.RequestID, ctx.BlockHeight()) {
nextRepOnlyBlock = ctx.BlockHeight() + 1
}
}
}
// If this exec msg has other non-report msg, disable feeless and skip other msgs in tx
if !allValidReportMsg {
isValidReportTx = false
break
}
}
}
if isRepOnlyBlock && !isValidReportTx {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Block reserved for report txs")
}
if isValidReportTx {
minGas := ctx.MinGasPrices()
newCtx, err := ante(ctx.WithMinGasPrices(sdk.DecCoins{}), tx, simulate)
// Set minimum gas price context and return context to caller.
return newCtx.WithMinGasPrices(minGas), err
}
}
return ante(ctx, tx, simulate)
}
}