For Protocol Developers Only: This documentation is primarily intended for protocol developers who build applications that utilize Interchain Queries (ICQs). ICQ relayers are typically run by the same teams that develop protocols using ICQs, as there is no clear economic incentive for third parties to process expensive query operations for other protocols. General network operators should focus on IBC relaying instead.
Overview
Interchain Queries 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.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 - Maintaining the list of queries to be executed
- Queries execution - Performing queries on remote chains
- Results submission - Submitting results back to Neutron
Queries Gathering
All registered Interchain Queries and their parameters are stored in the eponymous module and available by its query interface. 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 theRELAYER_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
andquery_removed
events and modifies the queries list and parameters correspondingly
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.
Queries Execution
When the update time comes for a query, the Relayer runs the specified query on the remote chain:KV Queries
In case of a KV-query, the Relayer just reads necessary KV-keys from the remote chain’s storage with Merkle Proofs. Neutron will need these proofs to verify validity of KV-results on results submission.TX Queries
In case of a TX-query, the Relayer makes a query to the target chain’s Tendermint RPC to search transactions by message types, events and attributes which were emitted during transactions execution and were indexed by Tendermint.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, therefore they can break blockchain consensus.
The Relayer 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.
Results Submission
Relayer submits a query result as the following depending on the Relayer’s configuration:- KV queries with
RELAYER_ALLOW_KV_CALLBACKS=false
: Simply sending it to the Neutron’s Interchain Queries module which handles it by storing the result in the blockchain state - KV queries with
RELAYER_ALLOW_KV_CALLBACKS=true
: Sending it to the Neutron’s Interchain Queries module which handles it by storing the result in the blockchain state and passing the result to the owner smart contract - TX queries: Passing it to the smart contract that has registered the query
This means that it’s the Relayer who pays gas for these actions. Note that KV queries submission are straightforward and therefore cheap whereas TX ones and KV callbacks also include smart contract call and their cost may vary significantly.
Technical Details
Queries Submission
The KV queries are submitted in a fire-and-forget way, i.e. they are submitted once perupdate_period
span and never retried forcibly (e.g. on a submission error). The TX queries are a bit more tricky: since they are not stored in the Neutron chain and simply passed to smart contracts, it’s needed that each tx is passed and handled by the smart contract only once.
The Relayer uses the BroadcastTxSync
messages broadcast type to maintain balance between performance and submission control, but this means that the submission result is not waited for.
Error Handling
As a default when the Relayer submits a TX query result to the Neutron chain and an error occurs in the smart contract during the sudo call, the Relayer will ignore this error and not retry the submission. For all other errors, the Relayer will exit with an error.This behaviour is caused by the fact that the Relayer is not aware of the smart contract’s logic and therefore can’t know whether the error is recoverable or not. Also, the Relayer should treat all other errors (network/balance/wallet) as fatal, exit and let itself be restarted by the admin/system.
It is strongly recommended to run the Relayer as a daemon to allow easy restart.
RELAYER_IGNORE_ERRORS_REGEX
.
Beacons in TX Queries
Transactions for a TX query are retrieved from a target chain in ascending order. Since the TX query results aren’t submitted to the Neutron chain storage (they are processed by smart contracts via Sudo calls right away) there is no way to get the last processed height from the Neutron for a TX query. In order to keep a TX query progress in terms of already processed heights, the Relayer saves progress for each TX query in its own storage. TheRELAYER_INITIAL_TX_SEARCH_OFFSET
config parameter is tightly coupled with this part of documentation.
Configuration
This section contains description for all the possible config values that the Relayer supports.Neutron Chain Node Settings
RELAYER_NEUTRON_CHAIN_RPC_ADDR
— RPC address of a Neutron node to interact with (e.g. get events and to submit results)RELAYER_NEUTRON_CHAIN_REST_ADDR
— REST address of a Neutron node to interact with (e.g. get registered queries list)RELAYER_NEUTRON_CHAIN_HOME_DIR
— path to keys directoryRELAYER_NEUTRON_CHAIN_SIGN_KEY_NAME
— name of the key pair to be used by the RelayerRELAYER_NEUTRON_CHAIN_TIMEOUT
— timeout for Neutron RPC and REST callsRELAYER_NEUTRON_CHAIN_GAS_PRICES
— the price for a unit of gas used by the RelayerRELAYER_NEUTRON_CHAIN_GAS_LIMIT
— the maximum price to be paid for a single submissionRELAYER_NEUTRON_CHAIN_GAS_ADJUSTMENT
— gas multiplier used in order to avoid underestimatingRELAYER_NEUTRON_CHAIN_CONNECTION_ID
— Neutron chain connection ID; Relayer will only relay events for this connectionRELAYER_NEUTRON_CHAIN_DEBUG
— flag to run neutron chain provider in debug modeRELAYER_NEUTRON_CHAIN_KEYRING_BACKEND
— keyring backend typeRELAYER_NEUTRON_CHAIN_OUTPUT_FORMAT
— Neutron chain provider output formatRELAYER_NEUTRON_CHAIN_SIGN_MODE_STR
— signing mode (e.g. direct)
Target Chain Node Settings
RELAYER_TARGET_CHAIN_RPC_ADDR
— RPC address of a target chain node to interact with (e.g. send queries)RELAYER_TARGET_CHAIN_ACCOUNT_PREFIX
— target chain account prefixRELAYER_TARGET_CHAIN_VALIDATOR_ACCOUNT_PREFIX
— target chain validator account prefixRELAYER_TARGET_CHAIN_TIMEOUT
— timeout for target chain RPC callsRELAYER_TARGET_CHAIN_DEBUG
— flag to run neutron chain provider in debug modeRELAYER_TARGET_CHAIN_OUTPUT_FORMAT
— target chain provider output format
Relayer Application Settings
RELAYER_REGISTRY_ADDRESSES
— a list of comma-separated smart-contract addresses (registered query owners) for which the Relayer processes interchain queries. If empty, literally all registered queries are processed which is usable if you are up to deploy a public RelayerRELAYER_REGISTRY_QUERY_IDS
— a list of comma-separated query IDs which complements toRELAYER_REGISTRY_ADDRESSES
to further filter out interchain queries being processed. If empty, no queries will be filtered outRELAYER_ALLOW_TX_QUERIES
— if true, Relayer will process tx queries (if false, Relayer will ignore them). A true value here is mostly usable for a private Relayer because TX queries submission is quite expensiveRELAYER_ALLOW_KV_CALLBACKS
— if true, will pass proofs as sudo callbacks to contracts. A true value here is mostly usable for a private Relayer because KV query callbacks execution is quite expensive. If false, results will simply be submitted to Neutron and become available for smart contracts retrievalRELAYER_MIN_KV_UPDATE_PERIOD
— minimal period of queries execution and submission. This value is usable for a public Relayer as a rate limiter because it roughly overrides the queriesupdate_period
and force queries execution not more often than N blocksRELAYER_STORAGE_PATH
— path to leveldb storage, will be created on the given path if it doesn’t exist. It is required ifRELAYER_ALLOW_TX_QUERIES
is trueRELAYER_QUERIES_TASK_QUEUE_CAPACITY
— capacity of the channel that is used to send messages from subscriber to Relayer. Better set to a higher value to avoid problems with Tendermint websocket subscriptionsRELAYER_CHECK_SUBMITTED_TX_STATUS_DELAY
— delay in seconds between TX query submission and the result handling checkingRELAYER_INITIAL_TX_SEARCH_OFFSET
— Only for transaction queries. If set to non zero and no prior search height exists, it will initially set search height to (last_height - X). One example of usage of it will be if you have lots of old tx’s on first start you don’t need. Keep in mind that it will affect each newly created transaction queryRELAYER_WEBSERVER_PORT
— listener address for webserver json api you can query and prometheus metricsRELAYER_IGNORE_ERRORS_REGEX
— regular expression to match errors that should be ignored. If the error matches the regex, the Relayer will ignore it and will not retry the submission. For any other errors, the Relayer will exit with an error
Logger Configuration
The Relayer uses a little bit modified version of Uber’s zap.Logger. This modification allows logger configuration via env parameters. See the logger configuration guide readme for more information.Prerequisites
Before running the Relayer application for production purposes, you need to create a wallet for the Relayer, and set up the configuration. Also you need to have nodes that will serve for the Relayer’s needs.Setting up Relayer Wallet
The keyring folder for Relayer’s usage is configured by theRELAYER_NEUTRON_CHAIN_HOME_DIR
variable. The easiest way is to run neutrond keys
from the cloned neutron repository and get the default value from the --keyring-dir
flag:
- Use
relayer
as theRELAYER_NEUTRON_CHAIN_SIGN_KEY_NAME
- Use
test
as theRELAYER_NEUTRON_CHAIN_KEYRING_BACKEND
- Pass the keyring directory as a volume to the Relayer’s docker container using the keyring path in the container as the
RELAYER_NEUTRON_CHAIN_HOME_DIR
Getting Server Nodes
The Relayer requires a REST and an RPC nodes for Neutron, and an RPC node for the target chain. Option 1: Run Your Own Nodes- For Neutron nodes refer to the Build and run section of Neutron docs
- For the target chain refer to the respective documentation section of the target chain
Running the Relayer
- Make sure you’ve finished the Configuration part
-
Build Relayer’s docker image from the Relayer’s folder:
-
Run Relayer in a docker container way:
-p 9999:9999
exposes the port that allows access to the webserver json api and Relayer’s metrics powered using Prometheus. The container’s port will be the same as theRELAYER_LISTEN_ADDR
value that is 9999 by default. Use another value if you are up to use a different port- Add keyring passing to the volumes list. For example, assign
RELAYER_NEUTRON_CHAIN_HOME_DIR=/keyring
and run the app as:
Webserver API
Relayer serves it’s own JSON API and provides commands for querying info about it. It listens on port that is set inRELAYER_LISTEN_ADDR
env.
Commands:
Print available queries:
Shutting the Relayer Down
During the execution the Neutron ICQ Relayer receives events from Neutron, reads remote chain’s state, and modifies its own state and the Neutron’ one. In order to reach a reliable and consistent flow the Relayer is designed the way it finishes initialised interactions with its local storage on receivedSIGINT
and SIGTERM
. It usually takes a fraction of a second.