-
Notifications
You must be signed in to change notification settings - Fork 12
/
querier.go
480 lines (432 loc) · 16.2 KB
/
querier.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
package keeper
import (
"strconv"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
abci "github.com/tendermint/tendermint/abci/types"
fxtypes "github.com/functionx/fx-core/v2/types"
"github.com/functionx/fx-core/v2/x/crosschain/types"
)
const MaxResults = 100
const maxOracleSetRequestsReturned = 5
const (
// OracleSets
// QueryOracleSetRequest This retrieves a specific validator set by it's nonce
// used to compare what's on Ethereum with what's in Cosmos
// to perform slashing / validation of system consistency
QueryOracleSetRequest = "oracleSetRequest"
// QueryOracleSetConfirmsByNonce Gets all the confirmation signatures for a given validator
// set, used by the relayer to package the validator set and
// it's signatures into an Ethereum transaction
QueryOracleSetConfirmsByNonce = "oracleSetConfirms"
// QueryLastOracleSetRequests Gets the last N (where N is currently 5) validator sets that
// have been produced by the chain. Useful to see if any recently
// signed requests can be submitted.
QueryLastOracleSetRequests = "lastOracleSetRequests"
// QueryLastPendingOracleSetRequestByAddr Gets a list of unsigned oracleSets for a given validators delegate
// bridger address. Up to 100 are sent at a time
QueryLastPendingOracleSetRequestByAddr = "lastPendingOracleSetRequest"
QueryCurrentOracleSet = "currentOracleSet"
QueryOracleSetConfirm = "oracleSetConfirm"
// QueryGravityID used by the contract deployer script. GravityID is set in the Genesis
// file, then read by the contract deployer and deployed to Ethereum
// a unique GravityID ensures that even if the same validator set with
// the same keys is running on two chains these chains can have independent
// bridges
QueryGravityID = "gravityID"
// Batches
// note the current logic here constrains batch throughput to one
// batch (of any type) per Cosmos block.
// QueryBatch This retrieves a specific batch by it's nonce and token contract
// or in the case of a Cosmos originated address it's denom
QueryBatch = "batch"
// QueryLastPendingBatchRequestByAddr Get the last unsigned batch (of any denom) for the validators
// bridger to sign
QueryLastPendingBatchRequestByAddr = "lastPendingBatchRequest"
// QueryOutgoingTxBatches gets the last 100 outgoing batches, regardless of denom, useful
// for a relayed to see what is available to relay
QueryOutgoingTxBatches = "lastBatches"
// QueryBatchConfirms Used by the relayer to package a batch with signatures required
// to submit to Ethereum
QueryBatchConfirms = "batchConfirms"
// QueryBatchFees Used to query all pending SendToEth transactions and fees available for each
// token type, a relayer can then estimate their potential profit when requesting
// a batch
QueryBatchFees = "batchFees"
// QueryTokenToDenom Token mapping
// This retrieves the denom which is represented by a given ERC20 contract
QueryTokenToDenom = "TokenToDenom"
// QueryDenomToToken This retrieves the ERC20 contract which represents a given denom
QueryDenomToToken = "DenomToToken"
// QueryPendingSendToExternal Query pending transactions
QueryPendingSendToExternal = "PendingSendToExternal"
)
// NewQuerier is the module level router for state queries
func NewQuerier(keeper Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err error) {
if len(path) <= 0 {
return nil, sdkerrors.ErrInvalidRequest
}
switch path[0] {
// OracleSets
case QueryCurrentOracleSet:
return queryCurrentOracleSet(ctx, keeper)
case QueryOracleSetRequest:
if len(path) != 2 {
return nil, sdkerrors.ErrInvalidRequest
}
return queryOracleSetRequest(ctx, path[1], keeper)
case QueryOracleSetConfirm:
if len(path) != 3 {
return nil, sdkerrors.ErrInvalidRequest
}
return queryOracleSetConfirm(ctx, path[1], path[2], keeper)
case QueryOracleSetConfirmsByNonce:
if len(path) != 2 {
return nil, sdkerrors.ErrInvalidRequest
}
return queryAllOracleSetConfirms(ctx, path[1], keeper)
case QueryLastOracleSetRequests:
return lastOracleSetRequests(ctx, keeper)
case QueryLastPendingOracleSetRequestByAddr:
if len(path) != 2 {
return nil, sdkerrors.ErrInvalidRequest
}
return lastPendingOracleSetRequest(ctx, path[1], keeper)
// Batches
case QueryBatch:
if len(path) != 3 {
return nil, sdkerrors.ErrInvalidRequest
}
return queryBatch(ctx, path[1], path[2], keeper)
case QueryBatchConfirms:
if len(path) != 3 {
return nil, sdkerrors.ErrInvalidRequest
}
return queryAllBatchConfirms(ctx, path[1], path[2], keeper)
case QueryLastPendingBatchRequestByAddr:
if len(path) != 2 {
return nil, sdkerrors.ErrInvalidRequest
}
return lastPendingBatchRequest(ctx, path[1], keeper)
case QueryOutgoingTxBatches:
return lastBatchesRequest(ctx, keeper)
case QueryBatchFees:
return queryBatchFees(ctx, keeper, req, legacyQuerierCdc)
case QueryGravityID:
return queryGravityID(ctx, keeper)
// Token mappings
case QueryDenomToToken:
if len(path) != 2 {
return nil, sdkerrors.ErrInvalidRequest
}
return queryDenomToToken(ctx, path[1], keeper)
case QueryTokenToDenom:
if len(path) != 2 {
return nil, sdkerrors.ErrInvalidRequest
}
return queryTokenToDenom(ctx, path[1], keeper)
// Pending transactions
case QueryPendingSendToExternal:
if len(path) != 2 {
return nil, sdkerrors.ErrInvalidRequest
}
return queryPendingSendToExternal(ctx, path[1], keeper)
default:
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", keeper.moduleName)
}
}
}
func queryOracleSetRequest(ctx sdk.Context, nonceStr string, keeper Keeper) ([]byte, error) {
nonce, err := uint64FromString(nonceStr)
if err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalid, "nonce")
}
oracleSet := keeper.GetOracleSet(ctx, nonce)
if oracleSet == nil {
return nil, nil
}
res, err := codec.MarshalJSONIndent(types.ModuleCdc, oracleSet)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return res, nil
}
// allOracleSetConfirmsByNonce returns all the confirm messages for a given nonce
// When nothing found an empty json array is returned. No pagination.
func queryAllOracleSetConfirms(ctx sdk.Context, nonceStr string, keeper Keeper) ([]byte, error) {
nonce, err := uint64FromString(nonceStr)
if err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalid, "nonce")
}
var confirms []*types.MsgOracleSetConfirm
keeper.IterateOracleSetConfirmByNonce(ctx, nonce, func(_ []byte, c types.MsgOracleSetConfirm) bool {
confirms = append(confirms, &c)
return false
})
if len(confirms) == 0 {
return nil, nil
}
res, err := codec.MarshalJSONIndent(types.ModuleCdc, confirms)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return res, nil
}
// allBatchConfirms returns all the confirm messages for a given nonce
// When nothing found an empty json array is returned. No pagination.
func queryAllBatchConfirms(ctx sdk.Context, nonceStr string, tokenContract string, keeper Keeper) ([]byte, error) {
nonce, err := uint64FromString(nonceStr)
if err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalid, "nonce")
}
if len(tokenContract) <= 0 {
return nil, sdkerrors.Wrap(types.ErrInvalid, "token contract")
}
var confirms []types.MsgConfirmBatch
keeper.IterateBatchConfirmByNonceAndTokenContract(ctx, nonce, tokenContract, func(_ []byte, c types.MsgConfirmBatch) bool {
confirms = append(confirms, c)
return false
})
if len(confirms) == 0 {
return nil, nil
}
res, err := codec.MarshalJSONIndent(types.ModuleCdc, confirms)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return res, nil
}
// lastOracleSetRequests returns up to maxOracleSetRequestsReturned oracleSets from the store
func lastOracleSetRequests(ctx sdk.Context, keeper Keeper) ([]byte, error) {
var valReq []*types.OracleSet
keeper.IterateOracleSets(ctx, func(_ []byte, val *types.OracleSet) bool {
valReq = append(valReq, val)
return false
})
valReqLen := len(valReq)
retLen := 0
if valReqLen < maxOracleSetRequestsReturned {
retLen = valReqLen
} else {
retLen = maxOracleSetRequestsReturned
}
res, err := codec.MarshalJSONIndent(types.ModuleCdc, valReq[0:retLen])
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return res, nil
}
// lastPendingOracleSetRequest gets a list of validator sets that this validator has not signed
// limited by 100 sets per request.
func lastPendingOracleSetRequest(ctx sdk.Context, bridgerAddr string, keeper Keeper) ([]byte, error) {
bridger, err := sdk.AccAddressFromBech32(bridgerAddr)
if err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalid, "bridger address")
}
var pendingOracleSetReq []*types.OracleSet
keeper.IterateOracleSets(ctx, func(_ []byte, val *types.OracleSet) bool {
// foundConfirm is true if the operatorAddr has signed the oracleSet we are currently looking at
foundConfirm := keeper.GetOracleSetConfirm(ctx, val.Nonce, bridger) != nil
// if this oracleSet has NOT been signed by operatorAddr, store it in pendingOracleSetReq
// and exit the loop
if !foundConfirm {
pendingOracleSetReq = append(pendingOracleSetReq, val)
}
// if we have more than 100 unconfirmed requests in
// our array we should exit, pagination
if len(pendingOracleSetReq) > MaxResults {
return true
}
// return false to continue the loop
return false
})
if len(pendingOracleSetReq) == 0 {
return nil, nil
}
res, err := codec.MarshalJSONIndent(types.ModuleCdc, pendingOracleSetReq)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return res, nil
}
func queryCurrentOracleSet(ctx sdk.Context, keeper Keeper) ([]byte, error) {
oracleSet := keeper.GetCurrentOracleSet(ctx)
res, err := codec.MarshalJSONIndent(types.ModuleCdc, oracleSet)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return res, nil
}
// queryOracleSetConfirm returns the confirm msg for single bridger address and nonce
// When nothing found a nil value is returned
func queryOracleSetConfirm(ctx sdk.Context, nonceStr, bridgerAddr string, keeper Keeper) ([]byte, error) {
nonce, err := uint64FromString(nonceStr)
if err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalid, "nonce")
}
bridger, err := sdk.AccAddressFromBech32(bridgerAddr)
if err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalid, "bridger address")
}
oracleSet := keeper.GetOracleSetConfirm(ctx, nonce, bridger)
if oracleSet == nil {
return nil, nil
}
res, err := codec.MarshalJSONIndent(types.ModuleCdc, *oracleSet)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return res, nil
}
type MultiSigUpdateResponse struct {
OracleSet types.OracleSet `json:"oracleSet"`
Signatures [][]byte `json:"signatures,omitempty"`
}
// lastPendingBatchRequest gets the latest batch that has NOT been signed by operatorAddr
func lastPendingBatchRequest(ctx sdk.Context, bridgerAddr string, keeper Keeper) ([]byte, error) {
bridger, err := sdk.AccAddressFromBech32(bridgerAddr)
if err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalid, "bridger address")
}
var pendingBatchReq *types.OutgoingTxBatch
keeper.IterateOutgoingTxBatches(ctx, func(_ []byte, batch *types.OutgoingTxBatch) bool {
foundConfirm := keeper.GetBatchConfirm(ctx, batch.BatchNonce, batch.TokenContract, bridger) != nil
if !foundConfirm {
pendingBatchReq = batch
return true
}
return false
})
if pendingBatchReq == nil {
return nil, nil
}
res, err := codec.MarshalJSONIndent(types.ModuleCdc, pendingBatchReq)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return res, nil
}
// Gets MaxResults batches from store. Does not select by token type or anything
func lastBatchesRequest(ctx sdk.Context, keeper Keeper) ([]byte, error) {
var batches []*types.OutgoingTxBatch
keeper.IterateOutgoingTxBatches(ctx, func(_ []byte, batch *types.OutgoingTxBatch) bool {
batches = append(batches, batch)
return len(batches) == MaxResults
})
if len(batches) == 0 {
return nil, nil
}
res, err := codec.MarshalJSONIndent(types.ModuleCdc, batches)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return res, nil
}
func queryBatchFees(ctx sdk.Context, keeper Keeper, req abci.RequestQuery, legacyQuerierCdc *codec.LegacyAmino) ([]byte, error) {
var params types.QueryBatchFeeRequest
if err := legacyQuerierCdc.UnmarshalJSON(req.Data, ¶ms); err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
if params.MinBatchFees == nil {
params.MinBatchFees = make([]types.MinBatchFee, 0)
}
val := types.QueryBatchFeeResponse{BatchFees: keeper.GetAllBatchFees(ctx, MaxResults, params.MinBatchFees)}
res, err := codec.MarshalJSONIndent(types.ModuleCdc, val)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return res, nil
}
// queryBatch gets a batch by tokenContract and nonce
func queryBatch(ctx sdk.Context, nonceStr string, tokenContract string, keeper Keeper) ([]byte, error) {
nonce, err := uint64FromString(nonceStr)
if err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalid, "nonce")
}
if fxtypes.ValidateEthereumAddress(tokenContract) != nil {
return nil, sdkerrors.Wrap(types.ErrInvalid, "token contract")
}
foundBatch := keeper.GetOutgoingTxBatch(ctx, tokenContract, nonce)
if foundBatch == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "Can not find tx batch")
}
res, err := codec.MarshalJSONIndent(types.ModuleCdc, foundBatch)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, err.Error())
}
return res, nil
}
func queryGravityID(ctx sdk.Context, keeper Keeper) ([]byte, error) {
gravityID := keeper.GetGravityID(ctx)
res, err := codec.MarshalJSONIndent(types.ModuleCdc, gravityID)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return res, nil
}
func queryDenomToToken(ctx sdk.Context, denom string, keeper Keeper) ([]byte, error) {
if len(denom) <= 0 {
return nil, sdkerrors.Wrap(types.ErrUnknown, "denom")
}
bridgeToken := keeper.GetDenomByBridgeToken(ctx, denom)
if bridgeToken == nil {
return nil, sdkerrors.Wrap(types.ErrEmpty, "bridge token is not exist")
}
bytes, err := codec.MarshalJSONIndent(types.ModuleCdc, types.QueryDenomToTokenResponse{
Token: bridgeToken.Token,
ChannelIbc: bridgeToken.ChannelIbc,
})
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bytes, nil
}
func queryTokenToDenom(ctx sdk.Context, token string, keeper Keeper) ([]byte, error) {
if fxtypes.ValidateEthereumAddress(token) != nil {
return nil, sdkerrors.Wrap(types.ErrInvalid, "token")
}
bridgeToken := keeper.GetBridgeTokenDenom(ctx, token)
if bridgeToken == nil {
return nil, sdkerrors.Wrap(types.ErrEmpty, "bridge token is not exist")
}
bytes, err := codec.MarshalJSONIndent(types.ModuleCdc, types.QueryTokenToDenomResponse{
Denom: bridgeToken.Denom,
ChannelIbc: bridgeToken.ChannelIbc,
})
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bytes, nil
}
func queryPendingSendToExternal(ctx sdk.Context, senderAddr string, k Keeper) ([]byte, error) {
if _, err := sdk.AccAddressFromBech32(senderAddr); err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalid, "sender address")
}
batches := k.GetOutgoingTxBatches(ctx)
unbatchedTx := k.GetUnbatchedTransactions(ctx)
senderAddress := senderAddr
res := types.QueryPendingSendToExternalResponse{}
for _, batch := range batches {
for _, tx := range batch.Transactions {
if tx.Sender == senderAddress {
res.TransfersInBatches = append(res.TransfersInBatches, tx)
}
}
}
for _, tx := range unbatchedTx {
if tx.Sender == senderAddress {
res.UnbatchedTransfers = append(res.UnbatchedTransfers, tx)
}
}
bytes, err := codec.MarshalJSONIndent(types.ModuleCdc, res)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return bytes, nil
}
// uint64FromString to parse out a uint64 for a nonce
func uint64FromString(s string) (uint64, error) {
return strconv.ParseUint(s, 10, 64)
}