diff --git a/docs/neutron/modules/interchain-queries/api.md b/docs/neutron/modules/interchain-queries/api.md new file mode 100644 index 000000000..93f6cbdce --- /dev/null +++ b/docs/neutron/modules/interchain-queries/api.md @@ -0,0 +1,377 @@ +# API + +This page describes the interface of the `interchainqueries` module, including the endpoints it provides and the `sudo` messages it issues. Each endpoint is detailed in its own section, with links to the request and response models, as well as examples of how to interact with the module using the endpoint. The request and response model links also offer more details about the request parameters and response values. + +Please note that the examples are for demonstration purposes only and may not reflect the current state of the chain. They are included to help visualize how requests are formed and what the response payloads look like. + +**Endpoints** + +This section lists the public RPC API of the `interchainqueries` module. + +Queries: +- [Params](#params); +- [RegisteredQueries](#registeredqueries); +- [RegisteredQuery](#registeredquery); +- [QueryResult](#queryresult); +- [LastRemoteHeight](#lastremoteheight). + +Messages: +- [RegisterInterchainQuery](#registerinterchainquery); +- [SubmitQueryResult](#submitqueryresult); +- [RemoveInterchainQuery](#removeinterchainquery); +- [UpdateInterchainQuery](#updateinterchainquery); +- [UpdateParams](#updateparams). + +**Sudo** + +This section lists the `sudo` messages that the `interchainqueries` module sends to smart contracts that own Interchain Queries. + +- [MessageTxQueryResult](#messagetxqueryresult) +- [MessageKvQueryResult](#messagekvqueryresult) + +## Queries + +### Params + +Fetches the current parameters of the `interchainqueries` module. + +- [Request model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#QueryParamsRequest) +- [Response model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#QueryParamsResponse) + +
+ Example + +```sh +curl -X 'GET' \ + 'https://rest-lb.neutron.org/neutron/interchainqueries/params' \ + -H 'accept: application/json' + ``` + +```json +{ + "params": { + "query_submit_timeout": "1036800", + "query_deposit": [ + { + "denom": "untrn", + "amount": "1000000" + } + ], + "tx_query_removal_limit": "10000", + "max_kv_query_keys_count": "32", + "max_transactions_filters": "32" + } +} +``` +
+ +### RegisteredQueries + +Retrieves all registered Interchain Queries in the module, with optional filtering by owner and/or connection ID. + +- [Request model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#QueryRegisteredQueriesRequest) +- [Response model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#QueryRegisteredQueriesResponse) + +
+ Example + +```sh +curl -X 'GET' \ + 'https://rest-lb-pion.ntrn.tech/neutron/interchainqueries/registered_queries?owners=neutron1zf2tdlq9pn8jq680rlsgwtsgljt54ctu0ulj8cm4s6r93mdmjuwqpurmf4&owners=neutron1sj5zhzmejs5q4t54zfgpkua6z0w0m87tz0sdzzqv8j0f5nwsv4fqmerty7' \ + -H 'accept: application/json' +``` + +```json +{ + "registered_queries": [ + { + "id": "30", + "owner": "neutron1sj5zhzmejs5q4t54zfgpkua6z0w0m87tz0sdzzqv8j0f5nwsv4fqmerty7", + "query_type": "tx", + "keys": [], + "transactions_filter": "[{\"field\":\"transfer.recipient\",\"op\":\"Eq\",\"value\":\"cosmos1mclft0hv4f385j2t4luwfz4gthn264alvufx7fjvkwd99qhl4e9sf4g87w\"},{\"field\":\"tx.height\",\"op\":\"Gte\",\"value\":2644737}]", + "connection_id": "connection-8", + "update_period": "6", + "last_submitted_result_local_height": "0", + "last_submitted_result_remote_height": null, + "deposit": [ + { + "denom": "untrn", + "amount": "1000000" + } + ], + "submit_timeout": "1036800", + "registered_at_height": "2644737" + }, + { + "id": "62", + "owner": "neutron1zf2tdlq9pn8jq680rlsgwtsgljt54ctu0ulj8cm4s6r93mdmjuwqpurmf4", + "query_type": "kv", + "keys": [ + { + "path": "bank", + "key": "AhRF6XHZfYzFmJGcX7lKP98y5ZeMgnVubHM=" + } + ], + "transactions_filter": "", + "connection_id": "connection-130", + "update_period": "30", + "last_submitted_result_local_height": "8544484", + "last_submitted_result_remote_height": { + "revision_number": "1", + "revision_height": "3036139" + }, + "deposit": [ + { + "denom": "untrn", + "amount": "1000000" + } + ], + "submit_timeout": "1036800", + "registered_at_height": "8542104" + } + ], + "pagination": { + "next_key": null, + "total": "2" + } +} +``` +
+ +**Might be interesting:** +- [Why is the Proof field nullified in QueryResult RPC response?](/neutron/modules/interchain-queries/explanation#why-is-the-proof-field-nullified-in-queryresult-rpc-response) +- [Why doesn't interchainqueries module store TX query results?](/neutron/modules/interchain-queries/explanation#why-doesnt-interchainqueries-module-store-tx-query-results) + +### RegisteredQuery + +Fetches details of a registered Interchain Query using its ID. + +- [Request model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#QueryRegisteredQueryRequest) +- [Response model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#QueryRegisteredQueryResponse) + +
+ Example + +```sh +curl -X 'GET' \ + 'https://rest-lb-pion.ntrn.tech/neutron/interchainqueries/registered_query?query_id=62' \ + -H 'accept: application/json' + ``` + +```json +{ + "registered_query": { + "id": "62", + "owner": "neutron1zf2tdlq9pn8jq680rlsgwtsgljt54ctu0ulj8cm4s6r93mdmjuwqpurmf4", + "query_type": "kv", + "keys": [ + { + "path": "bank", + "key": "AhRF6XHZfYzFmJGcX7lKP98y5ZeMgnVubHM=" + } + ], + "transactions_filter": "", + "connection_id": "connection-130", + "update_period": "30", + "last_submitted_result_local_height": "8544484", + "last_submitted_result_remote_height": { + "revision_number": "1", + "revision_height": "3036139" + }, + "deposit": [ + { + "denom": "untrn", + "amount": "1000000" + } + ], + "submit_timeout": "1036800", + "registered_at_height": "8542104" + } +} +``` +
+ +**Might be interesting:** +- [Why is the Proof field nullified in QueryResult RPC response?](/neutron/modules/interchain-queries/explanation#why-is-the-proof-field-nullified-in-queryresult-rpc-response) +- [Why doesn't interchainqueries module store TX query results?](/neutron/modules/interchain-queries/explanation#why-doesnt-interchainqueries-module-store-tx-query-results) + +### QueryResult + +Retrieves the most recent successfully submitted result of an Interchain Query. This is only applicable for KV Interchain Queries. + +- [Request model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#QueryQueryResultRequest) +- [Response model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#QueryQueryResultResponse) + +
+ Example + +```sh +curl -X 'GET' \ + 'https://rest-lb-pion.ntrn.tech/neutron/interchainqueries/query_result?query_id=62' \ + -H 'accept: application/json' + ``` + +```json +{ + "result": { + "kv_results": [ + { + "storage_prefix": "bank", + "key": "AhRF6XHZfYzFmJGcX7lKP98y5ZeMgnVubHM=", + "value": "CgR1bmxzEgczOTk3NDkx", + "Proof": null + } + ], + "block": null, + "height": "3036139", + "revision": "1", + "allow_kv_callbacks": false + } +} +``` +
+ +**Might be interesting:** +- [Why is the Proof field nullified in QueryResult RPC response?](/neutron/modules/interchain-queries/explanation#why-is-the-proof-field-nullified-in-queryresult-rpc-response) +- [Why doesn't interchainqueries module store TX query results?](/neutron/modules/interchain-queries/explanation#why-doesnt-interchainqueries-module-store-tx-query-results) + +### LastRemoteHeight + +Retrieves the most recent height of a remote chain as known by the IBC client associated with a given connection ID. + +- [Request model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#QueryLastRemoteHeightRequest) +- [Response model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#QueryLastRemoteHeightResponse) + +
+ Example + +```sh +curl -X 'GET' \ + 'https://rest-lb-pion.ntrn.tech/neutron/interchainqueries/remote_height?connection_id=connection-92' \ + -H 'accept: application/json' + ``` + +```json +{ + "height": "5788175" +} +``` +
+ +**Might be interesting:** +-[What's the role of IBC connections in Interchain Queries and how to choose one?](/neutron/modules/interchain-queries/explanation#whats-the-role-of-ibc-connections-in-interchain-queries-and-how-to-choose-one) + +## Messages + +### RegisterInterchainQuery + +Registers a new Interchain Query in the `interchainqueries` module. This message should only be issued by a smart contract. The calling contract is automatically charged a query registration deposit, based on the module's query deposit parameter. The deposit is refunded when the query is removed. Ensure the contract's account has sufficient assets at the time of message execution. + +The response includes the ID assigned to the registered query. Use a reply handler to process this response and utilize the query ID. + +- [Request model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#MsgRegisterInterchainQuery) +- [Response model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#MsgRegisterInterchainQueryResponse) + +Events emission on success: +- `type=neutron`, attributes: + - `module=interchainqueries`; + - `action=query_updated`; + - other attributes with query parameters: `query_id`, `connection_id`, `owner`, `type`, `tx_filter`, `kv_key`. + +**Might be interesting:** +- [How to choose the right IBC connection ID for an Interchain Query and verify it](/neutron/modules/interchain-queries/how-to#how-to-choose-the-right-ibc-connection-id-for-an-interchain-query-and-verify-it) +- [How to find out what transaction filter to use](/neutron/modules/interchain-queries/how-to#how-to-find-out-what-transaction-filter-to-use) +- [How to register and handle a KV Interchain Query](/neutron/modules/interchain-queries/how-to#how-to-register-and-handle-a-kv-interchain-query) +- [How to register and handle a TX Interchain Query](/neutron/modules/interchain-queries/how-to#how-to-register-and-handle-a-tx-interchain-query) +- [Why is there a query creation deposit?](/neutron/modules/interchain-queries/explanation#why-is-there-a-query-creation-deposit) +- [Impossibility to retrieve and prove KV data with nil values](/neutron/modules/interchain-queries/known-bugs#impossibility-to-retrieve-and-prove-kv-data-with-nil-values) + +### SubmitQueryResult + +Submits the result of an Interchain Query execution to the chain. Handling this message may involve forwarding the result to the smart contract that owns the query for processing, which could require significant gas usage. + +- [Request model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#MsgSubmitQueryResult) +- [Response model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#MsgSubmitQueryResultResponse) + +**Might be interesting:** +- [What are entry points and sudo calls?](/neutron/modules/interchain-queries/explanation#what-are-entry-points-and-sudo-calls) +- [Limited gas for sudo calls](/neutron/modules/interchain-queries/explanation#limited-gas-for-sudo-calls) +- [What happens if a sudo callback to a smart contract owning an Interchain Query fails?](/neutron/modules/interchain-queries/explanation#what-happens-if-a-sudo-callback-to-a-smart-contract-owning-an-interchain-query-fails) +- [Impossibility to retrieve and prove KV data with nil values](/neutron/modules/interchain-queries/known-bugs#impossibility-to-retrieve-and-prove-kv-data-with-nil-values) + +### RemoveInterchainQuery + +Removes a specific Interchain Query and its results from the module. The query can only be removed by its owner during the query's submit timeout. After the timeout, anyone can remove it. Upon successful removal, the query deposit is refunded to the caller. + +- [Request model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#MsgRemoveInterchainQuery) +- [Response model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#MsgRemoveInterchainQueryResponse) + +Events emission on success: +- `type=neutron`, attributes: + - `module=interchainqueries`; + - `action=query_removed`; + - other attributes with query parameters: `query_id`, `connection_id`, `owner`, `type`, `tx_filter`, `kv_key`. + +**Might be interesting:** +- [What are the rules for creation deposit refund?](/neutron/modules/interchain-queries/explanation#what-are-the-rules-for-creation-deposit-refund) +- [How Interchain Query results are removed?](/neutron/modules/interchain-queries/explanation#how-interchain-query-results-are-removed) + +### UpdateInterchainQuery + +Updates the parameters of a registered Interchain Query. This action can only be performed by the query's owner. + +- [Request model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#MsgUpdateInterchainQuery) +- [Response model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#MsgUpdateInterchainQueryResponse) + +Events emission on success: +- `type=neutron`, attributes: + - `module=interchainqueries`; + - `action=query_updated`; + - other attributes with query parameters: `query_id`, `connection_id`, `owner`, `type`, `tx_filter`, `kv_key`. + +### UpdateParams + +Updates the parameters of the `interchainqueries` module. This action can only be performed by the module's authority. + +- [Request model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#MsgUpdateParams) +- [Response model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#MsgUpdateParamsResponse) + +## Sudo + +### MessageTxQueryResult + +MessageTxQueryResult is the model of the `sudo` message sent to a smart contract when a TX Interchain Query result is submitted. The owner of a TX Interchain Query must implement a `sudo` entry point to handle `tx_query_result` messages and include the necessary logic in it. The `tx_query_result` handler functions as a callback, triggered by the `interchainqueries` module each time a TX query result is submitted. + +- [Message model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/contractmanager/types#MessageTxQueryResult) +- [Message model in neutron-sdk](https://docs.rs/neutron-sdk/0.11.0/neutron_sdk/sudo/msg/enum.SudoMsg.html#variant.TxQueryResult) + +Events emission on failure: +- `type=sudo`, attributes: + - attributes with the failure parameters: `_contract_address`, `failure_id`, `error`. + +**Might be interesting:** +- [What are entry points and sudo calls?](/neutron/modules/interchain-queries/explanation#what-are-entry-points-and-sudo-calls) +- [Limited gas for sudo calls](/neutron/modules/interchain-queries/explanation#limited-gas-for-sudo-calls) +- [What happens if a sudo callback to a smart contract owning an Interchain Query fails?](/neutron/modules/interchain-queries/explanation#what-happens-if-a-sudo-callback-to-a-smart-contract-owning-an-interchain-query-fails) +- [Why is it mandatory to do contract's side verification of submitted TX Interchain Query results?](/neutron/modules/interchain-queries/explanation#why-is-it-mandatory-to-do-contracts-side-verification-of-submitted-tx-interchain-query-results) +- [Why doesn't interchainqueries module store TX query results?](/neutron/modules/interchain-queries/explanation#why-doesnt-interchainqueries-module-store-tx-query-results) + +### MessageKvQueryResult + +MessageKvQueryResult is the model of the `sudo` message sent to a smart contract when a KV Interchain Query result is submitted. If the owner of a KV Interchain Query wants to handle updates, they must implement a `sudo` entry point to process `kv_query_result` messages and include the necessary logic in it. The `kv_query_result` handler acts as a callback, triggered by the `interchainqueries` module whenever a KV query result is submitted. + +Note that the message does not include the actual query result, only the query ID. To access the result data, use the `Query/QueryResult` RPC of the `interchainqueries` module. + +- [Message model](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/contractmanager/types#MessageKVQueryResult) +- [Message model in neutron-sdk](https://docs.rs/neutron-sdk/0.11.0/neutron_sdk/sudo/msg/enum.SudoMsg.html#variant.KVQueryResult) + +Events emission on failure: +- `type=sudo`, attributes: + - attributes with the failure parameters: `_contract_address`, `failure_id`, `error`. + +**Might be interesting:** +- [What are entry points and sudo calls?](/neutron/modules/interchain-queries/explanation#what-are-entry-points-and-sudo-calls) +- [Limited gas for sudo calls](/neutron/modules/interchain-queries/explanation#limited-gas-for-sudo-calls) +- [What happens if a sudo callback to a smart contract owning an Interchain Query fails?](/neutron/modules/interchain-queries/explanation#what-happens-if-a-sudo-callback-to-a-smart-contract-owning-an-interchain-query-fails) +- [Impossibility to retrieve and prove KV data with nil values](/neutron/modules/interchain-queries/known-bugs#impossibility-to-retrieve-and-prove-kv-data-with-nil-values) diff --git a/docs/neutron/modules/interchain-queries/client.md b/docs/neutron/modules/interchain-queries/client.md deleted file mode 100644 index 25c43e3c7..000000000 --- a/docs/neutron/modules/interchain-queries/client.md +++ /dev/null @@ -1,238 +0,0 @@ -# Client - -## Transactions - -### submit-query-result - -Submits query result: -```shell -neutrond tx interchainqueries submit-query-result [query-id] [result-file] -``` - -
- Example - Register an interchain query to get delegations of delegator on remote chain: - -```shell -neutrond tx interchainqueries submit-query-result result.json --from demowallet1 --gas 10000000 --gas-adjustment 1.4 --gas-prices 0.5stake --broadcast-mode block --chain-id test-1 -``` - -Example content of `result.json` file: -```json -{ - "kv_results": [ - { - "storage_prefix": "staking", - "key": "MRQE7KnWf7BcUyQTX/rb+q7XJL590xQE7KnWf7BcUyQTX/rb+q7XJL590w==", - "value": "Ci5uZXV0cm9uMXFuazJuNG5sa3B3OXhmcW50bGFkaDc0dzZ1anR1bHduNmR3cTh6EjVuZXV0cm9udmFsb3BlcjFxbmsybjRubGtwdzl4ZnFudGxhZGg3NHc2dWp0dWx3bnFzaGVweBocNzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMA==", - "Proof": { - "ops": [ - { - "type": "ics23:iavl", - "key": "MRQE7KnWf7BcUyQTX/rb+q7XJL590xQE7KnWf7BcUyQTX/rb+q7XJL590w==", - "data": "CvwDCisxFATsqdZ/sFxTJBNf+tv6rtckvn3TFATsqdZ/sFxTJBNf+tv6rtckvn3TEoUBCi5uZXV0cm9uMXFuazJuNG5sa3B3OXhmcW50bGFkaDc0dzZ1anR1bHduNmR3cTh6EjVuZXV0cm9udmFsb3BlcjFxbmsybjRubGtwdzl4ZnFudGxhZGg3NHc2dWp0dWx3bnFzaGVweBocNzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMBoLCAEYASABKgMAAgIiKQgBEiUCBAQg/XaR47+Bw1YRGxSWwyiaAq5OSBQJIJ1qSFWbOe/5msIgIisIARIEBAgYIBohIK8BCwRz+Fod0SUgzLUhUK6VU2mEVhOqM53DZgtpytmXIikIARIlBhAcIEDf0aJaZU9bWVCd7T6zPbZoDp9Z+5w4qnurGAYVS85jICIrCAESBAgYJCAaISDJJKeGrIRSJj3EYotsdiXp6QNsqlzjMJuy4aELAnFvYiIrCAESBAo4SiAaISDcflhqTQQJl5EG2W37BWlPexWgUWXE0agE9ir+M5zA6SIsCAESBQxkjgEgGiEg4dZUUhewJTuJ2dNjKe7cJCKzJANcYVTprAPKkjQOtQciLQgBEgYOpgGaASAaISBRJQpR01RPTxIakznqcierctkEkx3Sp51sbw4+cAXnIQ==" - }, - { - "type": "ics23:simple", - "key": "c3Rha2luZw==", - "data": "Cq8BCgdzdGFraW5nEiAX2lqGKZJW473ICfGb3Wa2lotPFt1cTLN+R9aZJjs2xBoJCAEYASABKgEAIicIARIBARogOqsHULjzmZkig3Kxczq2JoCMuiq6iXWpKHea7ZB9gWAiJwgBEgEBGiBp76tKiIQVkrMiaBxiQMYu0e/01Saw7T/PjyEPDPlQbiIlCAESIQEmmrFm4aKKJReopSqK+rTjZSDTKuV0duBPSipjJxPzaA==" - } - ] - } - }, - { - "storage_prefix": "staking", - "key": "IRQE7KnWf7BcUyQTX/rb+q7XJL590w==", - "value": "CjVuZXV0cm9udmFsb3BlcjFxbmsybjRubGtwdzl4ZnFudGxhZGg3NHc2dWp0dWx3bnFzaGVweBJDCh0vY29zbW9zLmNyeXB0by5lZDI1NTE5LlB1YktleRIiCiA/t9hdbTKV91SkxZmBgg39qOod/0vO76wK5QW4V6ZyiyADKgo3MDAwMDAwMDAwMhw3MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwOgYKBHRlc3RKAFJLCjsKEjEwMDAwMDAwMDAwMDAwMDAwMBISMjAwMDAwMDAwMDAwMDAwMDAwGhExMDAwMDAwMDAwMDAwMDAwMBIMCIvg1pYGEMCl3pgBWgEx", - "Proof": { - "ops": [ - { - "type": "ics23:iavl", - "key": "IRQE7KnWf7BcUyQTX/rb+q7XJL590w==", - "data": "CuYEChYhFATsqdZ/sFxTJBNf+tv6rtckvn3TEoICCjVuZXV0cm9udmFsb3BlcjFxbmsybjRubGtwdzl4ZnFudGxhZGg3NHc2dWp0dWx3bnFzaGVweBJDCh0vY29zbW9zLmNyeXB0by5lZDI1NTE5LlB1YktleRIiCiA/t9hdbTKV91SkxZmBgg39qOod/0vO76wK5QW4V6ZyiyADKgo3MDAwMDAwMDAwMhw3MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwOgYKBHRlc3RKAFJLCjsKEjEwMDAwMDAwMDAwMDAwMDAwMBISMjAwMDAwMDAwMDAwMDAwMDAwGhExMDAwMDAwMDAwMDAwMDAwMBIMCIvg1pYGEMCl3pgBWgExGgsIARgBIAEqAwACAiIrCAESBAIEAiAaISAUEQIV7Mowp74roi0TppU27U2MG6vmxyfOJv0qCgfKqyIpCAESJQQIAiCCcJylC1/v/+M5ac1A5fvMcAA+5C+mG74CeoXjOScF1SAiKwgBEgQGEBwgGiEgD6ziA96HscsB249ulbBxQa4rSR8BzLzeJNkXjOakvjoiKwgBEgQIGCQgGiEgySSnhqyEUiY9xGKLbHYl6ekDbKpc4zCbsuGhCwJxb2IiKwgBEgQKOEogGiEg3H5Yak0ECZeRBtlt+wVpT3sVoFFlxNGoBPYq/jOcwOkiLAgBEgUMZI4BIBohIOHWVFIXsCU7idnTYynu3CQisyQDXGFU6awDypI0DrUHIi0IARIGDqYBmgEgGiEgUSUKUdNUT08SGpM56nInq3LZBJMd0qedbG8OPnAF5yE=" - }, - { - "type": "ics23:simple", - "key": "c3Rha2luZw==", - "data": "Cq8BCgdzdGFraW5nEiAX2lqGKZJW473ICfGb3Wa2lotPFt1cTLN+R9aZJjs2xBoJCAEYASABKgEAIicIARIBARogOqsHULjzmZkig3Kxczq2JoCMuiq6iXWpKHea7ZB9gWAiJwgBEgEBGiBp76tKiIQVkrMiaBxiQMYu0e/01Saw7T/PjyEPDPlQbiIlCAESIQEmmrFm4aKKJReopSqK+rTjZSDTKuV0duBPSipjJxPzaA==" - } - ] - } - } - ], - "height": 77, - "revision": 2 -} -``` -
- -## Queries - -In this section we describe the queries required on grpc server. - -```protobuf -// Query defines the gRPC querier service. -service Query { - // returns all the registered queries in the module with filtration by owner and/or connection id - rpc RegisteredQueries(QueryRegisteredQueriesRequest) - returns (QueryRegisteredQueriesResponse) { - option (google.api.http).get = - "/neutron/interchainqueries/interchainqueries/registered_queries"; - } - - // returns registered query by id - rpc RegisteredQuery(QueryRegisteredQueryRequest) - returns (QueryRegisteredQueryResponse) { - option (google.api.http).get = - "/neutron/interchainqueries/interchainqueries/registered_query"; - } - - // returns query result for a particular registered interchain query by id - rpc QueryResult(QueryRegisteredQueryResultRequest) returns (QueryRegisteredQueryResultResponse) { - option (google.api.http).get = "/neutron/interchainqueries/interchainqueries/query_result"; - } - - // returns last height about which Neutron knows for the particular remote chain - rpc LastRemoteHeight(QueryLastRemoteHeight) returns (QueryLastRemoteHeightResponse) { - option (google.api.http).get = "/neutron/interchainqueries/interchainqueries/remote_height"; - } -} -``` - -### registered-query - -Returns registered query by id. - -```bash -neutrond query interchainqueries registered-query [id] -``` - -
- Example - Returns info about registered query with id 1: - - ```shell - neutrond query interchainqueries registered-query 1 - ``` - -Output: - - ```shell - registered_query: - connection_id: connection-0 - id: "1" - owner: "neutron14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s5c2epq" - last_submitted_result_local_height: "0" - last_submitted_result_remote_height: "0" - transactions_filter: "{}" - keys: - - path: "staking" - key: "MRQE7KnWf7BcUyQTX/rb+q7XJL590xQE7KnWf7BcUyQTX/rb+q7XJL590w==" - query_type: kv - update_period: "1" - ``` - -
- -### registered-queries - -Returns all the registered queries in the module with filtration by owner and/or connection id. - -```bash -neutrond query interchainqueries registered-queries -``` - -
- Example - Returns all registered interchain queries in the module with connection id `connection-0` and owner `neutron14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s5c2epq`: - - ```shell - neutrond query interchainqueries registered-queries --connection-id connection-0 --owners neutron14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s5c2epq - ``` - -Output: - - ```shell - registered_queries: - - connection_id: connection-0 - id: "1" - owner: "neutron14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s5c2epq" - last_submitted_result_local_height: "206" - last_submitted_result_remote_height: "203" - transactions_filter: "{}" - keys: - - path: "staking" - key: "MRQE7KnWf7BcUyQTX/rb+q7XJL590xQE7KnWf7BcUyQTX/rb+q7XJL590w==" - query_type: kv - update_period: "1" - - connection_id: connection-0 - id: "2" - owner: "neutron14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s5c2epq" - last_submitted_result_local_height: "199" - last_submitted_result_remote_height: "188" - transactions_filter: '{"message.module": "bank"}' - query_type: tx - update_period: "5" - ``` - -
- -### query-result - -Returns KV-storage result for particular registered interchain query by id. - -```bash -neutrond query interchainqueries query-result [query-id] -``` - -
- Example - Returns KV-storage result for registered interchain query with id 1: - - ```shell - neutrond query interchainqueries query-result 1 - ``` - -Output: - - ```shell - result: - blocks: [] - height: "203" - kv_results: - - Proof: null - key: MRQE7KnWf7BcUyQTX/rb+q7XJL590xQE7KnWf7BcUyQTX/rb+q7XJL590w== - storage_prefix: staking - value: Ci5uZXV0cm9uMXFuazJuNG5sa3B3OXhmcW50bGFkaDc0dzZ1anR1bHduNmR3cTh6EjVuZXV0cm9udmFsb3BlcjFxbmsybjRubGtwdzl4ZnFudGxhZGg3NHc2dWp0dWx3bnFzaGVweBocNzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMA== - - Proof: null - key: IRQE7KnWf7BcUyQTX/rb+q7XJL590w== - storage_prefix: staking - value: CjVuZXV0cm9udmFsb3BlcjFxbmsybjRubGtwdzl4ZnFudGxhZGg3NHc2dWp0dWx3bnFzaGVweBJDCh0vY29zbW9zLmNyeXB0by5lZDI1NTE5LlB1YktleRIiCiCGVtQII4Ok0ieJqHiQcBkW42FKCSKPv+3poD5Me4zh1SADKgo3MDAwMDAwMDAwMhw3MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwOgYKBHRlc3RKAFJKCjsKEjEwMDAwMDAwMDAwMDAwMDAwMBISMjAwMDAwMDAwMDAwMDAwMDAwGhExMDAwMDAwMDAwMDAwMDAwMBILCKLo1pYGEKjc/G1aATE= - revision: "0" - ``` - -
- -### query-last-remote-height - -Returns last height about which Neutron knows for the particular remote chain by connection id. - -```bash -neutrond query interchainqueries query-last-remote-height [connection-id] -``` - -
- Example - Returns last height remote chain by connection id `connection-0`: - - ```shell - neutrond query interchainqueries query-last-remote-height connection-0 - ``` - -Output: - - ```shell - height: "29" - ``` - -
\ No newline at end of file diff --git a/docs/neutron/modules/interchain-queries/events.md b/docs/neutron/modules/interchain-queries/events.md deleted file mode 100644 index 5d0e580e6..000000000 --- a/docs/neutron/modules/interchain-queries/events.md +++ /dev/null @@ -1,17 +0,0 @@ -# Events - -There is one important event that exists in the ICQ module, which is emitted after any action that happened to a registered Interchain Query: -* `EventTypeNeutronMessage` - "neutron" - -### `EventTypeNeutronMessage` - -| Attribute Key | Attribute Value | -|---------------|-------------------------------------------------------------------------------| -| module | `interchainqueries` | -| action | action identifier: `query_updated` or `query_removed` | -| query_id | `{identifier_of_registered_query}` | -| connection_id | `{connection_id_for_query}` | -| owner | `{query_owner}` Note: is only presented in `query_updated` action | -| type | `{query_type}` Note: only presented in `query_updated` action | -| tx_filter | `{transactions_search_filter}` Note: only presented in `query_updated` action | -| kv_key | `{kv_keys}` Note: only presented in `query_updated` action | \ No newline at end of file diff --git a/docs/neutron/modules/interchain-queries/explanation.md b/docs/neutron/modules/interchain-queries/explanation.md new file mode 100644 index 000000000..db0c7ddbe --- /dev/null +++ b/docs/neutron/modules/interchain-queries/explanation.md @@ -0,0 +1,243 @@ +# Explanation + +## How do KV Interchain Queries work? + +A KV Interchain Query allows a smart contract to securely access the storage of a remote blockchain. This secure access relies on: + +- The [IAVL tree](https://github.com/cosmos/iavl), a data storage structure used in Cosmos-SDK-based blockchains. Each piece of data (value) is a leaf node in the tree, uniquely identified by its data path (key). +- The [abci_query](https://docs.cometbft.com/v0.38/spec/rpc/#abciquery) RPC, which provides access to the storage (`IAVL tree`) for reading operations using a specific path. + +Typical Flow of KV Interchain Queries Usage: + +1. A smart contract developer identifies that their interchain protocol requires access to the state of a remote chain and determines the specific data needed. + +2. The developer creates a list of storage paths that contain the required data. + +3. The developer writes and deploys a smart contract that includes logic for registering an Interchain Query and a callback to handle the query results. + +4. The smart contract registers a KV Interchain Query with the necessary set of keys. The registered query is saved in the `interchainqueries` module's state. + +5. An [Interchain Query relayer](/neutron/modules/interchain-queries/explanation#what-is-an-interchain-query-relayer) reads the state of the `interchainqueries` module, finds the registered query and its parameters, and performs the `abci_query` RPC. This call returns a set of key-value pairs along with proofs from the `IAVL tree`. + +6. The relayer submits the key-value pairs and proofs to the `interchainqueries` module. This operation is called KV Interchain Query result submission. The module verifies the result using the proofs, stores it on-chain, and notifies the owning smart contract about the new result. + +7. The smart contract retrieves the result from the module's storage and processes it as needed. + +8. Steps 5-7 are repeated periodically until the query is removed. + +**Might be interesting:** +- [The dependency of KV Interchain Queries on remote chain storage layout](/neutron/modules/interchain-queries/explanation#the-dependency-of-kv-interchain-queries-on-remote-chain-storage-layout) +- [How to register and handle a KV Interchain Query](/neutron/modules/interchain-queries/how-to#how-to-register-and-handle-a-kv-interchain-query) +- [How to register and handle a KV Interchain Query with custom keys](/neutron/modules/interchain-queries/how-to#how-to-register-and-handle-a-kv-interchain-query-with-custom-keys) + +## How do TX Interchain Queries work? + +A TX Interchain Query allows a smart contract to securely subscribe to and react to transactions occurring on a remote chain. This subscription relies on: + +- [Events](https://docs.cosmos.network/v0.50/learn/advanced/events) emitted during transaction execution. Events are structured logs of actions that occur within the blockchain. +- The [tx_search](https://docs.cometbft.com/v0.38/app-dev/indexing-transactions#querying-transactions-events) RPC, which enables searching for transactions based on the events they emit. + +Typical Flow of TX Interchain Queries Usage: + +1. A smart contract developer identifies that their interchain protocol requires responding to specific actions on a remote chain and determines the relevant actions. + +2. Based on these requirements, the developer defines a set of filtering conditions to identify the needed transactions based on their emitted events. + +3. The developer writes and deploys a smart contract that includes logic for registering an Interchain Query and a callback to handle the query results. + +4. The smart contract registers a TX Interchain Query with the specified set of transaction filters. The registered query is saved in the `interchainqueries` module's state. + +5. An [Interchain Query relayer](/neutron/modules/interchain-queries/explanation#what-is-an-interchain-query-relayer) reads the state of the `interchainqueries` module, finds the registered query and its parameters, and performs the `tx_search` RPC. This call returns a list of transactions that match the filters and have been successfully processed on the remote chain. + +6. The relayer submits the list of transactions along with a few headers for each transaction (used to verify the result) to the `interchainqueries` module. This operation is called TX Interchain Query result submission. The module verifies the result using the headers and passes the data to the owning smart contract. + +7. The smart contract processes the result as needed. + +8. Steps 5-7 are repeated periodically until the query is removed. + +**Might be interesting:** +- [How to register and handle a TX Interchain Query](/neutron/modules/interchain-queries/how-to#how-to-register-and-handle-a-tx-interchain-query) +- [How to find out what transaction filter to use](/neutron/modules/interchain-queries/how-to#how-to-find-out-what-transaction-filter-to-use) +- [Why doesn't interchainqueries module store TX query results?](/neutron/modules/interchain-queries/explanation#why-doesnt-interchainqueries-module-store-tx-query-results) +- [Why is it mandatory to do contract's side verification of submitted TX Interchain Query results?](/neutron/modules/interchain-queries/explanation#why-is-it-mandatory-to-do-contracts-side-verification-of-submitted-tx-interchain-query-results) + +## What is an Interchain Query relayer? + +An Interchain Query relayer is an off-chain application that facilitates the functionality of the `interchainqueries` module. Acting as an intermediary between two blockchains, it is similar in concept to an IBC relayer. The main responsibilities of an Interchain Query relayer are: + +- **Monitoring registered Interchain Queries**: Fetching the Interchain Queries that need processing from the `interchainqueries` module's state. + +- **Executing Interchain Queries**: Accessing the remote chain's state based on the parameters defined in the Interchain Query and obtaining proofs for the retrieved data. + +- **Submitting query results**: Delivering the retrieved data and its corresponding proofs to the `interchainqueries` module, which then forwards it to the relevant smart contracts. + +**Might be interesting:** +- [Neutron implementation of an Interchain Query relayer](/relaying/icq-relayer) +- [Limited gas for sudo calls](/neutron/modules/interchain-queries/explanation#limited-gas-for-sudo-calls) + +## The dependency of KV Interchain Queries on remote chain storage layout + +KV Interchain Queries rely heavily on the storage layout of the remote chain, including the paths to IAVL leaf nodes and the data models used to represent the stored information. The accuracy and functionality of KV Interchain Queries depend entirely on the correctness of the paths and the consistency of the data models. This tight coupling introduces several considerations and potential challenges for dApp developers. The [neutron-sdk interchain queries related package](https://docs.rs/neutron-sdk/0.11.0/neutron_sdk/interchain_queries/index.html#) includes target chain version separation precisely for this reasoning. + +- Each KV Interchain Query requires the exact path to the desired entry in the IAVL tree of the remote chain. These paths are specific to the version and implementation of the modules on the remote chain. If a path is incorrect or becomes outdated, the query will fail. +- The structure of the data retrieved from the IAVL tree must match the expected model in the dApp. Changes in the data model due to updates or modifications in the remote chain's code can lead to errors in decoding or interpreting the retrieved data. + +Recomendations for dApp developers: + +1. **Follow and communicate**: + - Regularly monitor release notes, upgrade announcements, and repository updates of the target chains. + - Participate in the target chain’s community discussions to stay informed about upcoming changes. + - Work closely with remote chain developers to understand their module design and planned upgrades. + - Advocate for better documentation and tools that simplify storage layout discovery. + +2. **Secure with code**: + - Continuously run your dApp on the testnet of the target chain to catch breaking changes before they affect the mainnet. + - Set up automated integration tests that verify the correctness of KV Interchain Queries against the latest builds. + - Set up monitoring for target chain versions to detect changes proactively. + - Build fallback mechanisms to detect and handle query failures gracefully. + +In the event of an upcoming breaking change, update your codebase and prepare to upgrade your dApp on the mainnet once the remote chain is updated. + +## What's the role of IBC connections in Interchain Queries and how to choose one? + +IBC [clients](https://ibc.cosmos.network/v8/ibc/overview/#clients) and [connections](https://ibc.cosmos.network/v8/ibc/overview/#connections) play a crucial role in ensuring the authenticity of Interchain Queries. When an IBC connection is initialized, it creates IBC clients and verifies that their states are accurate for their respective counterparties. These client states are then used to verify the results of Interchain Queries. The chosen connection ID for an Interchain Query directly impacts the trustworthiness of the entire Interchain Queries-based application. + +There are two ways to find an appropriate IBC connection ID: + +- **[Use an existing IBC connection](/neutron/modules/interchain-queries/how-to#how-to-choose-the-right-ibc-connection-id-for-an-interchain-query-and-verify-it):** Select from connections that are already set up. + +- **Create a new IBC connection:** Establish a connection between the chains yourself. For instance, the [Hermes IBC relayer](https://hermes.informal.systems/documentation/commands/path-setup/connections.html#establish-connection) can be used to create IBC connections. + +## Why is there a query creation deposit? + +To ensure the ledger is kept clean of unused or outdated queries, a deposit mechanism is implemented. When a contract sends a [RegisterInterchainQuery](/neutron/modules/interchain-queries/api#registerinterchainquery) message, the required deposit is deducted from the contract's balance as an escrow payment for the query creation. This deposit is refunded when the query is removed by issuing a [RemoveInterchainQuery](/neutron/modules/interchain-queries/api#removeinterchainquery) message. + +The amount required for the deposit is defined by the `query_deposit` [module parameter](/neutron/modules/interchain-queries/api#params). + +In essence, query owners are expected to remove their queries once they are no longer needed. If a query is not used within the `query_submit_timeout` period and the owner does not remove it, any network user is allowed to clean up the chain by removing the unused query. As a reward, the deposited assets are transferred to the user who performs the cleanup. + +**Might be interesting:** +- [What are the rules for creation deposit refund?](/neutron/modules/interchain-queries/explanation#what-are-the-rules-for-creation-deposit-refund) + +## What are the rules for creation deposit refunds? + +The query creation deposit is refunded when an Interchain Query is removed by issuing a [RemoveInterchainQuery](/neutron/modules/interchain-queries/api#removeinterchainquery) message. This message can be sent either by the query owner or, under certain conditions, by anyone. + +If the query is within its active **query service period** (recently registered or continuously updated), it is considered valuable to its owner, and only the owner can remove it and reclaim the deposit. If the query has not been updated or used for a long time (beyond the **query service period**), it is likely abandoned. In this case, anyone can remove it and claim the deposit as a reward for cleaning up the chain's state. + +Parameters Defining Removal Permissions: + +1. **`query_submit_timeout`**: A [registered query property](/neutron/modules/interchain-queries/api#registeredquery) specifying the number of blocks that define the renewable **query service period**. This period starts when a query is registered and renews with each query update. The `query_submit_timeout` value is set based on the [module parameters](/neutron/modules/interchain-queries/api#params) at the time of query registration. + +2. **`last_submitted_result_local_height`**: A [registered query property](/neutron/modules/interchain-queries/api#registeredquery) indicating the block height on the home chain when the query was last updated. + +3. **`registered_at_height`**: A [registered query property](/neutron/modules/interchain-queries/api#registeredquery) indicating the block height on the home chain when the query was initially registered. + +Removal Permissions: + +- **Within the Query Service Period** + Only the query owner can remove the query. A query is considered within the service period if the following condition is true: + ``` + within_service_period = + current_height <= query.last_submitted_result_local_height + params.query_submit_timeout || + current_height <= query.registered_at_height + params.query_submit_timeout + ``` + +- **Beyond the Query Service Period** + Anyone can remove the query and claim the deposit as a reward. A query is beyond the service period if the following condition is true: + ``` + beyond_service_period = + current_height > query.last_submitted_result_local_height + params.query_submit_timeout && + current_height > query.registered_at_height + params.query_submit_timeout + ``` +**Might be interesting:** +- [How Interchain Query results are removed?](/neutron/modules/interchain-queries/explanation#how-interchain-query-results-are-removed) + +## Why is it mandatory to do contract's side verification of submitted TX Interchain Query results? + +A crucial aspect of Interchain Queries is that result submissions are **permissionless**. This means that anyone — not just your designated relayer — can submit the results of TX Interchain Queries registered by your contract to the `interchainqueries` module. + +Since events are not part of the blockchain consensus and are not included in the transaction results (and thus not in TX Interchain Query result submissions), the `interchainqueries` module cannot ensure that the submitted transactions match the filters specified in your query. While the module can confirm that the submitted data comes from a valid transaction, it cannot determine whether the transaction satisfies your specific filtering criteria. + +To address this limitation, your contract's `SudoTXQueryResult` handler must include additional checks to verify that the submitted transactions meet the criteria defined in your query's transaction filters. At a minimum, you should confirm that: + +1. The messages in the transaction body have the correct message types. +2. The message values align with your filter conditions. + +Suppose you register a TX Interchain Query to track [Undelegate transactions issued by a specific address](/neutron/modules/interchain-queries/how-to#how-to-find-out-what-transaction-filter-to-use). Your contract should verify: + +- The message type is `/cosmos.staking.v1beta1.MsgUndelegate`. +- The `delegator_address` matches the specific address, e.g., `cosmos17s3uhcvrwrsp2ldjvxp8rseyc3ulpchdry87hp`. + +These checks must be implemented for each transaction filter in your query. + +If you skip these checks, a malicious relayer could submit valid Tendermint transactions that don't meet your query's filters. This could disrupt your business logic. Using the above example, if you fail to verify the `delegator_address`, any valid Undelegate transaction — regardless of the sender — could pass through, potentially compromising your application's functionality. + +**Might be interesting:** +- [How to find out what transaction filter to use](/neutron/modules/interchain-queries/how-to#how-to-find-out-what-transaction-filter-to-use) +- [How to register and handle a TX Interchain Query](/neutron/modules/interchain-queries/how-to#how-to-register-and-handle-a-tx-interchain-query) + +## Why is the Proof field nullified in QueryResult RPC response? + +The `interchainqueries` module requires KV proofs only during the submission process to verify that the submitted values match the provided proofs. Storing these proofs afterward would unnecessarily consume blockchain storage without serving any practical purpose. The KV results saved on-chain can be trusted because they are cryptographically verified during the submission step by the module. + +The `Proof` field is present but empty because the [QueryResult](https://pkg.go.dev/github.com/neutron-org/neutron/v4@v4.0.1/x/interchainqueries/types#QueryResult) type is used both for query result submission and for storing query results in the module's state. This dual-purpose usage can cause confusion, but more specific types may be introduced in the future to address this issue. + +## Why doesn't interchainqueries module store TX query results? + +Unlike KV Interchain Queries, which store the latest result on-chain for retrieval, TX Interchain Queries involve a list of potential results (transactions that match the filter). Storing the entire list of results on-chain would require a significant amount of storage, which is impractical. + +Instead of saving results on-chain, the `interchainqueries` module directly sends the full TX results to the owning smart contracts during the [TxQueryResult](/neutron/modules/interchain-queries/api#messagetxqueryresult) `sudo` call. Once the `sudo` call is successfully completed, the results are discarded, minimizing storage use. + +The module stores only two things related to TX Interchain Queries: + +1. **Hashes of processed transactions**: This prevents the same transactions from being processed multiple times. +2. **[Failures appeared during sudo calls](#what-happens-if-a-sudo-callback-to-a-smart-contract-owning-an-interchain-query-fails)**. + +**Might be interesting:** +- [How to register and handle a TX Interchain Query](/neutron/modules/interchain-queries/how-to#how-to-register-and-handle-a-tx-interchain-query) + +## What are entry points and sudo calls? + +[Entry points](https://docs.cosmwasm.com/core/entrypoints) are functions in your smart contract that can be invoked by external entities. [Sudo](https://docs.cosmwasm.com/core/entrypoints/sudo) calls are special messages issued by the chain itself. These messages are routed to a dedicated `sudo` entry point, which is only accessible to the chain. + +## Limited gas for sudo calls + +The `interchainqueries` module relies on the [contractmanager](/neutron/modules/contract-manager/overview) module to handle `sudo` operations. The `contractmanager` module [enforces strict gas limits](/neutron/modules/contract-manager/overview#gas-limitation) on `sudo` callbacks, defined by its `sudo_call_gas_limit` parameter, to prevent excessive gas usage. + +To manage cases where computations exceed the allocated gas, the `contractmanager` module recommends separating `sudo` callbacks from heavy computations. One approach is to store the `sudo` callback payload in the contract's state during the `sudo` call and then process it later in a separate external `execute` call. This method ensures that the gas limit for `sudo` calls is not exceeded, while still allowing complex logic to be executed when needed. + +**Might be interesting:** +- [What are entry points and sudo calls?](/neutron/modules/interchain-queries/explanation#what-are-entry-points-and-sudo-calls) + +## What happens if a sudo callback to a smart contract owning an Interchain Query fails? + +If a `sudo` callback fails, the `interchainqueries` module records the failure in the [contractmanager](/neutron/modules/contract-manager/overview) module. This record includes all relevant information about the query and a redacted error message (reduced to the codespace and error code). A full error message is emitted as an [event](https://docs.cosmos.network/v0.50/learn/advanced/events) to provide more context about the failure. For details, see the [message events emission](/neutron/modules/interchain-queries/api#sudo) section. + +Failures due to `out of gas` errors are also recorded. + +Failed query result submissions can be retrieved from the `contractmanager` module and resubmitted to the `interchainqueries` module. For more information about reading and resubmitting failed operations, refer to the [contractmanager documentation](/neutron/modules/contract-manager/overview). + +## How are Interchain Query results removed? + +When a [RemoveInterchainQuery](/neutron/modules/interchain-queries/api#removeinterchainquery) message is successfully executed, the results of the respective Interchain Query are removed from the chain's storage. The removal process depends on the type of the Interchain Query: + +- **KV Query Results**: + KV query results are single records in the store with a predictable gas consumption for removal. These records are removed immediately during the handling of the removal message. + +- **TX Query Results**: + TX query results consist of hashes of remote chain transactions, with each hash being a separate record in the store. The gas consumption for removal depends on the number of hashes, which introduces uncertainty. + + To handle this, TX query results are removed in batches during the `EndBlock` phase. When a removal message is processed, the TX Interchain Query is marked for removal. Subsequently, in each `EndBlock`, a small batch of hashes is removed. The batch size is controlled by the `tx_query_removal_limit` [module parameter](/neutron/modules/interchain-queries/api#params). + +This approach ensures that gas consumption remains manageable while removing large numbers of TX query results. + +## Configuring your own remote chain RPC node for TX ICQ usage + +If you are running your own RPC node for the target chain, ensure that its configuration supports the transaction filters defined in your TX Interchain Queries. Specifically, you need to adjust the following parameters: + +- **`pruning` parameter**: Configure this in the [app.toml](https://docs.cosmos.network/v0.50/learn/advanced/config) file to retain the required historical data for your queries. + +- **`indexer` parameter**: Set this in the [config.toml](https://docs.cometbft.com/v0.38/core/configuration) file to enable transaction indexing. + +Proper configuration of these parameters ensures that the node can provide the necessary data for your TX Interchain Queries. \ No newline at end of file diff --git a/docs/neutron/modules/interchain-queries/how-to.md b/docs/neutron/modules/interchain-queries/how-to.md new file mode 100644 index 000000000..ffb0f1efa --- /dev/null +++ b/docs/neutron/modules/interchain-queries/how-to.md @@ -0,0 +1,686 @@ +# How To + +## How to choose the right IBC connection ID for an Interchain Query and verify it + +This guide explains how to identify and verify an IBC connection between Neutron and CosmosHub. + +#### 1. Find an existing IBC connection using an explorer + +Visit the [map of zones](https://mapofzones.com/zones/neutron-1/peers?columnKey=ibcVolumeIn&period=7d). You may find multiple connections between the two chains. For Neutron and CosmosHub, we’ll use `connection-0`. + +#### 2. Pick a Neutron RPC node from the chain registry + +Go to [Neutron's chain registry page](https://github.com/cosmos/chain-registry/blob/master/neutron/chain.json), choose an RPC node from the `apis` section, and use it in subsequent `neutrond` queries with the `--node` flag. + +#### 3. Gather Neutron-side information about the chosen IBC connection + +Retrieve the IBC client ID and counterparty details for `connection-0`. + +
+Show code + +``` +neutrond q ibc connection end connection-0 --node https://rpc-voidara.neutron-1.neutron.org + +connection: +client_id: 07-tendermint-0 +counterparty: + client_id: 07-tendermint-1119 + connection_id: connection-809 + ... +``` +
+ +#### 4. Check the counterparty chain ID for the Neutron-side IBC connection + +Ensure that the counterparty chain ID of the Neutron-side IBC client matches the CosmosHub chain ID. + +
+Show code + +``` +neutrond q ibc client state 07-tendermint-0 --node https://rpc-voidara.neutron-1.neutron.org + +client_state: + ... + chain_id: cosmoshub-4 << matches the CosmosHub chain ID + ... +``` +
+ +#### 5. Pick a CosmosHub RPC node from the chain registry + +Visit [CosmosHub's chain registry page](https://github.com/cosmos/chain-registry/blob/master/cosmoshub/chain.json), select an RPC node from the `apis` section, and use it in `gaiad` queries with the `--node` flag. + +#### 6. Verify that CosmosHub's counterparty corresponds to Neutron + +Using the counterparty information from Step 3, confirm that CosmosHub's IBC connection and client details match Neutron's information. + +
+Show code + +``` +gaiad q ibc connection end connection-809 --node https://cosmoshub.tendermintrpc.lava.build:443 + +connection: + client_id: 07-tendermint-1119 << matches the third step's connection.counterparty.client_id + counterparty: + client_id: 07-tendermint-0 << matches the third step's connection.client_id + connection_id: connection-0 << matches the third step's connection-id query parameter +``` + +``` +gaiad q ibc client state 07-tendermint-1119 --node https://cosmoshub.tendermintrpc.lava.build:443 + +client_state: + ... + chain_id: neutron-1 << matches the Neutron chain ID + ... +``` +
+ +By following these steps, you can ensure that the IBC connection ID you choose is valid and correctly corresponds to the intended chains. + +**Might be interesting:** +- [What's the role of IBC connections in Interchain Queries and how to choose one?](/neutron/modules/interchain-queries/explanation#whats-the-role-of-ibc-connections-in-interchain-queries-and-how-to-choose-one) + +## How to find out what transaction filter to use + +Imagine you need your Interchain Query-based smart contract to track undelegations made by `cosmos17s3uhcvrwrsp2ldjvxp8rseyc3ulpchdry87hp` on CosmosHub. + +#### 1. Find the up-to-date source code of the staking module + +1. Locate the current version of the `staking` module used by CosmosHub. Check the [chain registry](https://github.com/cosmos/chain-registry/blob/5e684932c8421a59cebe0b4cc5a2fde2d0633b44/cosmoshub/chain.json#L36-L37) to find the repository and version in use, e.g., `v21.0.0`. +2. In the `gaia` repository for `v21.0.0`, locate the [cosmos-sdk](https://github.com/cosmos/gaia/blob/v21.0.0/go.mod#L24) import in the `go.mod` file, e.g., `v0.50.9`. In this case, the `cosmos-sdk` version is replaced with a special release [v0.50.9-lsm](https://github.com/cosmos/gaia/blob/v21.0.0/go.mod#L251-L252) +3. Access the `staking` module's source code in the [cosmos-sdk with tag v0.50.9-lsm](https://github.com/cosmos/cosmos-sdk/tree/v0.50.9-lsm/x/staking). + +:::note +Currently used versions might be different by the time you read this. +::: + +#### 2. Find the handler managing undelegations + +Identify the [Undelegate handler](https://github.com/cosmos/cosmos-sdk/tree/v0.50.9-lsm/x/staking/keeper/msg_server.go#L455-L456) in the `staking` module's keeper. + +#### 3. Locate the events emitted during undelegations + +Examine the [event emission section](https://github.com/cosmos/cosmos-sdk/tree/v0.50.9-lsm/x/staking/keeper/msg_server.go#L542-L550) of the Undelegate handler code. + +:::note +As an alternative for Steps 1-3, you can issue a transaction similar to the ones you want to capture using your transactions filter. After broadcasting the transaction, observe the emitted events list and use the event types and attributes to construct your query. +::: + +#### 4. Create a transaction filter using the events + +Match the event type and attributes emitted. For this scenario, use the filter: +`unbond.delegator=cosmos17s3uhcvrwrsp2ldjvxp8rseyc3ulpchdry87hp` +This corresponds to: +`types.EventTypeUnbond.types.AttributeKeyDelegator = cosmos17s3uhcvrwrsp2ldjvxp8rseyc3ulpchdry87hp`. + +#### 5. Test the query filter + +Verify your filter by running the following `gaiad q txs` query to ensure it retrieves the expected results. + +
+Show code + +``` +gaiad q txs --query "unbond.delegator='cosmos17s3uhcvrwrsp2ldjvxp8rseyc3ulpchdry87hp'" + +... +txs: +- code: 0 + ... + height: "22645909" + ... + body: + ... + messages: + - '@type': /cosmos.staking.v1beta1.MsgUndelegate + amount: + amount: "20045172" + denom: uatom + delegator_address: cosmos17s3uhcvrwrsp2ldjvxp8rseyc3ulpchdry87hp + validator_address: cosmosvaloper1zqgheeawp7cmqk27dgyctd80rd8ryhqs6la9wc +``` +
+ +:::note Sure that the query is correct but the result is empty? +If the query appears correct but no results are returned, it may indicate that the RPC node is not properly configured. For more information, refer to the [RPC node configuration guide](/neutron/modules/interchain-queries/explanation#configuring-your-own-remote-chain-rpc-node-for-tx-icq-usage). +::: + +**Might be interesting:** +- [How do TX Interchain Queries work?](/neutron/modules/interchain-queries/explanation#how-do-tx-interchain-queries-work) + +## How to register and handle a KV Interchain Query + +This guide provides a brief guide to registering a KV Interchain Query and handling its results using the [neutron-std](https://docs.rs/neutron-std/4.2.2-rc/neutron_std) and [neutron-sdk](https://docs.rs/neutron-sdk/0.11.0/neutron_sdk) libraries in a smart contract. + +#### 1. Find the appropriate helper function in Neutron SDK + +Locate the helper function for registering an Interchain Query that suits your requirements in the [neutron-sdk](https://docs.rs/neutron-sdk/0.11.0/neutron_sdk/interchain_queries/v045/register_queries/index.html). For this example, we’ll use the [new_register_balances_query_msg](https://docs.rs/neutron-sdk/0.11.0/neutron_sdk/interchain_queries/v045/register_queries/fn.new_register_balances_query_msg.html) function. + +:::note Couldn't find the required helper function? +If no predefined helper function meets your needs, refer to the [How to register a KV Interchain Query with custom keys](/neutron/modules/interchain-queries/how-to#how-to-register-and-handle-a-kv-interchain-query-with-custom-keys) section. +::: + +#### 2. Define the Interchain Query registration entry point + +Create an `execute` message handler in your contract to register the Interchain Query using the helper function as a [submessage](https://docs.cosmwasm.com/docs/smart-contracts/message/submessage/). + +
+Show code + +```rust +use neutron_sdk::interchain_queries::v047::register_queries::new_register_balances_query_msg; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + _deps: DepsMut, + env: Env, + _info: MessageInfo, + msg: ExecuteMsg, +) -> NeutronResult { + match msg { + ExecuteMsg::RegisterBalancesQuery { + connection_id, + addr, + denoms, + update_period, + } => register_balances_query(env, connection_id, addr, denoms, update_period), + } +} + +pub fn register_balances_query( + env: Env, + connection_id: String, + addr: String, + denoms: Vec, + update_period: u64, +) -> NeutronResult { + let msg = new_register_balances_query_msg( + env.contract.address, + connection_id, + addr.clone(), + denoms, + update_period, + )?; + + // Send the ICQ registration message as a submessage to receive a reply callback + Ok(Response::new().add_submessage(SubMsg { + id: REGISTER_BALANCES_ICQ_REPLY_ID, + payload: to_json_binary(&addr)?, + msg: msg, + gas_limit: None, + reply_on: ReplyOn::Success, + })) +} +``` +[View full code here](https://github.com/neutron-org/neutron-dev-contracts/blob/07d0f1e6b4c36b8541e74530986a6baba2710cf1/contracts/docs/interchainqueries/howto/register_kv_icq/src/contract.rs#L47-L87) +
+ +#### 3. Define the Interchain Query registration response handler + +In the reply handler, decode the submessage result as a [MsgRegisterInterchainQueryResponse](https://docs.rs/neutron-std/4.2.2-rc/neutron_std/types/neutron/interchainqueries/struct.MsgRegisterInterchainQueryResponse.html) to access the assigned Interchain Query ID. + +
+Show code + +```rust +use neutron_std::types::neutron::interchainqueries::MsgRegisterInterchainQueryResponse; + +#[entry_point] +pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> NeutronResult { + match msg.id { + REGISTER_BALANCES_ICQ_REPLY_ID => { + // decode the reply msg result as MsgRegisterInterchainQueryResponse + let resp = MsgRegisterInterchainQueryResponse::decode( + msg.result + .into_result() + .map_err(StdError::generic_err)? + .msg_responses[0] + .clone() + .value + .to_vec() + .as_slice(), + )?; + + // memorize the address that corresponds to the query id to use it later in the + // SudoMsg::KVQueryResult handler. + let addr: String = from_json(&msg.payload)?; + ICQ_ID_TO_WATCHED_ADDR.save(deps.storage, resp.id, &addr)?; + + Ok(Response::default()) + } + _ => Err(NeutronError::InvalidReplyID(msg.id)), + } +} +``` +[View full code here](https://github.com/neutron-org/neutron-dev-contracts/blob/07d0f1e6b4c36b8541e74530986a6baba2710cf1/contracts/docs/interchainqueries/howto/register_kv_icq/src/contract.rs#L104-L129) +
+ +#### 4. Define the Interchain Query results processing handler + +Retrieve the query result from the `interchainqueries` module's storage using the [query_balance](https://docs.rs/neutron-sdk/0.11.0/neutron_sdk/interchain_queries/v047/queries/fn.query_balance.html) function and process it. + +
+Show code + +```rust +use neutron_sdk::interchain_queries::v047::queries::query_balance; +use neutron_sdk::sudo::msg::SudoMsg; + +#[entry_point] +pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> NeutronResult { + match msg { + SudoMsg::KVQueryResult { query_id } => sudo_kv_query_result(deps, env, query_id), + _ => Ok(Response::default()), + } +} + +/// The contract's callback for KV query results. Note that only the query id is provided, so you +/// need to read the query result from the state. +pub fn sudo_kv_query_result(deps: DepsMut, env: Env, query_id: u64) -> NeutronResult { + // Get the last submitted ICQ result from the Neutron ICQ module storage + let balance_resp = query_balance(deps.as_ref(), env.clone(), query_id)?; + // Get the address that was registered for the ICQ + let addr = ICQ_ID_TO_WATCHED_ADDR.load(deps.storage, query_id)?; + + // Put your business logic here + // For this example we just preserve the freshly fetched balances in the contract's state + REMOTE_BALANCES.save(deps.storage, addr, &balance_resp.balances)?; + + Ok(Response::default()) +} +``` +[View full code here](https://github.com/neutron-org/neutron-dev-contracts/blob/07d0f1e6b4c36b8541e74530986a6baba2710cf1/contracts/docs/interchainqueries/howto/register_kv_icq/src/contract.rs#L131-L152) +
+ +#### 5. Register the Interchain Query + +Send a [ExecuteMsg::RegisterBalancesQuery](https://github.com/neutron-org/neutron-dev-contracts/blob/9977666069741116cd95200ffb6ae05ab0834eae/contracts/docs/interchainqueries/howto/register_kv_icq/src/msg.rs#L10-L15) message to the contract with the required parameters. + +**Might be interesting:** +- [What are entry points and sudo calls?](/neutron/modules/interchain-queries/explanation#what-are-entry-points-and-sudo-calls) +- [Limited gas for sudo calls](/neutron/modules/interchain-queries/explanation#limited-gas-for-sudo-calls) +- [What happens if a sudo callback to a smart contract owning an Interchain Query fails?](/neutron/modules/interchain-queries/explanation#what-happens-if-a-sudo-callback-to-a-smart-contract-owning-an-interchain-query-fails) + +## How to register and handle a KV Interchain Query with custom keys + +If your KV Interchain Query cannot be handled using the helpers from the [Interchain Queries related package](https://docs.rs/neutron-sdk/0.11.0/neutron_sdk/interchain_queries/v045/register_queries/index.html) in [neutron-sdk](https://docs.rs/neutron-sdk/0.11.0/neutron_sdk), you can define the `KVKeys` manually. This example demonstrates registering an [Account](https://github.com/cosmos/cosmos-sdk/blob/853dbbf3e84900214137805d78e325ecd56fd68f/proto/cosmos/auth/v1beta1/query.proto#L27-L31) Interchain Query for `cosmos-hub` `v21.0.0`. + +#### 1. Figure out the respective data path and model + +To determine how the data path is constructed and what the data model is, you need to investigate the module's code. Start by locating [the gRPC handler](https://github.com/cosmos/cosmos-sdk/blob/853dbbf3e84900214137805d78e325ecd56fd68f/x/auth/keeper/grpc_query.go#L62-L63) in the module that corresponds to the data you're interested in. This handler provides a clue about where the data is stored and what the data model is. + +For this example: +- The store key used at [module's keeper initialisation](https://github.com/cosmos/gaia/blob/db2cc90315161d6730551d795558aaa7664aea6f/app/keepers/keepers.go#L207) is [`acc`](https://github.com/cosmos/cosmos-sdk/blob/853dbbf3e84900214137805d78e325ecd56fd68f/x/auth/types/keys.go#L11-L12). +- The data path is the [accounts store prefix](https://github.com/cosmos/cosmos-sdk/blob/853dbbf3e84900214137805d78e325ecd56fd68f/x/auth/types/keys.go#L22-L23) + [hex address representation](https://github.com/cosmos/cosmos-sdk/blob/853dbbf3e84900214137805d78e325ecd56fd68f/x/auth/keeper/grpc_query.go#L72-L76). +- The data model is [`BaseAccount`](https://github.com/cosmos/cosmos-sdk/blob/853dbbf3e84900214137805d78e325ecd56fd68f/x/auth/types/auth.pb.go#L29-L37). + +#### 2. Define Interchain Query registration entry point + +To enable Interchain Query registration, implement an `execute` message handler in your smart contract. This handler will broadcast a [MsgRegisterInterchainQuery](https://docs.rs/neutron-std/4.2.2-rc/neutron_std/types/neutron/interchainqueries/struct.MsgRegisterInterchainQuery.html) message as a [submessage](https://docs.cosmwasm.com/docs/smart-contracts/message/submessage/). Use the data path information derived earlier to configure the message. + +
+Show code + +```rust +use neutron_std::types::neutron::interchainqueries::MsgRegisterInterchainQuery; + +/// Store key for standard **auth** Cosmos-SDK module +pub const AUTH_STORE_KEY: &str = "acc"; +/// Storage prefix for account-by-address store +/// +pub const ACCOUNTS_PREFIX: u8 = 0x01; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + _deps: DepsMut, + env: Env, + _info: MessageInfo, + msg: ExecuteMsg, +) -> NeutronResult { + match msg { + ExecuteMsg::RegisterAccountQuery { + connection_id, + addr, + update_period, + } => register_account_query(env, connection_id, addr, update_period), + } +} + +pub fn register_account_query( + env: Env, + connection_id: String, + addr: String, + update_period: u64, +) -> NeutronResult { + // compose key as accounts store prefix + hex address representation + let mut key: Vec = vec![ACCOUNTS_PREFIX]; + key.extend_from_slice(decode_and_convert(&addr)?.as_slice()); + + let msg = MsgRegisterInterchainQuery { + query_type: QueryType::KV.into(), + keys: vec![KvKey { + path: AUTH_STORE_KEY.to_string(), + key: key, + }], + transactions_filter: String::default(), + connection_id, + update_period, + sender: env.contract.address.to_string(), + }; + + // Send the ICQ registration message as a submessage to receive a reply callback + Ok(Response::new().add_submessage(SubMsg { + id: REGISTER_ACCOUNT_ICQ_REPLY_ID, + payload: to_json_binary(&addr)?, + msg: msg.into(), + gas_limit: None, + reply_on: ReplyOn::Success, + })) +} +``` +[View full code here](https://github.com/neutron-org/neutron-dev-contracts/blob/07d0f1e6b4c36b8541e74530986a6baba2710cf1/contracts/docs/interchainqueries/howto/register_custom_kv_icq/src/contract.rs#L56-L102) +
+ +#### 3. Define Interchain Query registration response handling + +In the reply handler, decode the submessage result as a [MsgRegisterInterchainQueryResponse](https://docs.rs/neutron-std/4.2.2-rc/neutron_std/types/neutron/interchainqueries/struct.MsgRegisterInterchainQueryResponse.html) to retrieve the assigned Interchain Query ID. + +
+Show code + +```rust +use neutron_std::types::neutron::interchainqueries::MsgRegisterInterchainQueryResponse; + +#[entry_point] +pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> NeutronResult { + match msg.id { + REGISTER_ACCOUNT_ICQ_REPLY_ID => { + // decode the reply msg result as MsgRegisterInterchainQueryResponse + let resp = MsgRegisterInterchainQueryResponse::decode( + msg.result + .into_result() + .map_err(StdError::generic_err)? + .msg_responses[0] + .clone() + .value + .to_vec() + .as_slice(), + )?; + + // memorize the address that corresponds to the query id to use it later in the + // SudoMsg::KVQueryResult handler. + let addr: String = from_json(&msg.payload)?; + ICQ_ID_TO_WATCHED_ADDR.save(deps.storage, resp.id, &addr)?; + + Ok(Response::default()) + } + _ => Err(NeutronError::InvalidReplyID(msg.id)), + } +} +``` +[View full code here](https://github.com/neutron-org/neutron-dev-contracts/blob/07d0f1e6b4c36b8541e74530986a6baba2710cf1/contracts/docs/interchainqueries/howto/register_custom_kv_icq/src/contract.rs#L119-L144) +
+ +#### 4. Implement reconstruction of the query result + +Define how the query result should be reconstructed from [StorageValue](https://docs.rs/neutron-std/4.2.2-rc/neutron_std/types/neutron/interchainqueries/struct.StorageValue.html) into a `BaseAccount` instance. This involves decoding the stored values into the appropriate data structure. + +
+Show code + +```rust +use neutron_sdk::interchain_queries::types::KVReconstruct; +use neutron_std::types::neutron::interchainqueries::StorageValue; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +/// A structure that can be reconstructed from **StorageValues**'s for the **Account Interchain Query**. +pub struct BaseAccount { + pub address: String, + pub account_number: String, + pub sequence: String, +} + +impl KVReconstruct for BaseAccount { + fn reconstruct(storage_values: &[StorageValue]) -> NeutronResult { + if storage_values.len() != 1 { + return Err(NeutronError::InvalidQueryResultFormat( + "storage_values length is not 1".into(), + )); + } + + // first level value is Any as sdk.AccountI implementation: + // https://github.com/cosmos/cosmos-sdk/blob/853dbbf3e84900214137805d78e325ecd56fd68f/types/account.go#L9-L32 + let any_value: Any = Any::decode(storage_values[0].value.as_slice())?; + // second level value is BaseAccount: + // https://github.com/cosmos/cosmos-sdk/blob/853dbbf3e84900214137805d78e325ecd56fd68f/x/auth/types/auth.pb.go#L29-L37 + let std_acc: StdBaseAccount = StdBaseAccount::decode(any_value.value.as_slice())?; + Ok(BaseAccount { + address: std_acc.address, + account_number: std_acc.account_number.to_string(), + sequence: std_acc.sequence.to_string(), + }) + } +} +``` +[View full code here](https://github.com/neutron-org/neutron-dev-contracts/blob/07d0f1e6b4c36b8541e74530986a6baba2710cf1/contracts/docs/interchainqueries/howto/register_custom_kv_icq/src/types.rs#L10-L38) +
+ +#### 5. Define Interchain Query results submission handling + +Retrieve the submitted Interchain Query result from the `interchainqueries` module's storage using the [query_kv_result](https://docs.rs/neutron-sdk/0.11.0/neutron_sdk/interchain_queries/queries/fn.query_kv_result.html) helper function. Handle the result by decoding it and performing your contract's desired logic. + +
+Show code + +```rust +use neutron_sdk::sudo::msg::SudoMsg; +use neutron_sdk::interchain_queries::queries::query_kv_result; +use crate::types::BaseAccount; + +#[entry_point] +pub fn sudo(deps: DepsMut, _env: Env, msg: SudoMsg) -> NeutronResult { + match msg { + SudoMsg::KVQueryResult { query_id } => sudo_kv_query_result(deps, query_id), + _ => Ok(Response::default()), + } +} + +/// The contract's callback for KV query results. Note that only the query id is provided, so you +/// need to read the query result from the state. +pub fn sudo_kv_query_result(deps: DepsMut, query_id: u64) -> NeutronResult { + // Get the last submitted ICQ result from the Neutron ICQ module storage and decode it + // as BaseAccount using its KVReconstruct::reconstruct implementation. + let account_resp: BaseAccount = query_kv_result(deps.as_ref(), query_id)?; + // Get the address that was registered for the ICQ + let addr = ICQ_ID_TO_WATCHED_ADDR.load(deps.storage, query_id)?; + + // Put your business logic here + // For this example we just preserve the freshly fetched account in the contract's state + REMOTE_ACCOUNTS.save(deps.storage, addr, &account_resp)?; + + Ok(Response::default()) +} +``` +[View full code here](https://github.com/neutron-org/neutron-dev-contracts/blob/07d0f1e6b4c36b8541e74530986a6baba2710cf1/contracts/docs/interchainqueries/howto/register_custom_kv_icq/src/contract.rs#L146-L168) +
+ +#### 6. Perform Interchain Query registration + +Broadcast a [ExecuteMsg::RegisterAccountQuery](https://github.com/neutron-org/neutron-dev-contracts/blob/9977666069741116cd95200ffb6ae05ab0834eae/contracts/docs/interchainqueries/howto/register_custom_kv_icq/src/msg.rs#L10-L14) message to the smart contract with the required parameters. + +**Might be interesting:** +- [What are entry points and sudo calls?](/neutron/modules/interchain-queries/explanation#what-are-entry-points-and-sudo-calls) +- [Limited gas for sudo calls](/neutron/modules/interchain-queries/explanation#limited-gas-for-sudo-calls) +- [What happens if a sudo callback to a smart contract owning an Interchain Query fails?](/neutron/modules/interchain-queries/explanation#what-happens-if-a-sudo-callback-to-a-smart-contract-owning-an-interchain-query-fails) + +## How to register and handle a TX Interchain Query + +This guide explains how to register a TX Interchain Query and handle its results using the [neutron-std](https://docs.rs/neutron-std/4.2.2-rc/neutron_std) and [neutron-sdk](https://docs.rs/neutron-sdk/0.11.0/neutron_sdk) libraries in a smart contract. + +#### 1. Find out what transaction filter to use + +Determine the appropriate [tx_search](https://docs.cometbft.com/v0.38/app-dev/indexing-transactions#querying-transactions-events) query that matches the transactions you want to process. + +The [How to find out what transaction filter to use](/neutron/modules/interchain-queries/how-to#how-to-find-out-what-transaction-filter-to-use) section provides detailed instructions for crafting a transaction filter. + +#### 2. Define Interchain Query registration entry point + +Implement an `execute` message handler in your contract to handle Interchain Query registration. Use the [MsgRegisterInterchainQuery](https://docs.rs/neutron-std/4.2.2-rc/neutron_std/types/neutron/interchainqueries/struct.MsgRegisterInterchainQuery.html) message. Populate this message using the `tx_search` query identified in step 1 as the `transactions_filter`. + +
+Show code + +```rust +use neutron_std::types::neutron::interchainqueries::MsgRegisterInterchainQuery; + +/// Unbond delegator attribute key. +/// https://github.com/cosmos/cosmos-sdk/blob/8bfcf554275c1efbb42666cc8510d2da139b67fa/x/staking/keeper/msg_server.go#L447-L455 +const UNBOND_DELEGATOR_ATTR: &str = "unbond.delegator"; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + _deps: DepsMut, + env: Env, + _info: MessageInfo, + msg: ExecuteMsg, +) -> NeutronResult { + match msg { + ExecuteMsg::RegisterUndelegationsQuery { + connection_id, + addr, + update_period, + } => register_undelegations_query(env, connection_id, addr, update_period), + } +} + +pub fn register_undelegations_query( + env: Env, + connection_id: String, + addr: String, + update_period: u64, +) -> NeutronResult { + let msg = MsgRegisterInterchainQuery { + query_type: QueryType::TX.into(), + keys: vec![], + // the result filter is unbond.delegator=addr + transactions_filter: to_string(&vec![TransactionFilterItem { + field: UNBOND_DELEGATOR_ATTR.to_string(), + op: TransactionFilterOp::Eq, + value: TransactionFilterValue::String(addr.clone()), + }]) + .map_err(|e| StdError::generic_err(e.to_string()))?, + connection_id, + update_period, + sender: env.contract.address.to_string(), + }; + + Ok(Response::default().add_message(msg)) +} +``` +[View full code here](https://github.com/neutron-org/neutron-dev-contracts/blob/07d0f1e6b4c36b8541e74530986a6baba2710cf1/contracts/docs/interchainqueries/howto/register_tx_icq/src/contract.rs#L55-L93) +
+ +#### 3. Define Interchain Query results submission handling + +Implement a handler to process submitted TX Interchain Query results. This includes: +- Decoding the transaction and its messages. +- Performing [contract-side verification of submitted TX Interchain Query results](/neutron/modules/interchain-queries/explanation#why-is-it-mandatory-to-do-contracts-side-verification-of-submitted-tx-interchain-query-results). +- Handling the results in your business logic. + +
+Show code + +```rust +use neutron_sdk::sudo::msg::SudoMsg; +use cosmos_sdk_proto::cosmos::staking::v1beta1::MsgUndelegate; +use cosmos_sdk_proto::cosmos::tx::v1beta1::{TxBody, TxRaw}; + +const STAKING_UNDELEGATE_MSG_URL: &str = "/cosmos.staking.v1beta1.MsgUndelegate"; + +#[entry_point] +pub fn sudo(deps: DepsMut, env: Env, msg: SudoMsg) -> NeutronResult { + match msg { + SudoMsg::TxQueryResult { + query_id, + height, + data, + } => sudo_tx_query_result(deps, env, query_id, height, data), + _ => Ok(Response::default()), + } +} + +/// The contract's callback for TX query results. +pub fn sudo_tx_query_result( + deps: DepsMut, + _env: Env, + query_id: u64, + _height: Height, + data: Binary, +) -> NeutronResult { + // Decode the transaction data + let tx: TxRaw = TxRaw::decode(data.as_slice())?; + let body: TxBody = TxBody::decode(tx.body_bytes.as_slice())?; + + // Get the registered query by ID and retrieve the delegator address from query's transaction filter + let registered_query: RegisteredQuery = get_registered_query(deps.as_ref(), query_id)?; + let query_tx_filter: Vec = + serde_json_wasm::from_str(registered_query.transactions_filter.as_str())?; + let delegator = match &query_tx_filter[0].value { + TransactionFilterValue::String(s) => s.clone(), + _ => { + return Err(NeutronError::Std(StdError::generic_err( + "undelegations transaction filter value must be a String", + ))) + } + }; + + // the contract's side verification of submitted TX Interchain Query results part + let mut new_undelegations: Vec = vec![]; + for msg in body.messages.iter() { + // Narrow down the messages to only MsgUndelegate ones + if msg.type_url != STAKING_UNDELEGATE_MSG_URL { + continue; + } + // Narrow down the MsgUndelegate messages to only those that match the delegator address + let undelegate_msg = MsgUndelegate::decode(msg.value.as_slice())?; + if undelegate_msg.delegator_address != delegator { + continue; + } + + #[allow(clippy::unwrap_used)] + let undelegation_amount = undelegate_msg.amount.unwrap(); + new_undelegations.push(Coin { + denom: undelegation_amount.denom, + amount: Uint128::from_str(undelegation_amount.amount.as_str())?, + }); + } + + // Put your business logic here + // For this example we just preserve the new undelegations in the state + if !new_undelegations.is_empty() { + let mut undelegations = UNDELEGATED_AMOUNTS + .may_load(deps.storage, delegator.clone())? + .unwrap_or_default(); + undelegations.extend(new_undelegations); + UNDELEGATED_AMOUNTS.save(deps.storage, delegator, &undelegations)?; + } + + Ok(Response::default()) +} +``` +[View full code here](https://github.com/neutron-org/neutron-dev-contracts/blob/07d0f1e6b4c36b8541e74530986a6baba2710cf1/contracts/docs/interchainqueries/howto/register_tx_icq/src/contract.rs#L115-L184) +
+ +#### 4. Perform Interchain Query registration + +Broadcast a [ExecuteMsg::RegisterUndelegationsQuery](https://github.com/neutron-org/neutron-dev-contracts/blob/9977666069741116cd95200ffb6ae05ab0834eae/contracts/docs/interchainqueries/howto/register_tx_icq/src/msg.rs#L10-L14) message to the contract with the required parameters. + +**Might be interesting:** +- [Why is it mandatory to do contract's side verification of submitted TX Interchain Query results?](/neutron/modules/interchain-queries/explanation#why-is-it-mandatory-to-do-contracts-side-verification-of-submitted-tx-interchain-query-results) +- [What are entry points and sudo calls?](/neutron/modules/interchain-queries/explanation#what-are-entry-points-and-sudo-calls) +- [Limited gas for sudo calls](/neutron/modules/interchain-queries/explanation#limited-gas-for-sudo-calls) +- [What happens if a sudo callback to a smart contract owning an Interchain Query fails?](/neutron/modules/interchain-queries/explanation#what-happens-if-a-sudo-callback-to-a-smart-contract-owning-an-interchain-query-fails) diff --git a/docs/neutron/modules/interchain-queries/known-bugs.md b/docs/neutron/modules/interchain-queries/known-bugs.md new file mode 100644 index 000000000..81f459d56 --- /dev/null +++ b/docs/neutron/modules/interchain-queries/known-bugs.md @@ -0,0 +1,17 @@ +# Known bugs + +### Impossibility to retrieve and prove KV data with nil values + +Due to a [bug](https://github.com/cosmos/ics23/issues/134) in the ICS23 package, it's currently not possible to query a `nil` or empty value from a remote chain. If your KV query is registered with a key `K` and the value under this key is `nil` or empty, the submission of such a KV result will fail due to an IAVL-proof verification error: + +``` +failed to verify proof: empty value in membership proof +``` + +Additionally, because of the [nature of IAVL proofs](https://github.com/cosmos/cosmos-sdk/blob/ae77f0080a724b159233bd9b289b2e91c0de21b5/docs/interfaces/lite/specification.md), which is the mechanism used for verifying KV query results, it's also impossible to verify an [IAVL absence proof](https://github.com/cosmos/cosmos-sdk/blob/ae77f0080a724b159233bd9b289b2e91c0de21b5/docs/interfaces/lite/specification.md#iavl-absence-proof) if the proof contains a `nil` or empty value. This results in the error: + +``` +failed to verify proof: could not verify absence of key. Please ensure that the path is correct. +``` + +We are in contact with the team developing ICS32 to resolve this issue as soon as possible. In the meantime, the only way to avoid this problem is to ensure that your queries do not target keys with `nil` or empty values. \ No newline at end of file diff --git a/docs/neutron/modules/interchain-queries/messages.md b/docs/neutron/modules/interchain-queries/messages.md deleted file mode 100644 index f4bf84ac7..000000000 --- a/docs/neutron/modules/interchain-queries/messages.md +++ /dev/null @@ -1,197 +0,0 @@ -# Messages - -### Register Interchain Query - -[`MsgRegisterInterchainQuery`](https://github.com/neutron-org/neutron/blob/v4.2.4/proto/neutron/interchainqueries/tx.proto#L27) can be submitted by smart-contract only via `MsgRegisterInterchainQuery` transaction: - -```protobuf -message MsgRegisterInterchainQuery { - // defines a query type: `kv` or `tx` now - string query_type = 1; - - // is used to define KV-storage keys for which we want to get values from remote chain - repeated KVKey keys = 2; - - // is used to define a filter for transaction search ICQ - string transactions_filter = 3; - - // is IBC connection ID for getting ConsensusState to verify proofs - string connection_id = 4; - - // is used to specify how often (in neutron blocks) the query must be updated - uint64 update_period = 5; - - // is the signer of the message - string sender = 6; -} - -message KVKey { - // Path (storage prefix) to the storage where you want to read value by key - // (usually name of cosmos-sdk module: 'staking', 'bank', etc.) - string path = 1; - // Key you want to read from the storage - bytes key = 2; -} -``` - -> **Note:** the maximum allowed number of KVKey values for a single InterchainQuery is defined by a module's param `MaxKvQueryKeysCount`, default value is 32. - -Currently `query_type` can take the following values: -* `kv` - query **values** from Cosmos-SDK KV-storage on remote chain which are stored under some **keys**. In this case `kv_keys` must be filled in. - -* `tx` - query to search for transactions on remote chain. `transactions_filter` describes a filter by which the [ICQ relayer](/relaying/icq-relayer) will perform the transactions search. [The transaction filter is described in more detail in the overview](/neutron/modules/interchain-queries/overview): - -`MsgRegisterInterchainQuery` returns [`MsgRegisterInterchainQueryResponse`](https://github.com/neutron-org/neutron/blob/v4.2.4/proto/neutron/interchainqueries/tx.proto#L50) where `id` is unique identifier of newly registered interchain query on success: -```protobuf -message MsgRegisterInterchainQueryResponse { - uint64 id = 1; -} -``` - -#### State modifications -* increments last registered query id; -* generates new [RegisteredQuery](https://github.com/neutron-org/neutron/blob/v4.2.4/proto/neutron/interchainqueries/genesis.proto#L11); -* save the record in storage under incremented query id; - -#### Events -Emits [`EventTypeNeutonMessage`](/neutron/modules/interchain-queries/events#eventtypeneutronmessage) with `action` equals `query_updated`. - -### Update Interchain Query - -> **Note:** as well as for query registration, for query updates the maximum allowed number of KVKey values for a single InterchainQuery equals to 32. - -[`MsgUpdateInterchainQueryRequest`](https://github.com/neutron-org/neutron/blob/v4.2.4/proto/neutron/interchainqueries/tx.proto#L126) can be submitted only by the owner of corresponding Interchain Query: -```protobuf -message MsgUpdateInterchainQueryRequest { - uint64 query_id = 1; - repeated KVKey new_keys = 2; - uint64 new_update_period = 3; - string new_transactions_filter = 4; - string sender = 5; // is the signer of the message -} -``` - -Returns just an empty [`MsgUpdateInterchainQueryResponse`](https://github.com/neutron-org/neutron/blob/v4.2.4/proto/neutron/interchainqueries/tx.proto#L134) on success: -```protobuf -message MsgUpdateInterchainQueryResponse { -} -``` - -#### State modifications -* [Updates](https://github.com/neutron-org/neutron/blob/v4.2.4/x/interchainqueries/keeper/msg_server.go#L124) a corresponding `RegisteredQuery` structure. - -#### Events -Emits [`EventTypeNeutonMessage`](/neutron/modules/interchain-queries/events#eventtypeneutronmessage) with `action` equals `query_updated`. - - -### Remove Interchain Query - -[`MsgRemoveInterchainQueryRequest`](https://github.com/neutron-org/neutron/blob/v4.2.4/proto/neutron/interchainqueries/tx.proto#L119) can be submitted only by the owner of corresponding Interchain Query within the query's service period or by anyone beyond it. Read more about this message permissions [here](/neutron/modules/interchain-queries/overview#query-creation-deposit). -```protobuf -message MsgRemoveInterchainQueryRequest { - uint64 query_id = 1; - string sender = 2; // is the signer of the message and the owner of corresponding ICQ -} -``` - -Returns just an empty [`MsgRemoveInterchainQueryResponse`](https://github.com/neutron-org/neutron/blob/v4.2.4/proto/neutron/interchainqueries/tx.proto#L124) on success: -```protobuf -message MsgRemoveInterchainQueryResponse { -} -``` - -#### State modifications -* [Removes](https://github.com/neutron-org/neutron/blob/v4.2.4/x/interchainqueries/keeper/msg_server.go#L97) a corresponding `RegisteredQuery` structure. -* Also removes the query results ([immediately](https://github.com/neutron-org/neutron/blob/v4.2.4/x/interchainqueries/keeper/keeper.go#L135) for a KV query, [deferred in the ICQ module EndBlock](https://github.com/neutron-org/neutron/blob/v4.2.4/x/interchainqueries/module.go#L177) for a TX query). - -#### Events -Emits [`EventTypeNeutonMessage`](/neutron/modules/interchain-queries/events#eventtypeneutronmessage) with `action` equals `query_removed`. - - -### Submit Query Result - -[`MsgSubmitQueryResult`](https://github.com/neutron-org/neutron/blob/v4.2.4/proto/neutron/interchainqueries/tx.proto#L54) can be submitted by any Neutron account via `MsgSubmitQueryResult` transaction: - -```protobuf -message MsgSubmitQueryResult { - uint64 query_id = 1; - string sender = 2; - - // is the IBC client ID for an IBC connection between Neutron chain and target chain (where the result was obtained from) - string client_id = 3; - QueryResult result = 4; -} - -message QueryResult { - repeated StorageValue kv_results = 1; - Block block = 2; - uint64 height = 3; - uint64 revision = 4; - bool allow_kv_callbacks = 5; -} - -message StorageValue { - // is the substore name (acc, staking, etc.) - string storage_prefix = 1; - - // is the key in IAVL store - bytes key = 2; - - // is the value in IAVL store - bytes value = 3; - - // is the Merkle Proof which proves existence of key-value pair in IAVL storage - tendermint.crypto.ProofOps Proof = 4; -} - -message Block { - // We need to know block X+1 to verify response of transaction for block X - // since LastResultsHash is root hash of all results from the txs from the previous block - google.protobuf.Any next_block_header = 1; - - // We need to know block X to verify inclusion of transaction for block X - google.protobuf.Any header = 2; - - TxValue tx = 3; -} - -message TxValue { - tendermint.abci.ResponseDeliverTx response = 1; - - // is the Merkle Proof which proves existence of response in block with height next_block_header.Height - tendermint.crypto.Proof delivery_proof = 2; - - // is the Merkle Proof which proves existence of data in block with height header.Height - tendermint.crypto.Proof inclusion_proof = 3; - - // is body of the transaction - bytes data = 4; -} -``` - -Returns just an empty [`MsgSubmitQueryResultResponse`](https://github.com/neutron-org/neutron/blob/v4.2.4/proto/neutron/interchainqueries/tx.proto#L117) on success: - -```protobuf -message MsgSubmitQueryResultResponse { -} -``` - -#### State modifications -* get registered interchain query info by `MsgSubmitQueryResult.query_id`; -* for every `result` in `MsgSubmitQueryResult.result.kv_results`: - * read IBC connection consensus state from IBC keeper storage with `registered_query.ConnectionID`, `MsgSubmitQueryResult.result.revision`, `MsgSubmitQueryResult.result.height+1`; - * verify `result.Proof` with Merkle Root Hash from consensus state; -* save `MsgSubmitQueryResult.result.kv_results` to the storage: - * clear `MsgSubmitQueryResult.result` from the proofs, Neutron doesn't need them anymore; - * save cleared result to storage with key `registered_query.id`; - * set `registered_query.last_submitted_result_remote_height` to `result.height`; - * set `registered_query.last_submitted_result_local_height` to the current Neutron height; -* callback `MsgSubmitQueryResult.result.kv_results` to thr appropriate smart-contract if needed; -* for every `block` in `MsgSubmitQueryResult.result.blocks`: - * verify `block.next_block_header` and `block.header` by calling [`clientKeeper.UpdateClient(header)`](https://github.com/neutron-org/neutron/blob/v4.2.4/x/interchainqueries/keeper/process_block_results.go#L69); - * [verify](https://github.com/neutron-org/neutron/blob/v4.2.4/x/interchainqueries/keeper/process_block_results.go#L168) `block.txs` with verified headers; -* process every `transaction` in every `block` from `MsgSubmitQueryResult.result.blocks`: - * [check](https://github.com/neutron-org/neutron/blob/v4.2.4/x/interchainqueries/keeper/process_block_results.go#L135) transaction was not processed previously to avoid double submitting - * save generated record to the storage with composite key `bigEndianBytes(registered_query.id) + bigEndianBytes(last_submitted_transaction_id` prefixed by [`SubmittedTxKey`](https://github.com/neutron-org/neutron/blob/v4.2.4/x/interchainqueries/types/keys.go#L38); - * [callback](https://github.com/neutron-org/neutron/blob/v4.2.4/x/interchainqueries/keeper/process_block_results.go#L144) transaction to the appropriate smart-contract; - * [save](https://github.com/neutron-org/neutron/blob/v4.2.4/x/interchainqueries/keeper/process_block_results.go#L151) transaction's hash to the storage to approach double-submission preventing mechanics. diff --git a/docs/neutron/modules/interchain-queries/overview.md b/docs/neutron/modules/interchain-queries/overview.md index 3724a1483..18f9e4029 100644 --- a/docs/neutron/modules/interchain-queries/overview.md +++ b/docs/neutron/modules/interchain-queries/overview.md @@ -1,167 +1,29 @@ # Overview -## Abstract +This document explains the `interchainqueries` module for the Neutron network. -This document specifies the ICQ (**I**nter**C**hain **Q**ueries) module for the Neutron network. +Interchain Queries allow developers to get verifiable data from other blockchains. This feature is essential for creating secure cross-chain applications. The module uses Merkle proofs and IBC clients to confirm the accuracy of the data retrieved directly from the storage of the target blockchain. It allows any smart contract to register Interchain Queries without needing any special module on the target blockchain. -The ICQ module implements a mechanism to retrieve data from remote chains connected to Neutron via IBC. - -## Concepts - -A smart-contract can register two types of Interchain Query for particular chain with some query payload and `update_period`: -* Key-Value query (KV-query) - to read **values** from Cosmos-SDK KV-storage on remote chain which are stored under a set of **keys**; -* Transactions query (TX-query) - find transactions on remote chain under by condition (transactions filter). - -> :warning: **IMPORTANT NOTE ABOUT KV-QUERIES** -> -> **Due to a [bug](https://github.com/cosmos/ics23/issues/134) in ICS23 package, it's currently impossible to query an empty or `nil` value from a remote chain.** -> -> **Meaning if your KV-query is registered with key `K` and a value under this key is `nil` or empty, submission of such** -> **KV result will fail due to IAVL-proof verification error: `failed to verify proof: empty value in membership proof`** -> -> **Moreover, due to [the nature of IAVL-proofs](https://github.com/cosmos/cosmos-sdk/blob/ae77f0080a724b159233bd9b289b2e91c0de21b5/docs/interfaces/lite/specification.md)** -> **(which is an underlying mechanism of verification of a validity of KV-queries results),** -> **it's also impossible to verify** -> **[IAVL-absence proof](https://github.com/cosmos/cosmos-sdk/blob/ae77f0080a724b159233bd9b289b2e91c0de21b5/docs/interfaces/lite/specification.md#iavl-absence-proof)** -> **if it contains IAVL-proof with `nil` or empty value: `failed to verify proof: could not verify absence of key. Please ensure that the path is correct.`** -> -> **We are in contact with ics23 team to fix this issue ASAP, but in the meantime the only way to deal with the bug - is to avoid querying keys with `nil` or empty values.** - - - -ICQ Relayer keeps track of registered Interchain Queries by querying all existed ICQs at the start of work and by subscribing on [Update](https://github.com/neutron-org/neutron/blob/v4.2.4/x/interchainqueries/keeper/msg_server.go#L354) and [Delete](https://github.com/neutron-org/neutron/blob/v4.2.4/x/interchainqueries/keeper/msg_server.go#L370) events which are emitted in corresponding Neutron handlers. When the ICQ Relayer sees that it's time to perform an interchain query, it makes a necessary RPC call to a remote chain and makes the results available for the Neutron's smart contracts by submitting the result to the module. Read more about it at the [Relayer's page](/relaying/icq-relayer#overview). - -Neutron verifies the data and processes the query result depending on the interchain query type: -* in case of a KV-query, the ICQ module saves the result into module's storage, and passed the query id to the contract's -[SudoKVQueryResult](https://github.com/neutron-org/neutron/blob/v4.2.4/x/contractmanager/keeper/sudo.go#L101) [handler](https://github.com/neutron-org/neutron-sdk/blob/v0.5.0/contracts/neutron_interchain_queries/src/contract.rs#L385); -* in case of a TX-query, the ICQ module **does not** save the result to the storage, finds the contract that registered the query, -and passes the full result to the contract's [SudoTXQueryResult](https://github.com/neutron-org/neutron/blob/v4.2.4/x/contractmanager/keeper/sudo.go#L61) [handler](https://github.com/neutron-org/neutron-sdk/blob/v0.5.0/contracts/neutron_interchain_queries/src/contract.rs#L267). - -## Query creation deposit -In order to clean up ledger from not used, outdated queries special deposit mechanism is used. [RegisteredQuery](https://github.com/neutron-org/neutron/blob/v4.2.4/proto/neutron/interchainqueries/genesis.proto#L11) contains `deposit` field, this field is used to collect escrow payment for query creation. In order to return escrow payment a `RemoveInterchainQuery` message should be issued. - -Permission to perform `RemoveInterchainQuery` message is based on three parameters: -1. `query_submit_timeout` — a module parameter which can be thought of as query service period; -2. `last_submitted_result_local_height` — registered query's property representing the Neutron's height the query was updated last time at; -3. `registered_at_height` — registered query's property representing the Neutron's height the query was registered at. - -The permissions to execute `RemoveInterchainQuery` are as follows: -- within the service period (i.e. if `current_height <= last_submitted_result_local_height + query_submit_timeout && current_height <= registered_at_height + query_submit_timeout`) only the query's owner is permissioned to remove it; -- beyond the service period (i.e. if `current_height > last_submitted_result_local_height + query_submit_timeout || current_height > registered_at_height + query_submit_timeout`) anyone can remove the query and take the deposit as a reward. - -Amount of coins to deposit is defined via parameter (`query_deposit`) controlled by governance proposal. - -In other words, it is expected of the query owner to remove its queries when they are not needed anymore. If a query hasn't been in use for the `query_submit_timeout` and owner hasn't removed it, network users are granted with an opportunity to clean the chain up and raise assets for it. - -## Transaction filters - -Since events themselves are not part of the consensus and are not included in the transaction result, it's necessary to -[implement additional checks](https://github.com/neutron-org/neutron-sdk/blob/c197ceacc1c23d2f1283be91f8f90c2be1328db0/contracts/neutron_interchain_queries/src/contract.rs#L197) -in your `SudoTXQueryResult` handler to check that result transactions satisfies your transactions filter. For instance, you can check that messages in the transactions have proper types, payload, etc. -If your contract does not have such checks, malicious relayer can send a fully valid Tendermint transaction which does not satisfy your defined transactions filter, and your business-logic can be broken. - -> NOTE: when registering a TX-query, you write the transaction filters as filters for transaction events. When you check the submitted transaction in your contracts, though, you can only check the information that is stored on-chain (i.e., message fields for messages in a transaction). To put it another way, the set of values that you can use to filter transactions is the intersection of the values that are added to transaction events (used by the ICQ relayer to perform the search) and the values included directly to sdk.Msgs (can be used by your code to check whether the submitted transaction matches your query). - -You can see more info, examples and recommendations about proper transactions result handling [here](https://github.com/neutron-org/neutron-sdk/blob/v0.5.0/contracts/neutron_interchain_txs/src/contract.rs#L439). - - -```json -[{"field": "{eventType}.{attributeKey}", "val": "{attributeValue}", "op": "gte"}, ...] -``` - -Maximum allowed amount of filters is defined by a module's param `MaxTransactionsFilters`, the default value is 32. Supplying more filters than allowed will return an error. - -Supported operators: -* `eq` -* `lt` -* `gt` -* `lte` -* `gte` - -The ICQ relayer can easily parse this format and compose it into usual [Tendermint syntax](https://docs.tendermint.com/v0.33/app-dev/indexing-transactions.html#querying-transactions) for searching transactions. +## Table of contents +- [Overview](/neutron/modules/interchain-queries/overview) — a high-level description of the module. +- [API](/neutron/modules/interchain-queries/api) — details about the module's interface, including endpoints, message models, and emitted events. +- [Explanation](/neutron/modules/interchain-queries/explanation) — an explanation of the module's concepts and the reasoning behind its design choices. +- [How To](/neutron/modules/interchain-queries/how-to) — short guides on performing basic operations with the module. +- [Known Bugs](/neutron/modules/interchain-queries/known-bugs) — a list of known issues in the module. -Suppose you want to search for transactions that meet specific conditions on a remote chain. You can build a query using various filters to narrow down the search. - -##### Finding Transfer Transactions with a Specific Amount - -```json -[{"field": "transfer.amount", "op": "eq", "val": 1000}] -``` - -This filter searches for all transfer transactions with an exact amount of 1000. The ICQ relayer converts it into the Tendermint search string: - -``` -"transfer.amount" = 1000 -``` - -##### Searching for Transactions within a Range of Dates - -```json -[{"field": "timestamp", "op": "gte", "val": "2023-01-01T00:00:00Z"}, {"field": "timestamp", "op": "lt", "val": "2023-02-01T00:00:00Z"}] -``` - -This filter queries for all transactions that were processed between January 1, 2023, and February 1, 2023. The corresponding Tendermint search string would be: - -``` -"timestamp" >= "2023-01-01T00:00:00Z" AND "timestamp" < "2023-02-01T00:00:00Z" -``` - -##### Combining Multiple Conditions - -```json -[{"field": "message.module", "op": "eq", "val": "bank"}, {"field": "transfer.sender", "op": "eq", "val": "neutron1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrstdxvff"}, {"field": "transfer.amount", "op": "gt", "val": 500}] -``` - -This example searches for bank transfer transactions sent by a specific address (`neutron1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrstdxvff`) and with an amount greater than 500. The search string would be: - -``` -"message.module" = "bank" AND "transfer.sender" = "neutron1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrstdxvff" AND "transfer.amount" > 500 -``` - -##### Effects of Filters - -The filters in the `transactions_filter` field allow for refined and targeted querying of transactions. Some effects of applying these filters are: - -- **Increased Efficiency**: By narrowing down the search criteria, the query can return results more quickly, reducing the computational resources required. -- **Improved Relevance**: Filters ensure that only transactions that meet specific criteria are returned, making the results more relevant to the user's needs. -- **Flexibility**: Users can combine different operators and fields to create complex queries that match their exact requirements. -- **Error Handling**: Providing incorrect or conflicting filters might result in an error, so the filter structure must be carefully constructed to avoid issues. - -By understanding the usage of the `transactions_filter` field, developers and users can leverage the power of targeted querying to interact with remote chains in a more effective and efficient manner. - -##### Having Fewer or More Filters - -###### Fewer Filters -**Pros:** -- **Broader Results**: Using fewer filters will generally lead to a larger result set, capturing more transactions that meet broad criteria. -- **Faster Execution**: With less complexity, the query may execute more quickly, as there are fewer conditions to evaluate. - -**Cons:** -- **Less Precision**: Fewer filters may lead to less relevant results if the query is too broad. - -###### More Filters -**Pros:** -- **More Specific Results**: More filters allow for more targeted and precise queries, narrowing down the result set to only the most relevant transactions. -- **Enhanced Control**: More filters offer greater control over the query, enabling more complex and nuanced searches. - -**Cons:** -- **Slower Execution**: More complex queries with multiple filters may take longer to execute, as each additional condition adds to the computational load. -- **Potential Overfitting**: Too many filters may lead to an overly narrow search, missing relevant transactions or even resulting in no results at all if the filters are too restrictive. +## Concepts -##### Good Practices +A smart contract can register an Interchain Query to regularly fetch data from a remote chain's state and process it in any way it needs. There are two types of Interchain Queries: -1. **Start with Core Criteria**: Identify the essential criteria for your query and start with those filters. It helps to focus on what you really need from the results. -2. **Incrementally Refine**: If needed, add additional filters incrementally to refine the results, testing at each stage to ensure relevance. -3. **Avoid Redundancy**: Ensure that each filter adds value to the query and that there are no redundant or conflicting filters. -4. **Test Performance**: Consider testing the query with different numbers of filters to gauge performance and result relevance, especially if using many filters. -5. **Use the Maximum Limit Wisely**: Note there is a maximum allowed amount of filters defined by a module's param `MaxTransactionsFilters`. +- **Key-Value Query (KV Query)**: This type retrieves values from the Cosmos-SDK KV-storage on a remote chain, using a set of keys. It can be used to get account balances, governance proposals, staking information, smart contract states, or any other data stored in the remote chain's KV-storage. Learn more about how KV queries work [here](/neutron/modules/interchain-queries/explanation#how-do-kv-interchain-queries-work). -##### How Many Filters Do You Need? +- **Transactions Query (TX Query)**: This type finds transactions on a remote chain based on a set of parameters and conditions (called a transaction filter). For example, it can find transactions that happened before a specific block height, transactions sent to a certain recipient, or any other condition that can be defined using emitted events. Learn more about how TX queries work [here](/neutron/modules/interchain-queries/explanation#how-do-tx-interchain-queries-work). -The optimal number of filters depends on the specific use case and the balance between precision and performance. Generally, it's best to use the minimum number of filters that provide the necessary specificity for your query. Using too few may yield irrelevant results, while using too many may overly narrow the search or impact performance. +Interchain Queries, like IBC, rely on an intermediary infrastructure called an [Interchain Query relayer](/neutron/modules/interchain-queries/explanation#what-is-an-interchain-query-relayer) to serve smart contracts. The relayer is responsible for collecting the requested information (along with cryptographic proofs to verify its validity) based on the parameters of the Interchain Query. It then delivers this data to the `interchainqueries` module, which forwards it to the smart contracts for processing. -##### Conclusion +This entire process is called Interchain Query execution, and the step where the data is delivered is known as Interchain Query result submission. The `interchainqueries` module provides endpoints and emits specific events to make the relayer's job easier. You can find more details about these events and endpoints in the [API section](/neutron/modules/interchain-queries/api). -The number of filters in a query is a vital consideration, influencing both the relevance of the results and the performance of the query. Striking the right balance requires a thoughtful approach, considering the specific needs of the query, and adhering to good practices for constructing and refining filters. +An Interchain Query relayer submits a query result using the [SubmitQueryResult](/neutron/modules/interchain-queries/api#submitqueryresult) endpoint of the `interchainqueries` module. Through this endpoint, the `interchainqueries` module verifies the result and triggers the corresponding [sudo handler](/neutron/modules/interchain-queries/api#sudo) in the smart contract that owns the Interchain Query. The smart contract uses this handler to process the query result, reacting to the new data received from the target chain. +Another similarity between the `interchainqueries` module and IBC is the use of IBC clients. All Interchain Queries involve reading data from a remote chain's storage, and this data is verified against the `ConsensusState` of an IBC client connected to that chain. During result submission, the `interchainqueries` module checks the submitted storage values using the provided proofs, comparing them against the `ConsensusState` of the IBC client on the Neutron side. This ensures that the authenticity of the retrieved data is guaranteed by the IBC protocol. diff --git a/docs/neutron/modules/interchain-queries/state.md b/docs/neutron/modules/interchain-queries/state.md deleted file mode 100644 index 6bc2155e3..000000000 --- a/docs/neutron/modules/interchain-queries/state.md +++ /dev/null @@ -1,7 +0,0 @@ -# State - -The ICQ module stores one [RegisteredQuery](https://github.com/neutron-org/neutron/blob/v4.2.4/proto/neutron/interchainqueries/genesis.proto#L11) per identifier. -`RegisteredQuery` contains all the necessary info for the ICQ relayer to perform a query on remote chain. `last_submitted_result_local_height`, `last_submitted_result_remote_height` are only used for KV-queries and are modified only when a relayer publishes result for a query. - -Results for interchain queries are stored as: -1. [QueryResult](https://github.com/neutron-org/neutron/blob/v4.2.4/proto/neutron/interchainqueries/tx.proto#L67) structure is stored under unique key containing identifier of `RegisteredQuery`; diff --git a/docs/relaying/icq-relayer.md b/docs/relaying/icq-relayer.md index 91bb56d6b..535f65fa2 100644 --- a/docs/relaying/icq-relayer.md +++ b/docs/relaying/icq-relayer.md @@ -1,18 +1,22 @@ # ICQ Relayer +## Source code + +- Github: https://github.com/neutron-org/neutron-query-relayer + ## Overview [Interchain Queries](/neutron/modules/interchain-queries/overview) allow smart contracts to make queries to a remote chain. An ICQ Relayer is a required component for making them possible. It acts as a facilitator between the Neutron chain and a querying chain, gathering queries that are needed to be performed from the Neutron, actually performing them, and eventually making the results available for the Neutron's smart contracts. These three main responsibilities are described in details below. -If you are a smart contracts developer and up to develop your dApp on Neutron, you will most likely need your own ICQ Relayer to manage your Interchain Queries. +If you are a smart contracts developer and up to develop your dApp on Neutron, you will most likely need your own ICQ Relayer to manage your Interchain Queries. ### Queries gathering -All registered Interchain Queries and their parameters are stored in the eponymous module and available by its [query interface](/neutron/modules/interchain-queries/client#queries). The Relayer utilises the module's interface in order to initialise the performing list of queries. This is how the Relayer maintains the list of queries to be executed: +All registered Interchain Queries and their parameters are stored in the eponymous module and available by its [query interface](/neutron/modules/interchain-queries/api#registeredqueries). The Relayer utilises the module's interface in order to initialise the performing list of queries. This is how the Relayer maintains the list of queries to be executed: - on initialisation, the ICQ module `RegisteredQueries` query is executed with the `RELAYER_REGISTRY_ADDRESSES` parameter used for the `Owners` field; - then the parameter `RELAYER_REGISTRY_QUERY_IDS` will be used to filter out queries if it is not empty; -- during the rest of the run, the Relayer listens to the ICQ module's `query_update` and `query_removed` [events](/neutron/modules/interchain-queries/events) and modifies the queries list and parameters correspondingly. +- during the rest of the run, the Relayer listens to the ICQ module's events emitted on [RegisterInterchainQuery](/neutron/modules/interchain-queries/api#registerinterchainquery), [UpdateInterchainQuery](/neutron/modules/interchain-queries/api#updateinterchainquery), and [RemoveInterchainQuery](/neutron/modules/interchain-queries/api#removeinterchainquery), and modifies the queries list and parameters correspondingly. The Relayer also listens to the Neutron's `NewBlockHeader` events that are used as a trigger for queries execution. Since each query has its own `update_period`, the Relayer tracks queries execution height and executes only the queries which update time has come. @@ -23,7 +27,7 @@ When the update time comes for a query, the Relayer runs the specified query on necessary KV-keys from the remote chain's storage with [Merkle Proofs](https://github.com/cosmos/cosmos-sdk/blob/ae77f0080a724b159233bd9b289b2e91c0de21b5/docs/interfaces/lite/specification.md). Neutron will need these proofs to [verify](https://github.com/neutron-org/neutron/blob/v4.2.4/x/interchainqueries/keeper/msg_server.go#L251) validity of KV-results on results submission; * in case of a TX-query, the Relayer makes a query to the target chain's [Tendermint RPC](https://docs.tendermint.com/v0.33/app-dev/indexing-transactions.html#querying-transactions) to search transactions by message types, events and attributes which were emitted during transactions execution and were -[indexed](https://docs.tendermint.com/v0.33/app-dev/indexing-transactions.html) by Tendermint. More about Tx query parameters syntax [in the dedicated section](/neutron/modules/interchain-queries/messages#register-interchain-query). When Relayer submits transactions search results to Neutron chain, it **DOES NOT** include events into result (even if events were used for the query), because [events are not deterministic](https://github.com/tendermint/tendermint/blob/bff63aec83a4cfbb3bba253cfa04737fb21dacb4/types/results.go#L47), therefore they can break blockchain consensus. One more important thing about TX queries is that the Relayer is made the way it only searches for and submits transactions within the trusting period of the Tendermint Light Client. Trusting period is usually calculated as `2/3 * unbonding_period`. Read more about Tendermint Light Client and trusted periods [at this post](https://blog.cosmos.network/light-clients-in-tendermint-consensus-1237cfbda104). +[indexed](https://docs.tendermint.com/v0.33/app-dev/indexing-transactions.html) by Tendermint. More about Tx query parameters syntax [in the dedicated section](/neutron/modules/interchain-queries/explanation#how-do-tx-interchain-queries-work). When Relayer submits transactions search results to Neutron chain, it **DOES NOT** include events into result (even if events were used for the query), because [events are not deterministic](https://github.com/tendermint/tendermint/blob/bff63aec83a4cfbb3bba253cfa04737fb21dacb4/types/results.go#L47), therefore they can break blockchain consensus. One more important thing about TX queries is that the Relayer is made the way it only searches for and submits transactions within the trusting period of the Tendermint Light Client. Trusting period is usually calculated as `2/3 * unbonding_period`. Read more about Tendermint Light Client and trusted periods [at this post](https://blog.cosmos.network/light-clients-in-tendermint-consensus-1237cfbda104). ### Results submission diff --git a/docs/tutorials/cosmwasm_icq.md b/docs/tutorials/cosmwasm_icq.md index dc010be27..19c5b66bb 100644 --- a/docs/tutorials/cosmwasm_icq.md +++ b/docs/tutorials/cosmwasm_icq.md @@ -15,7 +15,7 @@ We are going to learn how to: > contracts. You can check out CosmWasm [docs](https://docs.cosmwasm.com/docs) > and [blog posts](https://medium.com/cosmwasm/writing-a-cosmwasm-contract-8fb946c3a516) for entry-level tutorials. -> **Note:** before running any query creation transaction you need to top up your contract address. See [Interchain Queries Overview](../neutron/modules/interchain-queries/overview.md), "Query creation deposit" section. +> **Note:** before running any query creation transaction you need to top up your contract address in order to pay for the [query creation deposit](/neutron/modules/interchain-queries/explanation#why-is-there-a-query-creation-deposit). ## The complete example @@ -204,8 +204,7 @@ fn write_balance_query_id_to_reply_id(deps: DepsMut, reply: Reply) -> StdResult< } ``` -> **Note:** the ICQ module's `RegisterInterchainQueryMsg` message [returns](/neutron/modules/interchain-queries/messages#register-interchain-query) -> an identifier of newly registered Interchain Query in response. So in a real world scenario you should implement a `reply` handler in your contract +> **Note:** the ICQ module's [RegisterInterchainQueryMsg](/neutron/modules/interchain-queries/api#registerinterchainquery) message returns an identifier of newly registered Interchain Query in response. So in a real world scenario you should implement a `reply` handler in your contract > to catch the identifier after the registration, so you'll be able to work with the registered query later. In the snippet above, we create the `ExecuteMsg` enum that contains two `Register` messages for two different queries: @@ -575,7 +574,7 @@ pub fn remove_interchain_query(query_id: u64) -> NeutronResult contracts. You can check out CosmWasm [docs](https://docs.cosmwasm.com/docs) > and [blog posts](https://medium.com/cosmwasm/writing-a-cosmwasm-contract-8fb946c3a516) for entry-level tutorials. -> **Note:** before running any query creation transaction you need to top up your contract address. See [Interchain Queries Overview](../neutron/modules/interchain-queries/overview.md), "Query creation deposit" section. +> **Note:** before running any query creation transaction you need to top up your contract address. See [Interchain Queries Overview](/1.0/neutron/modules/interchain-queries/overview), "Query creation deposit" section. ## The complete example @@ -159,7 +159,7 @@ pub fn register_transfers_query( } ``` -> **Note:** the ICQ module's `RegisterInterchainQueryMsg` message [returns](/neutron/modules/interchain-queries/messages#register-interchain-query) +> **Note:** the ICQ module's `RegisterInterchainQueryMsg` message [returns](/1.0/neutron/modules/interchain-queries/messages#register-interchain-query) > an identifier of newly registered Interchain Query in response. So in a real world scenario you should implement a `reply` handler in your contract > to catch the identifier after the registration, so you'll be able to work with the registered query later. @@ -504,5 +504,5 @@ pub fn remove_interchain_query(query_id: u64) -> NeutronResult contracts. You can check out CosmWasm [docs](https://docs.cosmwasm.com/docs) > and [blog posts](https://medium.com/cosmwasm/writing-a-cosmwasm-contract-8fb946c3a516) for entry-level tutorials. -> **Note:** before running any query creation transaction you need to top up your contract address. See [Interchain Queries Overview](../neutron/modules/interchain-queries/overview.md), "Query creation deposit" section. +> **Note:** before running any query creation transaction you need to top up your contract address. See [Interchain Queries Overview](/2.0/neutron/modules/interchain-queries/overview), "Query creation deposit" section. ## The complete example @@ -204,7 +204,7 @@ fn write_balance_query_id_to_reply_id(deps: DepsMut, reply: Reply) -> StdResult< } ``` -> **Note:** the ICQ module's `RegisterInterchainQueryMsg` message [returns](/neutron/modules/interchain-queries/messages#register-interchain-query) +> **Note:** the ICQ module's `RegisterInterchainQueryMsg` message [returns](/2.0/neutron/modules/interchain-queries/messages#register-interchain-query) > an identifier of newly registered Interchain Query in response. So in a real world scenario you should implement a `reply` handler in your contract > to catch the identifier after the registration, so you'll be able to work with the registered query later. @@ -575,7 +575,7 @@ pub fn remove_interchain_query(query_id: u64) -> NeutronResult contracts. You can check out CosmWasm [docs](https://docs.cosmwasm.com/docs) > and [blog posts](https://medium.com/cosmwasm/writing-a-cosmwasm-contract-8fb946c3a516) for entry-level tutorials. -> **Note:** before running any query creation transaction you need to top up your contract address. See [Interchain Queries Overview](../neutron/modules/interchain-queries/overview.md), "Query creation deposit" section. +> **Note:** before running any query creation transaction you need to top up your contract address. See [Interchain Queries Overview](/3.0/neutron/modules/interchain-queries/overview), "Query creation deposit" section. ## The complete example @@ -204,7 +204,7 @@ fn write_balance_query_id_to_reply_id(deps: DepsMut, reply: Reply) -> StdResult< } ``` -> **Note:** the ICQ module's `RegisterInterchainQueryMsg` message [returns](/neutron/modules/interchain-queries/messages#register-interchain-query) +> **Note:** the ICQ module's `RegisterInterchainQueryMsg` message [returns](/3.0/neutron/modules/interchain-queries/messages#register-interchain-query) > an identifier of newly registered Interchain Query in response. So in a real world scenario you should implement a `reply` handler in your contract > to catch the identifier after the registration, so you'll be able to work with the registered query later. @@ -575,7 +575,7 @@ pub fn remove_interchain_query(query_id: u64) -> NeutronResult contracts. You can check out CosmWasm [docs](https://docs.cosmwasm.com/docs) > and [blog posts](https://medium.com/cosmwasm/writing-a-cosmwasm-contract-8fb946c3a516) for entry-level tutorials. -> **Note:** before running any query creation transaction you need to top up your contract address. See [Interchain Queries Overview](../neutron/modules/interchain-queries/overview.md), "Query creation deposit" section. +> **Note:** before running any query creation transaction you need to top up your contract address. See [Interchain Queries Overview](/4.0/neutron/modules/interchain-queries/overview), "Query creation deposit" section. ## The complete example @@ -204,7 +204,7 @@ fn write_balance_query_id_to_reply_id(deps: DepsMut, reply: Reply) -> StdResult< } ``` -> **Note:** the ICQ module's `RegisterInterchainQueryMsg` message [returns](/neutron/modules/interchain-queries/messages#register-interchain-query) +> **Note:** the ICQ module's `RegisterInterchainQueryMsg` message [returns](/4.0/neutron/modules/interchain-queries/messages#register-interchain-query) > an identifier of newly registered Interchain Query in response. So in a real world scenario you should implement a `reply` handler in your contract > to catch the identifier after the registration, so you'll be able to work with the registered query later. @@ -575,7 +575,7 @@ pub fn remove_interchain_query(query_id: u64) -> NeutronResult