github.com/badrootd/celestia-core@v0.0.0-20240305091328-aa4207a4b25d/docs/app-dev/indexing-transactions.md (about) 1 --- 2 order: 6 3 --- 4 5 # Indexing Transactions 6 7 CometBFT allows you to index transactions and blocks and later query or 8 subscribe to their results. Transactions are indexed by `TxResult.Events` and 9 blocks are indexed by `Response(Begin|End)Block.Events`. However, transactions 10 are also indexed by a primary key which includes the transaction hash and maps 11 to and stores the corresponding `TxResult`. Blocks are indexed by a primary key 12 which includes the block height and maps to and stores the block height, i.e. 13 the block itself is never stored. 14 15 Each event contains a type and a list of attributes, which are key-value pairs 16 denoting something about what happened during the method's execution. For more 17 details on `Events`, see the 18 [ABCI](https://github.com/cometbft/cometbft/blob/v0.34.x/spec/abci/abci.md#events) 19 documentation. 20 21 An `Event` has a composite key associated with it. A `compositeKey` is 22 constructed by its type and key separated by a dot. 23 24 For example: 25 26 ```json 27 "jack": [ 28 "account.number": 100 29 ] 30 ``` 31 32 would be equal to the composite key of `jack.account.number`. 33 34 By default, CometBFT will index all transactions by their respective hashes 35 and height and blocks by their height. 36 37 CometBFT allows for different events within the same height to have 38 equal attributes. 39 40 ## Configuration 41 42 Operators can configure indexing via the `[tx_index]` section. The `indexer` 43 field takes a series of supported indexers. If `null` is included, indexing will 44 be turned off regardless of other values provided. 45 46 ```toml 47 [tx-index] 48 49 # The backend database to back the indexer. 50 # If indexer is "null", no indexer service will be used. 51 # 52 # The application will set which txs to index. In some cases a node operator will be able 53 # to decide which txs to index based on configuration set in the application. 54 # 55 # Options: 56 # 1) "null" 57 # 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). 58 # - When "kv" is chosen "tx.height" and "tx.hash" will always be indexed. 59 # 3) "psql" - the indexer services backed by PostgreSQL. 60 # indexer = "kv" 61 ``` 62 63 ### Supported Indexers 64 65 #### KV 66 67 The `kv` indexer type is an embedded key-value store supported by the main 68 underlying CometBFT database. Using the `kv` indexer type allows you to query 69 for block and transaction events directly against CometBFT's RPC. However, the 70 query syntax is limited and so this indexer type might be deprecated or removed 71 entirely in the future. 72 73 **Implementation and data layout** 74 75 The kv indexer stores each attribute of an event individually, by creating a composite key 76 of the *event type*, *attribute key*, *attribute value*, *height* and *event sequence*. 77 78 For example the following events: 79 80 ``` 81 Type: "transfer", 82 Attributes: []abci.EventAttribute{ 83 {Key: []byte("sender"), Value: []byte("Bob"), Index: true}, 84 {Key: []byte("recipient"), Value: []byte("Alice"), Index: true}, 85 {Key: []byte("balance"), Value: []byte("100"), Index: true}, 86 {Key: []byte("note"), Value: []byte("nothing"), Index: true}, 87 }, 88 89 ``` 90 91 ``` 92 Type: "transfer", 93 Attributes: []abci.EventAttribute{ 94 {Key: []byte("sender"), Value: []byte("Tom"), Index: true}, 95 {Key: []byte("recipient"), Value: []byte("Alice"), Index: true}, 96 {Key: []byte("balance"), Value: []byte("200"), Index: true}, 97 {Key: []byte("note"), Value: []byte("nothing"), Index: true}, 98 }, 99 ``` 100 101 will be represented as follows in the store: 102 103 ``` 104 Key value 105 transferSenderBobEndBlock1 1 106 transferRecipientAliceEndBlock11 1 107 transferBalance100EndBlock11 1 108 transferNodeNothingEndblock11 1 109 ---- event2 ------ 110 transferSenderTomEndBlock12 1 111 transferRecipientAliceEndBlock12 1 112 transferBalance200EndBlock12 1 113 transferNodeNothingEndblock12 1 114 115 ``` 116 The key is thus formed of the event type, the attribute key and value, the event the attribute belongs to (`EndBlock` or `BeginBlock`), 117 the height and the event number. The event number is a local variable kept by the indexer and incremented when a new event is processed. 118 119 It is an `int64` variable and has no other semantics besides being used to associate attributes belonging to the same events within a height. 120 This variable is not atomically incremented as event indexing is deterministic. **Should this ever change**, the event id generation 121 will be broken. 122 123 #### PostgreSQL 124 125 The `psql` indexer type allows an operator to enable block and transaction event 126 indexing by proxying it to an external PostgreSQL instance allowing for the events 127 to be stored in relational models. Since the events are stored in a RDBMS, operators 128 can leverage SQL to perform a series of rich and complex queries that are not 129 supported by the `kv` indexer type. Since operators can leverage SQL directly, 130 searching is not enabled for the `psql` indexer type via CometBFT's RPC -- any 131 such query will fail. 132 133 Note, the SQL schema is stored in `state/indexer/sink/psql/schema.sql` and operators 134 must explicitly create the relations prior to starting CometBFT and enabling 135 the `psql` indexer type. 136 137 Example: 138 139 ```shell 140 $ psql ... -f state/indexer/sink/psql/schema.sql 141 ``` 142 143 ## Default Indexes 144 145 The CometBFT tx and block event indexer indexes a few select reserved events 146 by default. 147 148 ### Transactions 149 150 The following indexes are indexed by default: 151 152 - `tx.height` 153 - `tx.hash` 154 155 ### Blocks 156 157 The following indexes are indexed by default: 158 159 - `block.height` 160 161 ## Adding Events 162 163 Applications are free to define which events to index. CometBFT does not 164 expose functionality to define which events to index and which to ignore. In 165 your application's `DeliverTx` method, add the `Events` field with pairs of 166 UTF-8 encoded strings (e.g. "transfer.sender": "Bob", "transfer.recipient": 167 "Alice", "transfer.balance": "100"). 168 169 Example: 170 171 ```go 172 func (app *KVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.Result { 173 //... 174 events := []abci.Event{ 175 { 176 Type: "transfer", 177 Attributes: []abci.EventAttribute{ 178 {Key: []byte("sender"), Value: []byte("Bob"), Index: true}, 179 {Key: []byte("recipient"), Value: []byte("Alice"), Index: true}, 180 {Key: []byte("balance"), Value: []byte("100"), Index: true}, 181 {Key: []byte("note"), Value: []byte("nothing"), Index: true}, 182 }, 183 }, 184 } 185 return types.ResponseDeliverTx{Code: code.CodeTypeOK, Events: events} 186 } 187 ``` 188 189 If the indexer is not `null`, the transaction will be indexed. Each event is 190 indexed using a composite key in the form of `{eventType}.{eventAttribute}={eventValue}`, 191 e.g. `transfer.sender=bob`. 192 193 ## Querying Transactions Events 194 195 You can query for a paginated set of transaction by their events by calling the 196 `/tx_search` RPC endpoint: 197 198 ```bash 199 curl "localhost:26657/tx_search?query=\"message.sender='cosmos1...'\"&prove=true" 200 ``` 201 If the conditions are related to transaction events and the user wants to make sure the 202 conditions are true within the same events, the `match_events` keyword should be used, 203 as described [below](#querying_block_events) 204 205 Check out [API docs](https://docs.cometbft.com/v0.34/rpc/#/Info/tx_search) 206 for more information on query syntax and other options. 207 208 ## Subscribing to Transactions 209 210 Clients can subscribe to transactions with the given tags via WebSocket by providing 211 a query to `/subscribe` RPC endpoint. 212 213 ```json 214 { 215 "jsonrpc": "2.0", 216 "method": "subscribe", 217 "id": "0", 218 "params": { 219 "query": "message.sender='cosmos1...'" 220 } 221 } 222 ``` 223 224 Check out [API docs](https://docs.cometbft.com/v0.34/rpc/#subscribe) for more information 225 on query syntax and other options. 226 227 ## Querying Block Events 228 229 You can query for a paginated set of blocks by their events by calling the 230 `/block_search` RPC endpoint: 231 232 ```bash 233 curl "localhost:26657/block_search?query=\"block.height > 10 AND val_set.num_changed > 0\"" 234 ``` 235 236 ## `match_events` keyword 237 238 The query results in the height number(s) (or transaction hashes when querying transactions) which contain events whose attributes match the query conditions. 239 However, there are two options to query the indexers. To demonstrate the two modes, we reuse the two events 240 where Bob and Tom send money to Alice and query the block indexer. We issue the following query: 241 242 ```bash 243 curl "localhost:26657/block_search?query=\"sender=Bob AND balance = 200\"" 244 ``` 245 246 The result will return height 1 even though the attributes matching the conditions in the query 247 occurred in different events. 248 249 If we wish to retrieve only heights where the attributes occurred within the same event, 250 the query syntax is as follows: 251 252 ```bash 253 curl "localhost:26657/block_search?query=\"sender=Bob AND balance = 200\"&match_events=true" 254 ``` 255 Currently the default behavior is if `match_events` is set to false. 256 257 Check out [API docs](https://docs.cometbft.com/v0.34/rpc/#/Info/block_search) 258 for more information on query syntax and other options. 259 260 **Backwards compatibility** 261 262 Storing the event sequence was introduced in CometBFT 0.34.25. As there are no previous releases of CometBFT, 263 all nodes running CometBFT will include the event sequence. However, mixed networks running CometBFT v0.34.25 and greater 264 and Tendermint Core versions before v0.34.25 are possible. On nodes running Tendermint Core, the `match_events` keyword 265 is ignored and the data is retrieved as if `match_events=false`. 266 267 Additionally, if a node that was running Tendermint Core 268 when the data was first indexed, and switched to CometBFT, is queried, it will retrieve this previously indexed 269 data as if `match_events=false` (attributes can match the query conditions across different events on the same height).