-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
query.go
153 lines (125 loc) · 4.09 KB
/
query.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
package client
import (
"context"
"encoding/hex"
"errors"
"fmt"
"strings"
"time"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/cosmos/cosmos-sdk/client"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// QueryTxsByEvents performs a search for transactions for a given set of events
// via the Tendermint RPC. An event takes the form of:
// "{eventAttribute}.{attributeKey} = '{attributeValue}'". Each event is
// concatenated with an 'AND' operand. It returns a slice of Info object
// containing txs and metadata. An error is returned if the query fails.
// If an empty string is provided it will order txs by asc
func QueryTxsByEvents(clientCtx client.Context, events []string, page, limit int, orderBy string) (*sdk.SearchTxsResult, error) {
if len(events) == 0 {
return nil, errors.New("must declare at least one event to search")
}
if page <= 0 {
return nil, errors.New("page must greater than 0")
}
if limit <= 0 {
return nil, errors.New("limit must greater than 0")
}
// XXX: implement ANY
query := strings.Join(events, " AND ")
node, err := clientCtx.GetNode()
if err != nil {
return nil, err
}
// TODO: this may not always need to be proven
// https://github.com/cosmos/cosmos-sdk/issues/6807
resTxs, err := node.TxSearch(context.Background(), query, true, &page, &limit, orderBy)
if err != nil {
return nil, err
}
resBlocks, err := getBlocksForTxResults(clientCtx, resTxs.Txs)
if err != nil {
return nil, err
}
txs, err := formatTxResults(clientCtx.TxConfig, resTxs.Txs, resBlocks)
if err != nil {
return nil, err
}
result := sdk.NewSearchTxsResult(uint64(resTxs.TotalCount), uint64(len(txs)), uint64(page), uint64(limit), txs)
return result, nil
}
// QueryTx queries for a single transaction by a hash string in hex format. An
// error is returned if the transaction does not exist or cannot be queried.
func QueryTx(clientCtx client.Context, hashHexStr string) (*sdk.TxResponse, error) {
hash, err := hex.DecodeString(hashHexStr)
if err != nil {
return nil, err
}
node, err := clientCtx.GetNode()
if err != nil {
return nil, err
}
//TODO: this may not always need to be proven
// https://github.com/cosmos/cosmos-sdk/issues/6807
resTx, err := node.Tx(context.Background(), hash, true)
if err != nil {
return nil, err
}
resBlocks, err := getBlocksForTxResults(clientCtx, []*ctypes.ResultTx{resTx})
if err != nil {
return nil, err
}
out, err := mkTxResult(clientCtx.TxConfig, resTx, resBlocks[resTx.Height])
if err != nil {
return out, err
}
return out, nil
}
// formatTxResults parses the indexed txs into a slice of TxResponse objects.
func formatTxResults(txConfig client.TxConfig, resTxs []*ctypes.ResultTx, resBlocks map[int64]*ctypes.ResultBlock) ([]*sdk.TxResponse, error) {
var err error
out := make([]*sdk.TxResponse, len(resTxs))
for i := range resTxs {
out[i], err = mkTxResult(txConfig, resTxs[i], resBlocks[resTxs[i].Height])
if err != nil {
return nil, err
}
}
return out, nil
}
func getBlocksForTxResults(clientCtx client.Context, resTxs []*ctypes.ResultTx) (map[int64]*ctypes.ResultBlock, error) {
node, err := clientCtx.GetNode()
if err != nil {
return nil, err
}
resBlocks := make(map[int64]*ctypes.ResultBlock)
for _, resTx := range resTxs {
if _, ok := resBlocks[resTx.Height]; !ok {
resBlock, err := node.Block(context.Background(), &resTx.Height)
if err != nil {
return nil, err
}
resBlocks[resTx.Height] = resBlock
}
}
return resBlocks, nil
}
func mkTxResult(txConfig client.TxConfig, resTx *ctypes.ResultTx, resBlock *ctypes.ResultBlock) (*sdk.TxResponse, error) {
txb, err := txConfig.TxDecoder()(resTx.Tx)
if err != nil {
return nil, err
}
p, ok := txb.(intoAny)
if !ok {
return nil, fmt.Errorf("expecting a type implementing intoAny, got: %T", txb)
}
any := p.AsAny()
return sdk.NewResponseResultTx(resTx, any, resBlock.Block.Time.Format(time.RFC3339)), nil
}
// Deprecated: this interface is used only internally for scenario we are
// deprecating (StdTxConfig support)
type intoAny interface {
AsAny() *codectypes.Any
}