ics | title | stage | category | requires | kind | author | created | modified |
---|---|---|---|---|---|---|---|---|
TBA |
Interchain Query |
draft |
IBC/APP |
25, 26, 27 |
instantiation |
Joe Schnetzler <schnetzlerjoe@gmail.com> |
2022-01-06 |
2022-02-12 |
This documents aims to document the structure, plan and implementation of the Interchain Queries Module allowing for cross-chain querying of state from IBC enabled chains.
Interchain Accounts (ICS-27) brings one of the most important features IBC offers, cross chain transactions (on-chain). Limited in this functionality is the querying of state from one chain, on another chain. Adding interchain querying via the Interchain Query module, gives unlimited flexibility to chains to build IBC enabled protocols around Interchain Accounts and beyond.
Querying Chain
: The chain that is interested in getting data from another chain (Queried Chain). The Querying Chain is the chain that implements the Interchain Query Module into their chain.Queried Chain
: The chain that's state is being queried via the Querying Chain. The Queried Chain gets queried via a relayer utilizing its RPC client which is then submitted back to the Querying Chain.Key
: A Key in the querying chain module is a user specified query identifier for a query so a user can identify various query types. For example, if you were to query stakers/delegators on Gaia (Cosmos Hub) and store it in state. You can set thekey
asstakers
and thus query via the key later on as needed.
Note:
that a query in state can only have one unique key-id representation. For example, if a query with key = stakers & id = 0 is already in state, the query will fail if you try to add another query with key = stakers & id = 0. A query with key = stakers & id = 1 will succeed however (as long as that is not in state already).
-
Permissionless: A Querying Chain can query a chain and implement cross-chain querying without any approval from a third party or chain governance.
-
Minimal Querying Chain Work: A Queried Chain has to do no implementation work or add any module to enable cross chain querying. By utilizing an RPC client on a relayer, this is possible.
-
Modular: Adding cross-chain querying should be as easy as implementing a module in your chain.
-
Control Queried Data: The Querying Chain should have ultimate control on how to handle queried data. Like querying for a certain query form/type.
-
Incentivization: In order to incentivize relayers for participating in interchain queries, a bounty is paid.
The Querying Chain starts with the implementation of the Interchain Query Module by adding the module into their chain.
The general flow for interchain queries starts with a Cross Chain Query Request from the Querying Chain which is listened to by relayers. Upon recognition of a cross chain query, relayers utilize a ABCI Query Request to query data from the Queried Chain. Upon success, the relayer submits a MsgSubmitQueryResult
to the Querying chain with the success flag as 1.
On failure of a query, relayers submit MsgSubmitQueryResult
with the success
flag as 0 to the Querying chain. Alternatively on timeout based on the height of the querying chain, the querying chain will submit SubmitQueryTimeoutResult
with the timeout height specified.
A CrossChainABCIQueryRequest data type is used to specify the query. Included in this is the Path
which is the path field of the query i.e: /custom/auth/account. Key
is the data key to name the query i.e: pools
or stakers
. Id
is the id of the query with each key to id being unique i.e: stakers-0 or stakers-1275 (this example follows key-id format). TimeoutHeight
specifies the timeout height on the querying chain to timeout the query. Bounty
is a bounty that is given to the relayer for participating in the query. ClientId
is used to identify the chain of interest.
type CrossChainABCIQueryRequest struct {
Path string
Key string
Id string
TimeoutHeight uint64
Bounty sdk.Coin
ClientId string
}
type QueryResult struct {
Data []byte
Key string
Id string
Height uint64
ClientId string
Success bool
Proof ProofOps
}
type QueryTimeoutResult struct {
Key string
Id string
TimeoutHeight string
ClientId string
Proof ProofOps
}
func CrossChainABCIQueryRequest(
QueryRequest CrossChainABCIQueryRequest
) {
//Keeper to initiate interchain query request. Can be imported into any module and called as needed.
}
At the beginning of each block, the querying module checks for pending queries and if the timeout on the querying chain is hit, a timeout result is submitted to state on-chain.
func SubmitQueryTimeoutResult(
QueryTimeout QueryTimeoutResult
) {
//Keeper to submit a query timeout result. Applied at the beggining of each block and timedout when the querying chain hits timeout height.
}
The querying chain has messages that the relayer can submit depending on a success/failed query.
func MsgSubmitQueryResult(
) *cobra.Command {
//Msg to submit a query result
}
The querying chain will have messages to get a list of interchain queries as well as get a query by type and id.
func MsgGetQueries(
) *cobra.Command {
//Msg to get a list of queries
}
// Add -- max-height flag to allow for querying the highest height for a query (most recent)?
func MsgGetQuery(
) *cobra.Command {
//Msg to get a query via id and key (i.e: id: 1 and key: OsmosisPool)
}
The querying chain will emit an EmitQueryEvent
which will signal the relayer to go perform an interchain query and submit the results on-chain.
func EmitQueryEvent(ctx sdk.Context, timeoutHeight exported.Height) {
//Event to trigger query event on relayer
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
)
})
}