github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/auth/client/utils/query.go (about) 1 package utils 2 3 import ( 4 "encoding/hex" 5 "errors" 6 "strings" 7 "time" 8 9 ctypes "github.com/fibonacci-chain/fbc/libs/tendermint/rpc/core/types" 10 11 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/context" 12 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec" 13 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 14 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/types" 15 ) 16 17 type ParseAppTxHandler func(cdc *codec.CodecProxy, txBytes []byte) (sdk.Tx, error) 18 19 var paresAppTx ParseAppTxHandler 20 21 func SetParseAppTx(hanlder ParseAppTxHandler) { 22 paresAppTx = hanlder 23 } 24 25 // QueryTxsByEvents performs a search for transactions for a given set of events 26 // via the Tendermint RPC. An event takes the form of: 27 // "{eventAttribute}.{attributeKey} = '{attributeValue}'". Each event is 28 // concatenated with an 'AND' operand. It returns a slice of Info object 29 // containing txs and metadata. An error is returned if the query fails. 30 func QueryTxsByEvents(cliCtx context.CLIContext, events []string, page, limit int) (*sdk.SearchTxsResult, error) { 31 if len(events) == 0 { 32 return nil, errors.New("must declare at least one event to search") 33 } 34 35 if page <= 0 { 36 return nil, errors.New("page must greater than 0") 37 } 38 39 if limit <= 0 { 40 return nil, errors.New("limit must greater than 0") 41 } 42 43 // XXX: implement ANY 44 query := strings.Join(events, " AND ") 45 46 node, err := cliCtx.GetNode() 47 if err != nil { 48 return nil, err 49 } 50 51 prove := !cliCtx.TrustNode 52 53 resTxs, err := node.TxSearch(query, prove, page, limit, "") 54 if err != nil { 55 return nil, err 56 } 57 58 if prove { 59 for _, tx := range resTxs.Txs { 60 err := ValidateTxResult(cliCtx, tx) 61 if err != nil { 62 return nil, err 63 } 64 } 65 } 66 67 resBlocks, err := getBlocksForTxResults(cliCtx, resTxs.Txs) 68 if err != nil { 69 return nil, err 70 } 71 72 txs, err := formatTxResults(cliCtx.CodecProy, resTxs.Txs, resBlocks) 73 if err != nil { 74 return nil, err 75 } 76 77 result := sdk.NewSearchTxsResult(resTxs.TotalCount, len(txs), page, limit, txs) 78 79 return &result, nil 80 } 81 82 // QueryTx queries for a single transaction by a hash string in hex format. An 83 // error is returned if the transaction does not exist or cannot be queried. 84 func QueryTx(cliCtx context.CLIContext, hashHexStr string) (sdk.TxResponse, error) { 85 // strip 0x prefix 86 if strings.HasPrefix(hashHexStr, "0x") { 87 hashHexStr = hashHexStr[2:] 88 } 89 90 hash, err := hex.DecodeString(hashHexStr) 91 if err != nil { 92 return sdk.TxResponse{}, err 93 } 94 95 node, err := cliCtx.GetNode() 96 if err != nil { 97 return sdk.TxResponse{}, err 98 } 99 100 resTx, err := node.Tx(hash, !cliCtx.TrustNode) 101 if err != nil { 102 return sdk.TxResponse{}, err 103 } 104 105 if !cliCtx.TrustNode { 106 if err = ValidateTxResult(cliCtx, resTx); err != nil { 107 return sdk.TxResponse{}, err 108 } 109 } 110 111 resBlocks, err := getBlocksForTxResults(cliCtx, []*ctypes.ResultTx{resTx}) 112 if err != nil { 113 return sdk.TxResponse{}, err 114 } 115 116 out, err := formatTxResult(cliCtx.CodecProy, resTx, resBlocks[resTx.Height]) 117 if err != nil { 118 return out, err 119 } 120 121 return out, nil 122 } 123 124 // formatTxResults parses the indexed txs into a slice of TxResponse objects. 125 func formatTxResults(cdc *codec.CodecProxy, resTxs []*ctypes.ResultTx, resBlocks map[int64]*ctypes.ResultBlock) ([]sdk.TxResponse, error) { 126 var err error 127 out := make([]sdk.TxResponse, len(resTxs)) 128 for i := range resTxs { 129 out[i], err = formatTxResult(cdc, resTxs[i], resBlocks[resTxs[i].Height]) 130 if err != nil { 131 return nil, err 132 } 133 } 134 135 return out, nil 136 } 137 138 // ValidateTxResult performs transaction verification. 139 func ValidateTxResult(cliCtx context.CLIContext, resTx *ctypes.ResultTx) error { 140 if !cliCtx.TrustNode { 141 check, err := cliCtx.Verify(resTx.Height) 142 if err != nil { 143 return err 144 } 145 err = resTx.Proof.Validate(check.Header.DataHash, resTx.Height) 146 if err != nil { 147 return err 148 } 149 } 150 return nil 151 } 152 153 func getBlocksForTxResults(cliCtx context.CLIContext, resTxs []*ctypes.ResultTx) (map[int64]*ctypes.ResultBlock, error) { 154 node, err := cliCtx.GetNode() 155 if err != nil { 156 return nil, err 157 } 158 159 resBlocks := make(map[int64]*ctypes.ResultBlock) 160 161 for _, resTx := range resTxs { 162 if _, ok := resBlocks[resTx.Height]; !ok { 163 resBlock, err := node.Block(&resTx.Height) 164 if err != nil { 165 return nil, err 166 } 167 168 resBlocks[resTx.Height] = resBlock 169 } 170 } 171 172 return resBlocks, nil 173 } 174 175 func formatTxResult(cdc *codec.CodecProxy, resTx *ctypes.ResultTx, resBlock *ctypes.ResultBlock) (sdk.TxResponse, error) { 176 tx, err := parseTx(cdc, resTx.Tx) 177 if err != nil { 178 return sdk.TxResponse{}, err 179 } 180 181 return sdk.NewResponseResultTx(resTx, tx, resBlock.Block.Time.Format(time.RFC3339)), nil 182 } 183 184 func parseTx(cdc *codec.CodecProxy, txBytes []byte) (sdk.Tx, error) { 185 var tx types.StdTx 186 err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) 187 if err != nil && paresAppTx != nil { 188 return paresAppTx(cdc, txBytes) 189 } 190 191 return &tx, nil 192 }