-
Notifications
You must be signed in to change notification settings - Fork 590
Connection handshake
This document attempts to explore IBC's connection handshake in detail. The goals of this tutorial are:
- Explore the code of hermes and ibc-go that makes connection handshake possible.
- Understand the data passed in the connection handshake messages and how/where it is obtained.
Some other useful resources:
- Transport, Authentication, and Ordering Layer - Connections in Cosmos Developer Portal.
- Hermes ADR 002.
- Setup
- Assumptions
- ICS-03 store paths
- Connection handshake
- Two chains (
chain1
andchain2
) running ib-go'ssimd
binary. -
chain1
's RPC endpoint runs onhttp://localhost:27000
and REST API runs onhttp://localhost:27001
. -
chain2
's RPC endpoint runs onhttp://localhost:27010
and REST API runs onhttp://localhost:27011
. - hermes relayer.
This document is updated for ibc-go v4.0.0 and hermes v1.0.0.
This tutorial assumes that both chains have already gone through the creation of a light client for the counterparty chain. In this tutorial the client ID of the light client on both chains is 07-tendermint-0
.
Connection-related information is stored in the IBC store:
key | value |
---|---|
[]byte("nextConnectionSequence") |
Next available connection sequence |
[]byte("connections/" + {connectionID} ) |
Connection end |
[]byte("clients/" + {clientID} + "/connections" ) |
Set of connection IDs associated with a particular client ID |
Before creating a connection between chain1
and chain2
we need to create the light client on each chain tracking the counterparty's consensus state (see hermes documentation for more details):
> hermes --config config.toml create client \
--host-chain chain2 \
--reference-chain chain1
SUCCESS CreateClient(
CreateClient(
Attributes {
client_id: ClientId(
"07-tendermint-0",
),
client_type: Tendermint,
consensus_height: Height {
revision: 0,
height: 120,
},
},
),
)
> hermes --config config.toml create client \
--host-chain chain1 \
--reference-chain chain2
SUCCESS CreateClient(
CreateClient(
Attributes {
client_id: ClientId(
"07-tendermint-0",
),
client_type: Tendermint,
consensus_height: Height {
revision: 0,
height: 58,
},
},
),
)
It's possible to trigger a connection handshake between chain1
and chain2
with just executing:
> hermes --config config.toml create connection \
--a-chain chain1 \
--a-client 07-tendermint-0 \
--b-client 07-tendermint-0
But in this guide we will execute each step of the handshake independently.
All the connection handshake logic in hermes happens in the handshake
function.
At a high level the process looks like the following:
In the first step of the connection handshake the relayer submits MsgConnectionOpenInit
on chain1
(see the message proto definition).
In hermes the MsgConnectionOpenInit
is constructed and sent in function build_conn_init_and_send
. More specifically, the message is constructed in build_conn_init.
The parameters needed to construct MsgConnectionOpenInit
are:
It's the client ID of the light client on chain1
(07-tendermint-0
in this tutorial).
It is constructed here:
-
client_id
is the client ID of the light client onchain2
(07-tendermint-0
in this tutorial). -
connection_id
is empty, sincechain2
does not have a connection end with a connection ID yet. -
prefix
is assigned at the moment the value read from the config file of hermes. In the future this value could be queried fromchain2
. This prefix is the store prefix used by the on-chain IBC module for Cosmos-SDK chains and at the moment isibc
.
It is assigned the default value. In the future this value could also be queried from chain2
. This default value matches ibc-go's DefaultIBCVersion
("identifier": "1", "features": ["ORDER_ORDERED", "ORDER_UNORDERED"]
).
It is assigned the value entered in the command line parameter of the create connection
command of hermes, if any; otherwise it takes the default value 0. The delay period is the time that must pass before a consensus state can be used for packet verification.
It is the address of the relayer that submits the message.
Using hermes to submit this message on chain1
we execute the following command:
> hermes --config config.toml tx conn-init \
--dst-chain chain1 \
--src-chain chain2 \
--dst-client 07-tendermint-0 \
--src-client 07-tendermint-0
From the hermes log we can retrieve the hash of the transaction that executed MsgConnectionOpenInit
:
2022-09-24T19:23:18.561753Z DEBUG ThreadId(13) send_tx_commit{id=ConnectionOpenInit}:send_tx_with_account_sequence_retry{id=chain1}: broadcast_tx_sync: Response { code: Ok, data: Data([]), log: Log("[]"), hash: transaction::Hash(7B2B567446610826C7F523632E03FB7DD919C82A41F21C6886DD369B90CBCD1B) }
And we can use this hash to get the transaction information using Tendermint's /tx
RPC endpoint:
http://localhost:27000/tx?hash=0x7B2B567446610826C7F523632E03FB7DD919C82A41F21C6886DD369B90CBCD1B
See sample JSON result.
The transaction was successfully executed and included in the block at height 142. The value in the field result.tx
is a base64-encoded string of the bytes of the messages that were executed as part of the transaction. This transaction contains only one message (MsgConnectionOpenInit
) and if we decode these bytes we can retrieve back the message data that the relayer submitted:
> simd tx decode CusBCrABCi0vaWJjLmNvcmUuY29ubmVjdGlvbi52MS5Nc2dDb25uZWN0aW9uT3BlbkluaXQSfwoPMDctdGVuZGVybWludC0wEhgKDzA3LXRlbmRlcm1pbnQtMBoFCgNpYmMaIwoBMRINT1JERVJfT1JERVJFRBIPT1JERVJfVU5PUkRFUkVEKi1jb3Ntb3MxOWVuZXB6M3B3eGQ1ZGd1Y3EycXAycjhsNXlubTkwZ3B0ZjUybnYSNmhlcm1lcyAxLjAuMCtlZDRkZDhjIChodHRwczovL2hlcm1lcy5pbmZvcm1hbC5zeXN0ZW1zKRJlClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEC4bLJKTkOe1oQftx4VX/S1yuAvSno3Z7WiJKEGqqQuAwSBAoCCAEYARIRCgsKBXN0YWtlEgI3NxDs1wQaQNKA9LVye1hnc09Vsga8fqjaXpGciuz/SrAKOvF11dCFXFqb5ogZREQOMx83FlfLVxkI/5VLVV8D3Mxyk3FFlSw=
See sample JSON result.
In particular the MsgConnectionOpenInit
looks like this:
{
"@type":"/ibc.core.connection.v1.MsgConnectionOpenInit",
"client_id":"07-tendermint-0",
"counterparty":{
"client_id":"07-tendermint-0",
"connection_id":"",
"prefix":{
"key_prefix":"aWJj"
}
},
"version":{
"identifier":"1",
"features":[
"ORDER_ORDERED",
"ORDER_UNORDERED"
]
},
"delay_period":"0",
"signer":"cosmos19enepz3pwxd5dgucq2qp2r8l5ynm90gptf52nv"
}
The key prefix is the string ibc
in base64 format. The counterparty
here is chain2
, which does not have a connection ID yet.
After the message reaches the message server the execution continues in ConnectionKeeper
's ConnOpenInit
function, to which the message fields are passed in. The steps in ConnOpenInit
consist of:
-
types.GetCompatibleVersions
returns the latest supported version of IBC used in connection version negotiation. These versions will be used in the creation of the connection end if the message does not provide any or the one provided is not supported.types.IsSupportedVersion
returnstrue
if the proposed version has a matching version in the list of compatible versions. - A new connection identifier is generated by retrieving the next connection sequence from the store. In this tutorial, the generated connection ID is
connection-0
. - A new
ConnectionEnd
is instantiated with the information from the message and with the stateSTATE_INIT
. The connection end is stored under the keyconnections/connection-0
. The connection ID is also added to the set of connections associated with the client (which is stored under keyclients/07-tendermint-0/connections
in this tutorial). - An event is emitted signalling that the connection open init has finished successfully. From the event information (which can also be found in the field
result.tx_result.log
of the transaction that includedMsgConnectionOpenInit
) it is possible to figure out what connection ID this new connection end has:
[
{
"events":[
{
"type":"connection_open_init",
"attributes":[
{
"key":"connection_id",
"value":"connection-0"
},
{
"key":"client_id",
"value":"07-tendermint-0"
},
{
"key":"counterparty_client_id",
"value":"07-tendermint-0"
},
{
"key":"counterparty_connection_id"
}
]
},
{
"type":"message",
"attributes":[
{
"key":"action",
"value":"/ibc.core.connection.v1.MsgConnectionOpenInit"
},
{
"key":"module",
"value":"ibc_connection"
}
]
}
]
}
]
After MsgConnectionOpenInit
successfully executes, there is a connection end stored on chain1
. We can use ibc-go's REST interface to check the existence of the connection end with connection ID connection-0
by simply entering http://localhost:27001/ibc/core/connection/v1/connections/connection-0
and check that the state of the connection is STATE_INIT
:
{
"connection": {
"client_id": "07-tendermint-0",
"versions": [
{
"identifier": "1",
"features": [
"ORDER_ORDERED",
"ORDER_UNORDERED"
]
}
],
"state": "STATE_INIT",
"counterparty": {
"client_id": "07-tendermint-0",
"connection_id": "",
"prefix": {
"key_prefix": "aWJj"
}
},
"delay_period": "0"
},
"proof": null,
"proof_height": {
"revision_number": "0",
"revision_height": "151"
}
}
After MsgConnectionOpenInit
succeeds on chain1
, then the relayer submits MsgConnectionOpenTry
on chain2
(see the message proto definition).
In hermes MsgConnectionOpenTry
is constructed and sent in function build_conn_try_and_send
. More specifically, the message is constructed in build_conn_try.
To build MsgConnectionOpenInit
hermes queries chain1
at a certain height to get proofs for the following things:
- A proof that
chain1
has stored a connection end with connection IDconnection-0
and stateSTATE_INIT
. - A proof that
chain1
has stored for the light client with client ID07-tendermint-0
the client state forchain2
. - A proof that
chain1
has stored for the light client with client ID07-tendermint-0
the consensus state forchain2
.
In this tutorial the height at which hermes is going to query chain1
for these proofs is 174 (we know this because we will see that hermes
has submitted together with MsgConnectionOpenTry
a MsgUpdateClient
that updates the client state of chain1
on chain2
to the height 175, this is because the hash after applying all transactions in block 174 is included in block at height 175). The query height is obtained here by querying Tendermint's /status
RPC endpoint and
reading the values for result.node_info.network
(which is the revision number, chain1
) and result.sync_info.latest_block_height
(which is the revision height, 174).
The parameters needed to construct MsgConnectionOpenTry
are:
it is the client ID of the light client on chain2
(07-tendermint-0
in this tutorial).
It is the connection ID for the connection end on chain2
in case there exists a connection already in state STATE_INIT
. The way hermes figures this out is by sending a query request to ibc-go's gRPC query endpoint for a single connection. This query returns the connection end on chain1
, which includes the counterparty
information with its respective connection_id
(for chain2
). In this tutorial the previous_connection_id
is empty, since there is no connection on chain2
in state STATE_INIT
and therefore there is no connection end on chain2
.
Note: This field is now unused. Crossing hellos are no longer supported in core IBC.
It is the client state for chain2
that chain1
has stored. It is retrieved here by performing an ABCI RPC query to the ibc
store of chain1
. The query uses the query height (174 in this tutorial). We will see a sample of the client state data in the proof_client
item a bit further down.
It is constructed here:
-
client_id
is the client ID of the light client onchain1
(07-tendermint-0
in this tutorial). -
connection_id
is the connection ID for the connection end onchain1
, which isconnection-0
in this tutorial. -
prefix
is determined here at the moment in a similar way as it was done forMsgConnectionOpenInit
(i.e. reading the valueibc
from the config file of hermes).
It is determined here.
It gets assigned here the versions from the connection end on chain1
, if they exist; or the default, otherwise (as done similarly for MsgConnectionOpenInit
).
It is the height for the commitment root for proving the proofs for init, client and consensus. When creating these proofs, chain1
is queried at height proof_height
- 1 (174 in this tutorial). The reason why proof_height
(175 in this tutorial) is query_height
+ 1 is because for Tendermint chains the hash after applying all transactions in block n
is included in block at height n
+ 1.
It is obtained by performing an ABCI RPC query to the ibc
store of chain1
. This proof is used to verify that chain1
has stored a connection end with state STATE_INIT
at the query height (174 in this tutorial). We can make the same ABCI RPC query on the browser by simply using Tendermint's /abci_query
REST endpoint:
path: "store/ibc/key"
data: "connections/connection-0" (in hex: 0x636f6e6e656374696f6e732f636f6e6e656374696f6e2d30)
prove: true (so that the response contains the merkle proof)
height: 174
URL: http://localhost:27000/abci_query?path="store/ibc/key"&data=0x636f6e6e656374696f6e732f636f6e6e656374696f6e2d30&prove=true&height=174
See sample JSON response.
The response to this ABCI RPC query contains both the value stored at the queried path (connections/connection-0
) and the proof needed to verify it. If we take the base64-encoded byte string from the value for the field result.response.value
and we decode it, then we can inspect the information for the connection end stored in chain1
:
{
ClientId:07-tendermint-0
Versions:[
identifier:"1"
features:"ORDER_ORDERED"
features:"ORDER_UNORDERED"
]
State:STATE_INIT
Counterparty:{
ClientId:07-tendermint-0
ConnectionId:
Prefix: {
KeyPrefix:[105 98 99]
}
}
DelayPeriod:0
}
We see that the connection end is in state STATE_INIT
. The key_prefix
is the byte representation of the string ibc
.
It is obtained by performing an ABCI RPC query to the ibc
store of chain1
. This proof is used to verify that that chain1
has stored at the query height (174 in this tutorial) for the light client with client ID 07-tendermint-0
the client state for chain2
(the same client state that is submitted as part of MsgConnectionOpenTry
in the client_state
field). We can make the same ABCI RPC query on the browser by simply using Tendermint's /abci_query
REST endpoint:
path: "store/ibc/key"
data: "clients/07-tendermint-0/clientState" (in hex: 0x636c69656e74732f30372d74656e6465726d696e742d302f636c69656e745374617465)
prove: true (so that the response contains the merkle proof)
height: 174
URL: http://localhost:27000/abci_query?path="store/ibc/key"&data=0x636c69656e74732f30372d74656e6465726d696e742d302f636c69656e745374617465&prove=true&height=174
See sample JSON response.
If we take the base64-encoded byte string from the value for the field result.response.value
and we decode it, then we can inspect the information for the client state of chain2
stored in chain1
:
chain_id:"chain2"
trust_level:<numerator:1 denominator:3 >
trusting_period:<seconds:1209600 >
unbonding_period:<seconds:1814400 >
max_clock_drift:<seconds:40 >
frozen_height:<>
latest_height:<revision_height:106 >
proof_specs:<leaf_spec:<hash:SHA256 prehash_value:SHA256 length:VAR_PROTO prefix:"\000" > inner_spec:<child_order:0 child_order:1 child_size:33 min_prefix_length:4 max_prefix_length:12 hash:SHA256 > >
proof_specs:<leaf_spec:<hash:SHA256 prehash_value:SHA256 length:VAR_PROTO prefix:"\000" > inner_spec:<child_order:0 child_order:1 child_size:32 min_prefix_length:1 max_prefix_length:1 hash:SHA256 > >
upgrade_path:"upgrade"
upgrade_path:"upgradedIBCState"
allow_update_after_expiry:true
allow_update_after_misbehaviour:true
We see that the latest_height
has a revision_height
of 106, which means that the client state for chain2
on chain1
was last updated for block height 106 of chain2
.
It is obtained by performing an ABCI RPC query to the ibc
store of chain1
. This proof is used to verify that chain1
has stored at the query height (174 in this tutorial) for the light client with client ID 07-tendermint-0
the consensus state at the consensus_height
of chain2
(106 in this tutorial). We can make the same ABCI RPC query on the browser by simply using Tendermint's /abci_query
REST endpoint. The data
query parameter follows the format clients/{client-id}/consensusStates/{epoch}-{height}
. epoch
is the revision number (0 in this tutorial) and height
is the revision height (106 in this tutorial), both retrieved from the latest_height
field of the client state stored on chain1
.
path: "store/ibc/key"
data: "clients/07-tendermint-0/consensusStates/0-106" (in hex 0x636c69656e74732f30372d74656e6465726d696e742d302f636f6e73656e7375735374617465732f302d313036)
prove: true
height: 174
URL: http://localhost:27000/abci_query?path="store/ibc/key"&data=0x636c69656e74732f30372d74656e6465726d696e742d302f636f6e73656e7375735374617465732f302d313036&prove=true&height=174
See sample JSON response.
If we take the base64-encoded byte string from the value for the field result.response.value
and we decode it, then we can inspect the information for the consensus state of chain2
(at block height 106) stored in chain1
(at block height 174):
timestamp:<seconds:1664047549 nanos:509581000 >
root:<hash:"i\307\025\205\335n\363\2758\302\201}?\006\334H?\273\033V\023|]f\022\206\217j;F\004\334" > next_validators_hash:"=\231\271\250\261I\024\366W\353\031_\256yI1\005\355!-\311I\327\317\342G\277E\177/l\322"
The timestamp
corresponds to the block height in which the consensus state was stored. root
is the app hash for block height 106 of chain2
and next_validators_hash
is the hash of the next validator set. The root
hash is the byte representation of the hex string 69c71585dd6ef3bd38c2817d3f06dc483fbb1b56137c5d6612868f6a3b4604dc
. If we query chain2
locally for the block at height 106 by entering http://localhost:27010/block?height=106
on the browser, we can check that the value in the response for the field result.block.header.app_hash
(69C71585DD6EF3BD38C2817D3F06DC483FBB1B56137C5D6612868F6A3B4604DC
) matches the root
hash from the consensus state (see the sample JSON response).
It is the latest height of chain2
which chain1
has stored in its chain2
light client. In this tutorial it is 106 (i.e. the value for latest_height
in the stored client state).
It is the address of the relayer that submits the message.
Using hermes to submit this message we execute the following command:
> hermes --config config.toml tx conn-try \
--dst-chain chain2 \
--src-chain chain1 \
--dst-client 07-tendermint-0 \
--src-client 07-tendermint-0 \
--src-connection connection-0
From the hermes log we can retrieve the hash of the transaction that executed MsgConnectionOpenTry
:
2022-09-24T19:26:07.221153Z DEBUG ThreadId(13) send_tx_commit{id=ConnectionOpenTry}:send_tx_with_account_sequence_retry{id=chain2}: broadcast_tx_sync: Response { code: Ok, data: Data([]), log: Log("[]"), hash: transaction::Hash(86F785AEA29C21BA41DE929796EF76ADAE242237B33D96C873517DA588B58E5D) }
And we can use this hash to get the transaction information using Tendermint's /tx
RPC endpoint:
http://localhost:27010/tx?hash=0x86F785AEA29C21BA41DE929796EF76ADAE242237B33D96C873517DA588B58E5D
See sample JSON result.
The transaction was successfully executed and included in the block at height 109. The value in the field result.tx
is a base64-encoded string of the bytes of the messages that were executed as part of the transaction. This transaction contains two messages: MsgUpdateClient
and MsgConnectionOpenTry
; and if we decode these bytes we can retrieve back the message data that the relayer submitted:
> simd tx decode CsYZCuYHCiMvaWJjLmNvcmUuY2xpZW50LnYxLk1zZ1VwZGF0ZUNsaWVudBK+BwoPMDctdGVuZGVybWludC0wEvsGCiYvaWJjLmxpZ2h0Y2xpZW50cy50ZW5kZXJtaW50LnYxLkhlYWRlchLQBgrJBAqNAwoCCAsSBmNoYWluMRivASIMCMmzvZkGEPj0x8sDKkgKIPfEPEACr+e/BRq4hWAtA7o4Hwp6Kh3BruJuwfjFJGGOEiQIARIg0flk4LAfjuWiOMYbMq3UdhMbXg2UBcdI3Emim+mGI0MyIK2nZyNsMd2klZpVJtaPYlcgKzOz1tM6msjjOI0Wtg5kOiDjsMRCmPwcFJr79MiZb7kkJ65B5GSbk0yklZkbeFK4VUIglGCNkT5hD2Ieag9s+6qfYt873x14YOJnVK1lLcyKWJxKIJRgjZE+YQ9iHmoPbPuqn2LfO98deGDiZ1StZS3MilicUiAEgJG8fdwoP3e/v5HXPETaWMPfipy8hnQF2Lfz2q2iL1ogrUxdlBQuYq/WohFEw3FVETLkjilva4ROP9xyYRmzp1RiIJG9/Tdp+vDIRV40iElKnQvV2UKMON+gnNpgL3Y+uTX/aiDjsMRCmPwcFJr79MiZb7kkJ65B5GSbk0yklZkbeFK4VXIU/KFlfQw/mhsgX7gFKSYhHuxsUakStgEIrwEaSAogq1I62Ejs3MfsoRhI/GcTn9AGiPssInumn69Sntu4uQgSJAgBEiAB9oeJ7wsK6G32MMFVZyidx7lddvuBmMdBiUJtfqRyKSJnCAISFPyhZX0MP5obIF+4BSkmIR7sbFGpGgsIz7O9mQYQ0NX0FyJAYUIr9AuQ2ifSbp2fAbdRwLN+6ckXuaA6QyyOaCd/W1Zg6oVift2+VCFWoJERG36eaxYY56l0zHtpWWIWYphVBRJ+CjwKFPyhZX0MP5obIF+4BSkmIR7sbFGpEiIKIFX9j498mhgZwLUC2ygm/bVgu1qS8p/LvZ1pKDSgez21GAoSPAoU/KFlfQw/mhsgX7gFKSYhHuxsUakSIgogVf2Pj3yaGBnAtQLbKCb9tWC7WpLyn8u9nWkoNKB7PbUYChgKGgIQeCJ+CjwKFPyhZX0MP5obIF+4BSkmIR7sbFGpEiIKIFX9j498mhgZwLUC2ygm/bVgu1qS8p/LvZ1pKDSgez21GAoSPAoU/KFlfQw/mhsgX7gFKSYhHuxsUakSIgogVf2Pj3yaGBnAtQLbKCb9tWC7WpLyn8u9nWkoNKB7PbUYChgKGi1jb3Ntb3MxZmxxdmxhdW0wZHJnMDg5Zmh6cXEzbjA0MnNkbHZ1a2o4ZGFlM3oKohEKLC9pYmMuY29yZS5jb25uZWN0aW9uLnYxLk1zZ0Nvbm5lY3Rpb25PcGVuVHJ5EvEQCg8wNy10ZW5kZXJtaW50LTAaqAEKKy9pYmMubGlnaHRjbGllbnRzLnRlbmRlcm1pbnQudjEuQ2xpZW50U3RhdGUSeQoGY2hhaW4yEgQIARADGgQIgOpJIgQIgN9uKgIIKDIAOgIQakIZCgkIARgBIAEqAQASDAoCAAEQIRgEIAwwAUIZCgkIARgBIAEqAQASDAoCAAEQIBgBIAEwAUoHdXBncmFkZUoQdXBncmFkZWRJQkNTdGF0ZVABWAEiJgoPMDctdGVuZGVybWludC0wEgxjb25uZWN0aW9uLTAaBQoDaWJjMiMKATESDU9SREVSX09SREVSRUQSD09SREVSX1VOT1JERVJFRDoDEK8BQrUECrECCq4CChhjb25uZWN0aW9ucy9jb25uZWN0aW9uLTASUgoPMDctdGVuZGVybWludC0wEiMKATESDU9SREVSX09SREVSRUQSD09SREVSX1VOT1JERVJFRBgBIhgKDzA3LXRlbmRlcm1pbnQtMBoFCgNpYmMaDAgBGAEgASoEAAKcAiIqCAESJgQG3AIgvX8E6AMbidgDi02Kr3BF4Mj2VpRpuUGI7xMvtl11fdkgIioIARImBgrcAiAGzIuCGnFlWI/cGfYPsgK6e8M4DQG1xpi7prYrDwDCbCAiLAgBEgUIENwCIBohIAt3eDLbhXORqk3PJTKizvcNqyQ4Woy6cHk8N7T7NuOoIioIARImChzcAiBO5xWJKpuALcyDpN7b0NIagPgH/eftgg8seyn9g2m93yAK/gEK+wEKA2liYxIgfbjt6JwOubUqy5RzRZstRfi9EUDslbURak/jB/6acokaCQgBGAEgASoBACIlCAESIQFGsg9bKg+Td5KCS6A+Qnp6eIVOcOR7NdKpisvlOJb5EyInCAESAQEaIKplBAbqDXbjndQ9LqapHj/aockI/CGnymjl5izIEVY5IicIARIBARogBbQQZKCtw0RxqRDcQ/p/9m7pUJ2wMDSouvR/Azr4aa4iJQgBEiEBk3pdrZNYsYXuTXdXl9HBwTwKYeOscKBrP/2hGRHwVF4iJwgBEgEBGiDgk7AN2ZqlJAGBS7smchl8P5DIdt5U6uyuO14q6dol+krvBArrAgroAgojY2xpZW50cy8wNy10ZW5kZXJtaW50LTAvY2xpZW50U3RhdGUSqAEKKy9pYmMubGlnaHRjbGllbnRzLnRlbmRlcm1pbnQudjEuQ2xpZW50U3RhdGUSeQoGY2hhaW4yEgQIARADGgQIgOpJIgQIgN9uKgIIKDIAOgIQakIZCgkIARgBIAEqAQASDAoCAAEQIRgEIAwwAUIZCgkIARgBIAEqAQASDAoCAAEQIBgBIAEwAUoHdXBncmFkZUoQdXBncmFkZWRJQkNTdGF0ZVABWAEaDAgBGAEgASoEAALcAiIsCAESBQIE3AIgGiEg92GpiD53hsngKtczMSrrFfgSEuQYuIVf6VecNhAR95siLAgBEgUGDNwCIBohIO5eRSHl7A5DsKZUn691xT0I4GJwfuhBxVtyt0zevrceIiwIARIFChzcAiAaISCsQG+kSL1hWhDLV9D+UBm3mErs7GfNtZWvCmgCvRVgvwr+AQr7AQoDaWJjEiB9uO3onA65tSrLlHNFmy1F+L0RQOyVtRFqT+MH/ppyiRoJCAEYASABKgEAIiUIARIhAUayD1sqD5N3koJLoD5Cenp4hU5w5Hs10qmKy+U4lvkTIicIARIBARogqmUEBuoNduOd1D0upqkeP9qhyQj8IafKaOXmLMgRVjkiJwgBEgEBGiAFtBBkoK3DRHGpENxD+n/2bulQnbAwNKi69H8DOvhpriIlCAESIQGTel2tk1ixhe5Nd1eX0cHBPAph46xwoGs//aEZEfBUXiInCAESAQEaIOCTsA3ZmqUkAYFLuyZyGXw/kMh23lTq7K47Xirp2iX6UoMFCv8CCvwCCi1jbGllbnRzLzA3LXRlbmRlcm1pbnQtMC9jb25zZW5zdXNTdGF0ZXMvMC0xMDYShgEKLi9pYmMubGlnaHRjbGllbnRzLnRlbmRlcm1pbnQudjEuQ29uc2Vuc3VzU3RhdGUSVAoMCL2zvZkGEMit/vIBEiIKIGnHFYXdbvO9OMKBfT8G3Eg/uxtWE3xdZhKGj2o7RgTcGiA9mbmosUkU9lfrGV+ueUkxBe0hLclJ18/iR79Ffy9s0hoMCAEYASABKgQAAtwCIiwIARIFAgTcAiAaISDwqm/y+VkCtd6CaW840BwW13RXUr6KOSFFTUk7td6gtSIsCAESBQQI3AIgGiEgn3k4CSY7YNV27j16HEr+iP4vBflYRyX4MgpxFRzF5dQiKggBEiYGDNwCIOHsqWVJSlq9tF94IHHRkfScWkZkDyCs7I7ShpgLvUgKICIsCAESBQoc3AIgGiEgrEBvpEi9YVoQy1fQ/lAZt5hK7OxnzbWVrwpoAr0VYL8K/gEK+wEKA2liYxIgfbjt6JwOubUqy5RzRZstRfi9EUDslbURak/jB/6acokaCQgBGAEgASoBACIlCAESIQFGsg9bKg+Td5KCS6A+Qnp6eIVOcOR7NdKpisvlOJb5EyInCAESAQEaIKplBAbqDXbjndQ9LqapHj/aockI/CGnymjl5izIEVY5IicIARIBARogBbQQZKCtw0RxqRDcQ/p/9m7pUJ2wMDSouvR/Azr4aa4iJQgBEiEBk3pdrZNYsYXuTXdXl9HBwTwKYeOscKBrP/2hGRHwVF4iJwgBEgEBGiDgk7AN2ZqlJAGBS7smchl8P5DIdt5U6uyuO14q6dol+loCEGpiLWNvc21vczFmbHF2bGF1bTBkcmcwODlmaHpxcTNuMDQyc2RsdnVrajhkYWUzehI2aGVybWVzIDEuMC4wK2VkNGRkOGMgKGh0dHBzOi8vaGVybWVzLmluZm9ybWFsLnN5c3RlbXMpEmYKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQMuzYqiJ9l05tleg1PXBRGCCJM8+5yqGVJyngaJGJ8ExRIECgIIARgBEhIKDAoFc3Rha2USAzE3NBDqxwoaQBV4BjHmTTQbUq6kSJIXgaQSvy7WUMa6Nzrtt561w9TpCtVM0/WV+Marenjtzly8zF1wwwAbuSDcL0cUp0TUddc=
See sample JSON result.
In particular the MsgUpdateClient
and MsgConnectionOpenTry
look like this:
{
"@type":"/ibc.core.client.v1.MsgUpdateClient",
"client_id":"07-tendermint-0",
"header":{
"@type":"/ibc.lightclients.tendermint.v1.Header",
"signed_header":{
"header":{
"version":{
"block":"11",
"app":"0"
},
"chain_id":"chain1",
"height":"175",
"time":"2022-09-24T19:26:01.963771Z",
"last_block_id":{
"hash":"98Q8QAKv578FGriFYC0DujgfCnoqHcGu4m7B+MUkYY4=",
"part_set_header":{
"total":1,
"hash":"0flk4LAfjuWiOMYbMq3UdhMbXg2UBcdI3Emim+mGI0M="
}
},
"last_commit_hash":"radnI2wx3aSVmlUm1o9iVyArM7PW0zqayOM4jRa2DmQ=",
"data_hash":"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"validators_hash":"lGCNkT5hD2Ieag9s+6qfYt873x14YOJnVK1lLcyKWJw=",
"next_validators_hash":"lGCNkT5hD2Ieag9s+6qfYt873x14YOJnVK1lLcyKWJw=",
"consensus_hash":"BICRvH3cKD93v7+R1zxE2ljD34qcvIZ0Bdi389qtoi8=",
"app_hash":"rUxdlBQuYq/WohFEw3FVETLkjilva4ROP9xyYRmzp1Q=",
"last_results_hash":"kb39N2n68MhFXjSISUqdC9XZQow436Cc2mAvdj65Nf8=",
"evidence_hash":"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"proposer_address":"/KFlfQw/mhsgX7gFKSYhHuxsUak="
},
"commit":{
"height":"175",
"round":0,
"block_id":{
"hash":"q1I62Ejs3MfsoRhI/GcTn9AGiPssInumn69Sntu4uQg=",
"part_set_header":{
"total":1,
"hash":"AfaHie8LCuht9jDBVWconce5XXb7gZjHQYlCbX6kcik="
}
},
"signatures":[
{
"block_id_flag":"BLOCK_ID_FLAG_COMMIT",
"validator_address":"/KFlfQw/mhsgX7gFKSYhHuxsUak=",
"timestamp":"2022-09-24T19:26:07.050146Z",
"signature":"YUIr9AuQ2ifSbp2fAbdRwLN+6ckXuaA6QyyOaCd/W1Zg6oVift2+VCFWoJERG36eaxYY56l0zHtpWWIWYphVBQ=="
}
]
}
},
"validator_set":{
"validators":[
{
"address":"/KFlfQw/mhsgX7gFKSYhHuxsUak=",
"pub_key":{
"ed25519":"Vf2Pj3yaGBnAtQLbKCb9tWC7WpLyn8u9nWkoNKB7PbU="
},
"voting_power":"10",
"proposer_priority":"0"
}
],
"proposer":{
"address":"/KFlfQw/mhsgX7gFKSYhHuxsUak=",
"pub_key":{
"ed25519":"Vf2Pj3yaGBnAtQLbKCb9tWC7WpLyn8u9nWkoNKB7PbU="
},
"voting_power":"10",
"proposer_priority":"0"
},
"total_voting_power":"10"
},
"trusted_height":{
"revision_number":"0",
"revision_height":"120"
},
"trusted_validators":{
"validators":[
{
"address":"/KFlfQw/mhsgX7gFKSYhHuxsUak=",
"pub_key":{
"ed25519":"Vf2Pj3yaGBnAtQLbKCb9tWC7WpLyn8u9nWkoNKB7PbU="
},
"voting_power":"10",
"proposer_priority":"0"
}
],
"proposer":{
"address":"/KFlfQw/mhsgX7gFKSYhHuxsUak=",
"pub_key":{
"ed25519":"Vf2Pj3yaGBnAtQLbKCb9tWC7WpLyn8u9nWkoNKB7PbU="
},
"voting_power":"10",
"proposer_priority":"0"
},
"total_voting_power":"10"
}
},
"signer":"cosmos1flqvlaum0drg089fhzqq3n042sdlvukj8dae3z"
},
{
"@type":"/ibc.core.connection.v1.MsgConnectionOpenTry",
"client_id":"07-tendermint-0",
"previous_connection_id":"",
"client_state":{
"@type":"/ibc.lightclients.tendermint.v1.ClientState",
"chain_id":"chain2",
"trust_level":{
"numerator":"1",
"denominator":"3"
},
"trusting_period":"1209600s",
"unbonding_period":"1814400s",
"max_clock_drift":"40s",
"frozen_height":{
"revision_number":"0",
"revision_height":"0"
},
"latest_height":{
"revision_number":"0",
"revision_height":"106"
},
"proof_specs":[
{
"leaf_spec":{
"hash":"SHA256",
"prehash_key":"NO_HASH",
"prehash_value":"SHA256",
"length":"VAR_PROTO",
"prefix":"AA=="
},
"inner_spec":{
"child_order":[
0,
1
],
"child_size":33,
"min_prefix_length":4,
"max_prefix_length":12,
"empty_child":null,
"hash":"SHA256"
},
"max_depth":0,
"min_depth":0
},
{
"leaf_spec":{
"hash":"SHA256",
"prehash_key":"NO_HASH",
"prehash_value":"SHA256",
"length":"VAR_PROTO",
"prefix":"AA=="
},
"inner_spec":{
"child_order":[
0,
1
],
"child_size":32,
"min_prefix_length":1,
"max_prefix_length":1,
"empty_child":null,
"hash":"SHA256"
},
"max_depth":0,
"min_depth":0
}
],
"upgrade_path":[
"upgrade",
"upgradedIBCState"
],
"allow_update_after_expiry":true,
"allow_update_after_misbehaviour":true
},
"counterparty":{
"client_id":"07-tendermint-0",
"connection_id":"connection-0",
"prefix":{
"key_prefix":"aWJj"
}
},
"delay_period":"0",
"counterparty_versions":[
{
"identifier":"1",
"features":[
"ORDER_ORDERED",
"ORDER_UNORDERED"
]
}
],
"proof_height":{
"revision_number":"0",
"revision_height":"175"
},
"proof_init":"CrECCq4CChhjb25uZWN0aW9ucy9jb25uZWN0aW9uLTASUgoPMDctdGVuZGVybWludC0wEiMKATESDU9SREVSX09SREVSRUQSD09SREVSX1VOT1JERVJFRBgBIhgKDzA3LXRlbmRlcm1pbnQtMBoFCgNpYmMaDAgBGAEgASoEAAKcAiIqCAESJgQG3AIgvX8E6AMbidgDi02Kr3BF4Mj2VpRpuUGI7xMvtl11fdkgIioIARImBgrcAiAGzIuCGnFlWI/cGfYPsgK6e8M4DQG1xpi7prYrDwDCbCAiLAgBEgUIENwCIBohIAt3eDLbhXORqk3PJTKizvcNqyQ4Woy6cHk8N7T7NuOoIioIARImChzcAiBO5xWJKpuALcyDpN7b0NIagPgH/eftgg8seyn9g2m93yAK/gEK+wEKA2liYxIgfbjt6JwOubUqy5RzRZstRfi9EUDslbURak/jB/6acokaCQgBGAEgASoBACIlCAESIQFGsg9bKg+Td5KCS6A+Qnp6eIVOcOR7NdKpisvlOJb5EyInCAESAQEaIKplBAbqDXbjndQ9LqapHj/aockI/CGnymjl5izIEVY5IicIARIBARogBbQQZKCtw0RxqRDcQ/p/9m7pUJ2wMDSouvR/Azr4aa4iJQgBEiEBk3pdrZNYsYXuTXdXl9HBwTwKYeOscKBrP/2hGRHwVF4iJwgBEgEBGiDgk7AN2ZqlJAGBS7smchl8P5DIdt5U6uyuO14q6dol+g==",
"proof_client":"CusCCugCCiNjbGllbnRzLzA3LXRlbmRlcm1pbnQtMC9jbGllbnRTdGF0ZRKoAQorL2liYy5saWdodGNsaWVudHMudGVuZGVybWludC52MS5DbGllbnRTdGF0ZRJ5CgZjaGFpbjISBAgBEAMaBAiA6kkiBAiA324qAggoMgA6AhBqQhkKCQgBGAEgASoBABIMCgIAARAhGAQgDDABQhkKCQgBGAEgASoBABIMCgIAARAgGAEgATABSgd1cGdyYWRlShB1cGdyYWRlZElCQ1N0YXRlUAFYARoMCAEYASABKgQAAtwCIiwIARIFAgTcAiAaISD3YamIPneGyeAq1zMxKusV+BIS5Bi4hV/pV5w2EBH3myIsCAESBQYM3AIgGiEg7l5FIeXsDkOwplSfr3XFPQjgYnB+6EHFW3K3TN6+tx4iLAgBEgUKHNwCIBohIKxAb6RIvWFaEMtX0P5QGbeYSuzsZ821la8KaAK9FWC/Cv4BCvsBCgNpYmMSIH247eicDrm1KsuUc0WbLUX4vRFA7JW1EWpP4wf+mnKJGgkIARgBIAEqAQAiJQgBEiEBRrIPWyoPk3eSgkugPkJ6eniFTnDkezXSqYrL5TiW+RMiJwgBEgEBGiCqZQQG6g12453UPS6mqR4/2qHJCPwhp8po5eYsyBFWOSInCAESAQEaIAW0EGSgrcNEcakQ3EP6f/Zu6VCdsDA0qLr0fwM6+GmuIiUIARIhAZN6Xa2TWLGF7k13V5fRwcE8CmHjrHCgaz/9oRkR8FReIicIARIBARog4JOwDdmapSQBgUu7JnIZfD+QyHbeVOrsrjteKunaJfo=",
"proof_consensus":"Cv8CCvwCCi1jbGllbnRzLzA3LXRlbmRlcm1pbnQtMC9jb25zZW5zdXNTdGF0ZXMvMC0xMDYShgEKLi9pYmMubGlnaHRjbGllbnRzLnRlbmRlcm1pbnQudjEuQ29uc2Vuc3VzU3RhdGUSVAoMCL2zvZkGEMit/vIBEiIKIGnHFYXdbvO9OMKBfT8G3Eg/uxtWE3xdZhKGj2o7RgTcGiA9mbmosUkU9lfrGV+ueUkxBe0hLclJ18/iR79Ffy9s0hoMCAEYASABKgQAAtwCIiwIARIFAgTcAiAaISDwqm/y+VkCtd6CaW840BwW13RXUr6KOSFFTUk7td6gtSIsCAESBQQI3AIgGiEgn3k4CSY7YNV27j16HEr+iP4vBflYRyX4MgpxFRzF5dQiKggBEiYGDNwCIOHsqWVJSlq9tF94IHHRkfScWkZkDyCs7I7ShpgLvUgKICIsCAESBQoc3AIgGiEgrEBvpEi9YVoQy1fQ/lAZt5hK7OxnzbWVrwpoAr0VYL8K/gEK+wEKA2liYxIgfbjt6JwOubUqy5RzRZstRfi9EUDslbURak/jB/6acokaCQgBGAEgASoBACIlCAESIQFGsg9bKg+Td5KCS6A+Qnp6eIVOcOR7NdKpisvlOJb5EyInCAESAQEaIKplBAbqDXbjndQ9LqapHj/aockI/CGnymjl5izIEVY5IicIARIBARogBbQQZKCtw0RxqRDcQ/p/9m7pUJ2wMDSouvR/Azr4aa4iJQgBEiEBk3pdrZNYsYXuTXdXl9HBwTwKYeOscKBrP/2hGRHwVF4iJwgBEgEBGiDgk7AN2ZqlJAGBS7smchl8P5DIdt5U6uyuO14q6dol+g==",
"consensus_height":{
"revision_number":"0",
"revision_height":"106"
},
"signer":"cosmos1flqvlaum0drg089fhzqq3n042sdlvukj8dae3z"
}
After the message reaches the message server the execution continues in ConnectionKeeper
's ConnOpenTry
function, to which the message fields are passed in. The steps in ConnOpenTry
consist of:
- A new connection identifier is generated by retrieving the next connection sequence from the store. In this tutorial, the generated connection ID is
connection-0
. - Check to make sure that the height of
chain2
is greater than the latest height ofchain2
thatchain1
has stored in itschain2
client state. - Validate that the parameters for the client state of
chain2
stored onchain1
are correct. -
Construct the expected consensus state and connection end that
chain2
expectschain1
to have stored in its state. The expected consensus state ischain2
's consensus state atconsensusHeight
(106 in this tutorial), which is retrieved bychain2
self-inspecting its own consensus state; the expected connection end ischain1
's connection end created during theConnectionOpenInit
step. -
A connection end is created that
chain2
will store in state. -
Verify against the app hash at height
proofHeight
thatchain1
stored in state the expected connection end. This verification checks thatchain1
has stored in the pathconnections/connection-0
of theibc
store the expected connection end with stateSTATE_INIT
created during the execution ofMsgConnectionOpenInit
. It does this by computing the merkle root hash usingproofInit
and the expected connection end and checking that it matches the root hash atproofHeight
of the consensus state stored for the light client ofchain1
. -
Verify against the app hash at height
proofHeight
thatchain1
stored in state theclientState
forchain2
. This verification checks thatchain1
has stored in the pathclients/07-tendermint-0/clientState
of theibc
store theclientState
submitted inMsgConnectionOpenTry
. It does this by computing the merkle root hash usingproofClient
and theclientState
and checking that it matches the root hash atproofHeight
of the consensus state stored for the light client ofchain1
. -
Verify against the app hash at height
proofHeight
thatchain1
stored in state the consensus state forchain2
. This verification checks thatchain1
has stored in the pathclients/07-tendermint-0/consensusStates/106
of theibc
store the consensus state forchain2
atconsensus_height
(106 in this tutorial). It does this by computing the merkle root hash usingproofConsensus
and the expected consensus state and checking that it matches the root hash atproofHeight
of the consensus state stored for the light client ofchain1
. - The connection ID is also added to the set of connections associated with a client (which is stored under key
clients/07-tendermint-0/connections
in this tutorial). And the connection end is stored under the keyconnections/connection-0
. - An event is emitted signalling that the connection open try has finished successfully. From the event information (which can also be found in the field
result.tx_result.log
of the transaction that includedMsgConnectionOpenTry
) it is possible to figure out what connection ID this new connection end has:
[
{
"events":[
{
"type":"message",
"attributes":[
{
"key":"action",
"value":"/ibc.core.client.v1.MsgUpdateClient"
},
{
"key":"module",
"value":"ibc_client"
}
]
},
{
"type":"update_client",
"attributes":[
{
"key":"client_id",
"value":"07-tendermint-0"
},
{
"key":"client_type",
"value":"07-tendermint"
},
{
"key":"consensus_height",
"value":"0-175"
},
{
"key":"header",
"value":"0a262f6962632e6c69676874636c69656e74732e74656e6465726d696e742e76312e48656164657212d0060ac9040a8d030a02080b1206636861696e3118af01220c08c9b3bd990610f8f4c7cb032a480a20f7c43c4002afe7bf051ab885602d03ba381f0a7a2a1dc1aee26ec1f8c524618e122408011220d1f964e0b01f8ee5a238c61b32add476131b5e0d9405c748dc49a29be98623433220ada767236c31dda4959a5526d68f6257202b33b3d6d33a9ac8e3388d16b60e643a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855422094608d913e610f621e6a0f6cfbaa9f62df3bdf1d7860e26754ad652dcc8a589c4a2094608d913e610f621e6a0f6cfbaa9f62df3bdf1d7860e26754ad652dcc8a589c5220048091bc7ddc283f77bfbf91d73c44da58c3df8a9cbc867405d8b7f3daada22f5a20ad4c5d94142e62afd6a21144c371551132e48e296f6b844e3fdc726119b3a754622091bdfd3769faf0c8455e3488494a9d0bd5d9428c38dfa09cda602f763eb935ff6a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8557214fca1657d0c3f9a1b205fb8052926211eec6c51a912b60108af011a480a20ab523ad848ecdcc7eca11848fc67139fd00688fb2c227ba69faf529edbb8b90812240801122001f68789ef0b0ae86df630c15567289dc7b95d76fb8198c74189426d7ea47229226708021214fca1657d0c3f9a1b205fb8052926211eec6c51a91a0b08cfb3bd990610d0d5f417224061422bf40b90da27d26e9d9f01b751c0b37ee9c917b9a03a432c8e68277f5b5660ea85627eddbe542156a091111b7e9e6b1618e7a974cc7b6959621662985505127e0a3c0a14fca1657d0c3f9a1b205fb8052926211eec6c51a912220a2055fd8f8f7c9a1819c0b502db2826fdb560bb5a92f29fcbbd9d692834a07b3db5180a123c0a14fca1657d0c3f9a1b205fb8052926211eec6c51a912220a2055fd8f8f7c9a1819c0b502db2826fdb560bb5a92f29fcbbd9d692834a07b3db5180a180a1a021078227e0a3c0a14fca1657d0c3f9a1b205fb8052926211eec6c51a912220a2055fd8f8f7c9a1819c0b502db2826fdb560bb5a92f29fcbbd9d692834a07b3db5180a123c0a14fca1657d0c3f9a1b205fb8052926211eec6c51a912220a2055fd8f8f7c9a1819c0b502db2826fdb560bb5a92f29fcbbd9d692834a07b3db5180a180a"
}
]
}
]
},
{
"msg_index":1,
"events":[
{
"type":"connection_open_try",
"attributes":[
{
"key":"connection_id",
"value":"connection-0"
},
{
"key":"client_id",
"value":"07-tendermint-0"
},
{
"key":"counterparty_client_id",
"value":"07-tendermint-0"
},
{
"key":"counterparty_connection_id",
"value":"connection-0"
}
]
},
{
"type":"message",
"attributes":[
{
"key":"action",
"value":"/ibc.core.connection.v1.MsgConnectionOpenTry"
},
{
"key":"module",
"value":"ibc_connection"
}
]
}
]
}
]
After MsgConnectionOpenTry
successfully executes, there is a connection end stored on chain2
. We can use ibc-go's REST interface to check the existence of the connection end with connection ID connection-0
by simply entering http://localhost:27011/ibc/core/connection/v1/connections/connection-0
and check that the state of the connection is STATE_TRYOPEN
:
{
"connection": {
"client_id": "07-tendermint-0",
"versions": [
{
"identifier": "1",
"features": [
"ORDER_ORDERED",
"ORDER_UNORDERED"
]
}
],
"state": "STATE_TRYOPEN",
"counterparty": {
"client_id": "07-tendermint-0",
"connection_id": "connection-0",
"prefix": {
"key_prefix": "aWJj"
}
},
"delay_period": "0"
},
"proof": null,
"proof_height": {
"revision_number": "0",
"revision_height": "114"
}
}
After MsgConnectionOpenTry
succeeds on chain2
, then the relayer submits MsgConnectionOpenAck
on chain1
(see the message proto definition.
In hermes MsgConnectionOpenTry
is constructed and sent in function build_conn_ack_and_send
. More specifically, the message is constructed in build_conn_ack.
To build MsgConnectionOpenAck
hermes queries chain2
at a certain height to get proofs for the following things:
- A proof that
chain2
has stored a connection end with connection IDconnection-0
and stateSTATE_TRY
. - A proof that
chain2
has stored for the light client with client ID07-tendermint-0
the client state forchain1
. - A proof that
chain2
has stored for the light client with client ID07-tendermint-0
the consensus state forchain1
.
In this tutorial the height at which hermes is going to query chain2
for these proofs is 144 (we know this because we will see that hermes
has submitted together with MsgConnectionOpenAck
a MsgUpdateClient
that updates the client state of chain2
on chain1
to the height 145). The query height is obtained here by querying Tendermint's /status
RPC endpoint and reading the values for result.node_info.network
(which is the revision number, chain1
) and result.sync_info.latest_block_height
(which is the revision height, 144).
The parameters needed to construct MsgConnectionOpenAck
are:
It is the connection ID for the connection end on chain1
(connection-0
in this tutorial).
It is the connection ID for the connection end on chain2
.
It is the version selected by chain2
.
It is the client state for chain1
that chain2
has stored. It is retrieved here by performing an ABCI RPC query to the ibc
store of chain2
. The query uses the query height (144 in this tutorial). We will see a sample of the client state data in the proof_client
item a bit further down.
It is the height for the commitment root for proving the proofs for try, client and consensus. When creating these proofs, chain2
is queried at height proof_height
- 1 (144 in this tutorial). The reason why proof_height
(145 in this tutorial) is query_height
+ 1 is because for Tendermint chains the app hash after applying all transactions in block n
is included in block at height n
+ 1.
It is obtained by performing an ABCI RPC query to the ibc
store of chain2
. This proof is used to verify that chain2
has stored a connection end with state STATE_TRYOPEN
at the query height (144 in this tutorial). We can make the same ABCI RPC query on the browser by simply using Tendermint's /abci_query
REST endpoint:
path: "store/ibc/key"
data: "connections/connection-0" (in hex: 0x636f6e6e656374696f6e732f636f6e6e656374696f6e2d30)
prove: true (so that the response contains the merkle proof)
height: 144
URL: http://localhost:27010/abci_query?path="store/ibc/key"&data=0x636f6e6e656374696f6e732f636f6e6e656374696f6e2d30&prove=true&height=144
See sample JSON response.
The response to this ABCI RPC query contains both the value stored at the queried path (connections/connection-0
) and the proof needed to verify it. If we take the base64-encoded byte string from the value for the field result.response.value
and we decode it, then we can inspect the information for the connection end stored in chain2
:
{
ClientId:07-tendermint-0
Versions:[
identifier:"1"
features:"ORDER_ORDERED"
features:"ORDER_UNORDERED"
]
State:STATE_TRYOPEN
Counterparty:{
ClientId:07-tendermint-0
ConnectionId:connection-0
Prefix:{
KeyPrefix:[105 98 99]
}
}
DelayPeriod:0
}
We see that the connection end is in state STATE_TRYOPEN
. The key_prefix
is the byte representation of the string ibc
.
It is obtained by performing an ABCI RPC query to the ibc
store of chain1
. This proof is used to check that chain2
has stored at the query height (144 in this tutorial) for the light client with client ID 07-tendermint-0
the client state for chain1
(the same client state that is submitted in MsgConnectionOpenAck
in the client_state
field). We can make the same ABCI RPC query on the browser by simply using Tendermint's /abci_query
REST endpoint:
path: "store/ibc/key"
data: "clients/07-tendermint-0/clientState" (in hex: 0x636c69656e74732f30372d74656e6465726d696e742d302f636c69656e745374617465)
prove: true (so that the response contains the merkle proof)
height: 144
URL: http://localhost:27010/abci_query?path="store/ibc/key"&data=0x636c69656e74732f30372d74656e6465726d696e742d302f636c69656e745374617465&prove=true&height=144
See sample JSON response.
If we take the base64-encoded byte string from the value for the field result.response.value
and we decode it, then we can inspect the information for the client state of chain1
stored in chain2
:
chain_id:"chain1"
trust_level:<numerator:1 denominator:3 >
trusting_period:<seconds:1209600 >
unbonding_period:<seconds:1814400 >
max_clock_drift:<seconds:40 >
frozen_height:<>
latest_height:<revision_height:209 >
proof_specs:<leaf_spec:<hash:SHA256 prehash_value:SHA256 length:VAR_PROTO prefix:"\000" > inner_spec:<child_order:0 child_order:1 child_size:33 min_prefix_length:4 max_prefix_length:12 hash:SHA256 > >
proof_specs:<leaf_spec:<hash:SHA256 prehash_value:SHA256 length:VAR_PROTO prefix:"\000" > inner_spec:<child_order:0 child_order:1 child_size:32 min_prefix_length:1 max_prefix_length:1 hash:SHA256 > >
upgrade_path:"upgrade"
upgrade_path:"upgradedIBCState"
allow_update_after_expiry:true
allow_update_after_misbehaviour:true
We see that the latest_height
has a revision_height
of 209, which means that the client state for chain1
on chain2
was last updated for block height 209 of chain1
.
It is obtained by performing an ABCI RPC query to the ibc
store of chain2
. This proof is used to check that chain2
has stored at the query height (144 in this tutorial) for the light client with client ID 07-tendermint-0
the consensus state at the consensus_height
(209 in this tutorial) of chain1
. We can make the same ABCI RPC query on the browser by simply using Tendermint's /abci_query
REST endpoint. The data
query parameter follows the format clients/{client-id}/consensusStates/{epoch}-{height}
. epoch
is the revision number (0 in this tutorial) and height
is the revision height (209 in this tutorial), both retrieved from the latest_height
field of the client state stored on chain2
.
path: "store/ibc/key"
data: "clients/07-tendermint-0/consensusStates/0-209" (in hex 0x636c69656e74732f30372d74656e6465726d696e742d302f636f6e73656e7375735374617465732f302d323039)
prove: true
height: 174
URL: http://localhost:27010/abci_query?path="store/ibc/key"&data=0x636c69656e74732f30372d74656e6465726d696e742d302f636f6e73656e7375735374617465732f302d323039&prove=true&height=174
See sample JSON response.
If we take the base64-encoded byte string from the value for the field result.response.value
and we decode it, then we can inspect the information for the consensus state of chain1
(at block height 209) stored in chain2
(at block height 174):
timestamp:<seconds:1664047735 nanos:238479000 >
root:<hash:".\345?@\244\305\303&8\n\246c\271\351\320\t\255\007m\275\334\226\247/x\020\306\273\357\273|*" > next_validators_hash:"\224`\215\221>a\017b\036j\017l\373\252\237b\337;\337\035x`\342gT\255e-\314\212X\234"
The timestamp
corresponds to the block height in which the consensus state was stored. root
is the app hash for block height 209 of chain2
and next_validators_hash
is the hash of the next validator set. The root
hash is the byte representation of the hex string 2ee53f40a4c5c326380aa663b9e9d009ad076dbddc96a72f7810c6bbefbb7c2a
. If we query chain1
locally for block at height 209 by entering http://localhost:27000/block?height=209
on the browser, we can check that the value in the response for the field result.block.header.app_hash
(2EE53F40A4C5C326380AA663B9E9D009AD076DBDDC96A72F7810C6BBEFBB7C2A
) matches the root
hash from the consensus state (see the sample JSON response).
It is the latest height of chain1
which chain2
has stored in its chain1
light client. In this tutorial it is 209 (i.e. the value for latest_height
in the stored client state).
It is the address of the relayer that submits the message.
Using hermes to submit this message we execute the following command:
> hermes --config config.toml tx conn-ack \
--dst-chain chain1 \
--src-chain chain2 \
--dst-client 07-tendermint-0 \
--src-client 07-tendermint-0 \
--dst-connection connection-0 \
--src-connection connection-0
From the hermes log we can retrieve the hash of the transaction that executed MsgConnectionOpenAck
:
2022-09-24T19:29:13.362255Z DEBUG ThreadId(13) send_tx_commit{id=ConnectionOpenAck}:send_tx_with_account_sequence_retry{id=chain1}: broadcast_tx_sync: Response { code: Ok, data: Data([]), log: Log("[]"), hash: transaction::Hash(6127DCDF7C2FAFE234ED69A377DE0B3682E947AE2BAC7D4697C352FC74441375) }
And we can use this hash to get the transaction information using Tendermint's /tx
RPC endpoint:
http://localhost:27000/tx?hash=0x6127DCDF7C2FAFE234ED69A377DE0B3682E947AE2BAC7D4697C352FC74441375
See sample JSON result.
The transaction was successfully executed and included in the block at height 212. The value in the field result.tx
is a base64-encoded string of the bytes of the messages that were executed as part of the transaction. This transaction contains two messages: MsgUpdateClient
and MsgConnectionOpenAck
; and if we decode these bytes we can retrieve back the message data that the relayer submitted:
> simd tx decode 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
See sample JSON result.
In particular the MsgUpdateClient
and MsgConnectionOpenAck
look like this:
{
"@type":"/ibc.core.client.v1.MsgUpdateClient",
"client_id":"07-tendermint-0",
"header":{
"@type":"/ibc.lightclients.tendermint.v1.Header",
"signed_header":{
"header":{
"version":{
"block":"11",
"app":"0"
},
"chain_id":"chain2",
"height":"145",
"time":"2022-09-24T19:29:08.106934Z",
"last_block_id":{
"hash":"VVJjWIG9i8TfuE0eRB01W0Og7WH3zxwqbE2aT/yDwug=",
"part_set_header":{
"total":1,
"hash":"RX+BotISwovJ1SHkyYYl5nZqCYO8AyHgjWKsG10ZtaQ="
}
},
"last_commit_hash":"9RBJCeXS5+4T6XbKn/M/gvqJGAnMSySILsC1o0mUZ6I=",
"data_hash":"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"validators_hash":"PZm5qLFJFPZX6xlfrnlJMQXtIS3JSdfP4ke/RX8vbNI=",
"next_validators_hash":"PZm5qLFJFPZX6xlfrnlJMQXtIS3JSdfP4ke/RX8vbNI=",
"consensus_hash":"BICRvH3cKD93v7+R1zxE2ljD34qcvIZ0Bdi389qtoi8=",
"app_hash":"ck9kuZEDQFWscI1qnvW/kjFh/Wx2oyrZ+Spemc2CUUA=",
"last_results_hash":"0z6NkrMeAFNh1m1enUoHzbkaiyL/Htok0jLIAYUGrVM=",
"evidence_hash":"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"proposer_address":"FKTxW3cu3Uzgr4aGZpg6/vni9DU="
},
"commit":{
"height":"145",
"round":0,
"block_id":{
"hash":"cWELs6N8+bFvOVKV9fqY8/6s9e9ThjAGmeFv0+xWEs8=",
"part_set_header":{
"total":1,
"hash":"Urpz4RLyq54rDkMBVmUkd00ove020ikWeU+fttUbQAQ="
}
},
"signatures":[
{
"block_id_flag":"BLOCK_ID_FLAG_COMMIT",
"validator_address":"FKTxW3cu3Uzgr4aGZpg6/vni9DU=",
"timestamp":"2022-09-24T19:29:13.190280Z",
"signature":"l1/xvEBl40sHHdXsiFaNH7oI+qtXRGD1JF5cSqCfBN9CC4C8dHVyYcwCyqLwtNOuZF8Hxu+s+/egT+FsTQi/BQ=="
}
]
}
},
"validator_set":{
"validators":[
{
"address":"FKTxW3cu3Uzgr4aGZpg6/vni9DU=",
"pub_key":{
"ed25519":"bbHID9h8mDVBW5UwkVTuMqy0kRDcCTO9k/x2XgbHEfs="
},
"voting_power":"10",
"proposer_priority":"0"
}
],
"proposer":{
"address":"FKTxW3cu3Uzgr4aGZpg6/vni9DU=",
"pub_key":{
"ed25519":"bbHID9h8mDVBW5UwkVTuMqy0kRDcCTO9k/x2XgbHEfs="
},
"voting_power":"10",
"proposer_priority":"0"
},
"total_voting_power":"10"
},
"trusted_height":{
"revision_number":"0",
"revision_height":"106"
},
"trusted_validators":{
"validators":[
{
"address":"FKTxW3cu3Uzgr4aGZpg6/vni9DU=",
"pub_key":{
"ed25519":"bbHID9h8mDVBW5UwkVTuMqy0kRDcCTO9k/x2XgbHEfs="
},
"voting_power":"10",
"proposer_priority":"0"
}
],
"proposer":{
"address":"FKTxW3cu3Uzgr4aGZpg6/vni9DU=",
"pub_key":{
"ed25519":"bbHID9h8mDVBW5UwkVTuMqy0kRDcCTO9k/x2XgbHEfs="
},
"voting_power":"10",
"proposer_priority":"0"
},
"total_voting_power":"10"
}
},
"signer":"cosmos19enepz3pwxd5dgucq2qp2r8l5ynm90gptf52nv"
},
{
"@type":"/ibc.core.connection.v1.MsgConnectionOpenAck",
"connection_id":"connection-0",
"counterparty_connection_id":"connection-0",
"version":{
"identifier":"1",
"features":[
"ORDER_ORDERED",
"ORDER_UNORDERED"
]
},
"client_state":{
"@type":"/ibc.lightclients.tendermint.v1.ClientState",
"chain_id":"chain1",
"trust_level":{
"numerator":"1",
"denominator":"3"
},
"trusting_period":"1209600s",
"unbonding_period":"1814400s",
"max_clock_drift":"40s",
"frozen_height":{
"revision_number":"0",
"revision_height":"0"
},
"latest_height":{
"revision_number":"0",
"revision_height":"209"
},
"proof_specs":[
{
"leaf_spec":{
"hash":"SHA256",
"prehash_key":"NO_HASH",
"prehash_value":"SHA256",
"length":"VAR_PROTO",
"prefix":"AA=="
},
"inner_spec":{
"child_order":[
0,
1
],
"child_size":33,
"min_prefix_length":4,
"max_prefix_length":12,
"empty_child":null,
"hash":"SHA256"
},
"max_depth":0,
"min_depth":0
},
{
"leaf_spec":{
"hash":"SHA256",
"prehash_key":"NO_HASH",
"prehash_value":"SHA256",
"length":"VAR_PROTO",
"prefix":"AA=="
},
"inner_spec":{
"child_order":[
0,
1
],
"child_size":32,
"min_prefix_length":1,
"max_prefix_length":1,
"empty_child":null,
"hash":"SHA256"
},
"max_depth":0,
"min_depth":0
}
],
"upgrade_path":[
"upgrade",
"upgradedIBCState"
],
"allow_update_after_expiry":true,
"allow_update_after_misbehaviour":true
},
"proof_height":{
"revision_number":"0",
"revision_height":"145"
},
"proof_try":"CusCCugCChhjb25uZWN0aW9ucy9jb25uZWN0aW9uLTASYAoPMDctdGVuZGVybWludC0wEiMKATESDU9SREVSX09SREVSRUQSD09SREVSX1VOT1JERVJFRBgCIiYKDzA3LXRlbmRlcm1pbnQtMBIMY29ubmVjdGlvbi0wGgUKA2liYxoMCAEYASABKgQAAtoBIioIARImAgSgAiC7KbNtxKFD+r7iLI7JQWTZ6PjEYWs11j2iLqAEUJ73oyAiKggBEiYECKACIO1PFmu3WPO8pqrDfHYT+fCVBEPWY0qppagnclfCYJlYICIsCAESBQYOoAIgGiEgS6K+ecLdX6ZH5y2FrZndDEvXZ5Y5tjBk2/Ge2nYsW/0iKggBEiYIGqACIIarz5iUxw8ostqGeTAW5iGqO5wMBHeZm8NtTDlY+EwHICIqCAESJgokoAIgtXBh7ekb0PmkVQ5ZfKjFSUBVFeuGJaNDF/+4gTMh1ZMgCv4BCvsBCgNpYmMSIAAa9ewRHR5FoTlsd5YI6YecY+1Z3d6U1khyzyQDJOCFGgkIARgBIAEqAQAiJQgBEiEBRrIPWyoPk3eSgkugPkJ6eniFTnDkezXSqYrL5TiW+RMiJwgBEgEBGiCqZQQG6g12453UPS6mqR4/2qHJCPwhp8po5eYsyBFWOSInCAESAQEaIMrCGJEl1NdHHTxSzLwezLjNlPb72glzUmWL6nM0GATQIiUIARIhAYqRDaJu600nq4vouKC8s36d4cpdqHQEUAxPd0DfZWzqIicIARIBARoggOS6HQIBFTr04W7CL3Dwnd4+SO5LXsefXeMTDMVv70I=",
"proof_client":"CpoDCpcDCiNjbGllbnRzLzA3LXRlbmRlcm1pbnQtMC9jbGllbnRTdGF0ZRKpAQorL2liYy5saWdodGNsaWVudHMudGVuZGVybWludC52MS5DbGllbnRTdGF0ZRJ6CgZjaGFpbjESBAgBEAMaBAiA6kkiBAiA324qAggoMgA6AxDRAUIZCgkIARgBIAEqAQASDAoCAAEQIRgEIAwwAUIZCgkIARgBIAEqAQASDAoCAAEQIBgBIAEwAUoHdXBncmFkZUoQdXBncmFkZWRJQkNTdGF0ZVABWAEaDAgBGAEgASoEAAKgAiIsCAESBQIEoAIgGiEgjh06ooepFXF11U91hB6FrO3lMaf7bC9FfVPw61wpeYQiLAgBEgUEBqACIBohIAm8255Xx3u4a1XuYL7KCwSuJ6WUSyt9B0EXePDC7Vf7IiwIARIFBgqgAiAaISBhOfhdRUYG3kAJMRfVe8RUgx3H1Kn9kO3fOijkfF4HKyIsCAESBQokoAIgGiEgYBv2UIjzjpPsRnbcplJ1dH/rzyVRP5cyhO8JclNaJucK/gEK+wEKA2liYxIgABr17BEdHkWhOWx3lgjph5xj7Vnd3pTWSHLPJAMk4IUaCQgBGAEgASoBACIlCAESIQFGsg9bKg+Td5KCS6A+Qnp6eIVOcOR7NdKpisvlOJb5EyInCAESAQEaIKplBAbqDXbjndQ9LqapHj/aockI/CGnymjl5izIEVY5IicIARIBARogysIYkSXU10cdPFLMvB7MuM2U9vvaCXNSZYvqczQYBNAiJQgBEiEBipENom7rTSeri+i4oLyzfp3hyl2odARQDE93QN9lbOoiJwgBEgEBGiCA5LodAgEVOvThbsIvcPCd3j5I7ktex59d4xMMxW/vQg==",
"proof_consensus":"CqgDCqUDCi1jbGllbnRzLzA3LXRlbmRlcm1pbnQtMC9jb25zZW5zdXNTdGF0ZXMvMC0yMDkShQEKLi9pYmMubGlnaHRjbGllbnRzLnRlbmRlcm1pbnQudjEuQ29uc2Vuc3VzU3RhdGUSUwoLCPe0vZkGEJjN23ESIgogLuU/QKTFwyY4CqZjuenQCa0Hbb3clqcveBDGu++7fCoaIJRgjZE+YQ9iHmoPbPuqn2LfO98deGDiZ1StZS3MilicGgwIARgBIAEqBAACoAIiKggBEiYCBKACICGIYuy9XMiupqWwXWqMKQfDNhJM/+lQRWL/gkLGskrJICIsCAESBQQIoAIgGiEgfxivhs5VQapRljQRhCUhqMQayqBDj6BpGEV6KwgChV8iKggBEiYGDKACIIqPfYNTkgrabCH/tx+B0D3Hrc036fUxQ/mLNYCj+/rnICIsCAESBQgaoAIgGiEgSxennFZ/rIYsaPJtru95KUi4KKyJ0fMFAydsrQz1fCoiKggBEiYKJKACILVwYe3pG9D5pFUOWXyoxUlAVRXrhiWjQxf/uIEzIdWTIAr+AQr7AQoDaWJjEiAAGvXsER0eRaE5bHeWCOmHnGPtWd3elNZIcs8kAyTghRoJCAEYASABKgEAIiUIARIhAUayD1sqD5N3koJLoD5Cenp4hU5w5Hs10qmKy+U4lvkTIicIARIBARogqmUEBuoNduOd1D0upqkeP9qhyQj8IafKaOXmLMgRVjkiJwgBEgEBGiDKwhiRJdTXRx08Usy8Hsy4zZT2+9oJc1Jli+pzNBgE0CIlCAESIQGKkQ2ibutNJ6uL6LigvLN+neHKXah0BFAMT3dA32Vs6iInCAESAQEaIIDkuh0CARU69OFuwi9w8J3ePkjuS17Hn13jEwzFb+9C",
"consensus_height":{
"revision_number":"0",
"revision_height":"209"
},
"signer":"cosmos19enepz3pwxd5dgucq2qp2r8l5ynm90gptf52nv"
}
After the message reaches the message server the execution continues in ConnectionKeeper
's ConnOpenAck
function, to which the message fields are passed in. The steps in ConnOpenAck
consist of:
- Check to make sure that the height of
chain1
is greater than the latest height ofchain1
thatchain2
has stored in itschain1
client. -
Retrieve the connection end previously stored during execution of
ConnOpenInit
and check that the state isSTATE_INIT
. -
Make sure that the version selected by
chain2
is supported bychain1
. - Validate that the parameters for the client state of
chain1
stored onchain2
are correct. -
Construct the expected consensus state and connection end that
chain1
expectschain2
to have stored in its state. The expected consensus state ischain1
's consensus state atconsensusHeight
(209 in this example), which is retrieved bychain1
self-inspecting its own consensus state; the expected connection end ischain2
's connection end created during theConnectionOpenTry
step. -
Verify against the app hash at height
proofHeight
thatchain2
stored in state the expected connection end. This verification checks thatchain2
has stored in the pathconnections/connection-0
of theibc
store the expected connection end with stateSTATE_TRY
created during the execution ofMsgConnectionOpenTry
. It does this by computing the merkle root hash usingproofTry
and the expected connection end and checking that it matches the root hash atproofHeight
of the consensus state stored for the light client ofchain2
. -
Verify against the app hash at height
proofHeight
thatchain2
stored in state theclientState
forchain1
. This verification checks thatchain2
has stored in the pathclients/07-tendermint-0/clientState
of theibc
store theclientState
submitted inMsgConnectionOpenTry
message at block heightproofHeight
and it does this by computing the merkle root hash usingproofClient
and theclientState
and checking that it matches the root hash atproofHeight
of the consensus state stored for the light client ofchain2
. -
Verify against the app hash at height
proofHeight
thatchain2
stored in state the consensus state forchain1
. This verification checks thatchain2
has stored in the pathclients/07-tendermint-0/consensusStates/209
of theibc
store the consensus state forchain1
atconsensus_height
(209 in this tutorial). It does this by computing the merkle root hash usingproofConsensus
and the expected consensus state and checking that it matches the root hash atproofHeight
of the consensus state stored for the light client ofchain2
. -
Update the state of the connection end on
chain1
toSTATE_OPEN
, the versions and the counterparty connection ID. - An event is emitted signalling that the connection open ack has finished successfully:
[[
{
"events":[
{
"type":"message",
"attributes":[
{
"key":"action",
"value":"/ibc.core.client.v1.MsgUpdateClient"
},
{
"key":"module",
"value":"ibc_client"
}
]
},
{
"type":"update_client",
"attributes":[
{
"key":"client_id",
"value":"07-tendermint-0"
},
{
"key":"client_type",
"value":"07-tendermint"
},
{
"key":"consensus_height",
"value":"0-145"
},
{
"key":"header",
"value":"0a262f6962632e6c69676874636c69656e74732e74656e6465726d696e742e76312e48656164657212cf060ac8040a8c030a02080b1206636861696e32189101220b0884b5bd990610f0ddfe322a480a205552635881bd8bc4dfb84d1e441d355b43a0ed61f7cf1c2a6c4d9a4ffc83c2e8122408011220457f81a2d212c28bc9d521e4c98625e6766a0983bc0321e08d62ac1b5d19b5a43220f5104909e5d2e7ee13e976ca9ff33f82fa891809cc4b24882ec0b5a3499467a23a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85542203d99b9a8b14914f657eb195fae79493105ed212dc949d7cfe247bf457f2f6cd24a203d99b9a8b14914f657eb195fae79493105ed212dc949d7cfe247bf457f2f6cd25220048091bc7ddc283f77bfbf91d73c44da58c3df8a9cbc867405d8b7f3daada22f5a20724f64b991034055ac708d6a9ef5bf923161fd6c76a32ad9f92a5e99cd8251406220d33e8d92b31e005361d66d5e9d4a07cdb91a8b22ff1eda24d232c8018506ad536a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855721414a4f15b772edd4ce0af868666983afef9e2f43512b6010891011a480a2071610bb3a37cf9b16f395295f5fa98f3feacf5ef5386300699e16fd3ec5612cf12240801122052ba73e112f2ab9e2b0e4301566524774d28bded36d22916794f9fb6d51b400422670802121414a4f15b772edd4ce0af868666983afef9e2f4351a0b0889b5bd990610c0e2dd5a2240975ff1bc4065e34b071dd5ec88568d1fba08faab574460f5245e5c4aa09f04df420b80bc74757261cc02caa2f0b4d3ae645f07c6efacfbf7a04fe16c4d08bf05127e0a3c0a1414a4f15b772edd4ce0af868666983afef9e2f43512220a206db1c80fd87c9835415b95309154ee32acb49110dc0933bd93fc765e06c711fb180a123c0a1414a4f15b772edd4ce0af868666983afef9e2f43512220a206db1c80fd87c9835415b95309154ee32acb49110dc0933bd93fc765e06c711fb180a180a1a02106a227e0a3c0a1414a4f15b772edd4ce0af868666983afef9e2f43512220a206db1c80fd87c9835415b95309154ee32acb49110dc0933bd93fc765e06c711fb180a123c0a1414a4f15b772edd4ce0af868666983afef9e2f43512220a206db1c80fd87c9835415b95309154ee32acb49110dc0933bd93fc765e06c711fb180a180a"
}
]
}
]
},
{
"msg_index":1,
"events":[
{
"type":"connection_open_ack",
"attributes":[
{
"key":"connection_id",
"value":"connection-0"
},
{
"key":"client_id",
"value":"07-tendermint-0"
},
{
"key":"counterparty_client_id",
"value":"07-tendermint-0"
},
{
"key":"counterparty_connection_id",
"value":"connection-0"
}
]
},
{
"type":"message",
"attributes":[
{
"key":"action",
"value":"/ibc.core.connection.v1.MsgConnectionOpenAck"
},
{
"key":"module",
"value":"ibc_connection"
}
]
}
]
}
]
After MsgConnectionOpenAck
successfully executes, the connection end stored on chain1
is now in state STATE_OPEN
. We can use ibc-go's REST interface to check by simply entering http://localhost:27001/ibc/core/connection/v1/connections/connection-0
and check that the state of the connection is indeed STATE_OPEN
:
{
"connection": {
"client_id": "07-tendermint-0",
"versions": [
{
"identifier": "1",
"features": [
"ORDER_ORDERED",
"ORDER_UNORDERED"
]
}
],
"state": "STATE_OPEN",
"counterparty": {
"client_id": "07-tendermint-0",
"connection_id": "connection-0",
"prefix": {
"key_prefix": "aWJj"
}
},
"delay_period": "0"
},
"proof": null,
"proof_height": {
"revision_number": "0",
"revision_height": "220"
}
}
After MsgConnectionOpenAck
succeeds on chain1
, then the relayer submits MsgConnectionOpenConfirm
on chain2
(see the message proto definition.
In hermes MsgConnectionOpenConfirm
is constructed and sent in function build_conn_confirm_and_send
. More specifically, the message is constructed in build_conn_confirm.
To build MsgConnectionOpenConfirm
hermes queries chain1
at a certain height to get proof for the following thing:
- A proof that
chain1
has updated the state of its connection end with connection IDconnection-0
toSTATE_OPEN
.
In this tutorial the height at which hermes is going to query chain1
for this proof is 242 (we know this because we will see that hermes
has submitted together with MsgConnectionOpenConfirm
a MsgUpdateClient
that updates the client state of chain1
on chain2
to the height 243). The query height is obtained here by querying Tendermint's /status
RPC endpoint and reading the values for result.node_info.network
(which is the revision number, chain1
) and result.sync_info.latest_block_height
(which is the revision height, 242).
The parameters needed to construct MsgConnectionOpenConfirm
are:
It is the connection ID for the connection end on chain2
(connection-0
in this tutorial).
It is obtained by performing an ABCI RPC query to the ibc
store of chain1
. This proof is used to verify that chain1
has stored a connection end with state STATE_OPEN
at the query height (242 in this tutorial). We can make the same ABCI RPC query on the browser by simply using Tendermint's /abci_query
REST endpoint:
path: "store/ibc/key"
data: "connections/connection-0" (in hex: 0x636f6e6e656374696f6e732f636f6e6e656374696f6e2d30)
prove: true (so that the response contains the merkle proof)
height: 242
URL: http://localhost:27000/abci_query?path="store/ibc/key"&data=0x636f6e6e656374696f6e732f636f6e6e656374696f6e2d30&prove=true&height=242
See sample JSON response.
The response to this ABCI RPC query contains both the value stored at the queried path (connections/connection-0
) and the proof needed to verify it. If we take the base64-encoded byte string from the value for the field result.response.value
and we decode it, then we can inspect the information for the connection end stored in chain1
:
{
ClientId:07-tendermint-0
Versions:[
identifier:"1"
features:"ORDER_ORDERED"
features:"ORDER_UNORDERED"
]
State:STATE_OPEN
Counterparty:{
ClientId:07-tendermint-0
ConnectionId:connection-0
Prefix:{
KeyPrefix:[105 98 99]
}
}
DelayPeriod:0
}
We see that the connection end is in state STATE_OPEN
. The key_prefix
is the byte representation of the string ibc
.
It is the height for the commitment root for proving the proof for ack. When creating this proof, chain1
is queried at height proof_height
- 1 (242 in this tutorial). The reason why proof_height
(243 in this tutorial) is query_height
+ 1 is because for Tendermint chains the app hash after applying all transactions in block n
is included in block at height n
+ 1.
It is the address of the relayer that submits the message.
Using hermes to submit this message we execute the following command:
> hermes --config config.toml tx conn-confirm \
--dst-chain chain2 \
--src-chain chain1 \
--dst-client 07-tendermint-0 \
--src-client 07-tendermint-0 \
--dst-connection connection-0 \
--src-connection connection-0
From the hermes log we can retrieve the hash of the transaction that executed MsgConnectionOpenConfirm
:
2022-09-24T19:31:53.480574Z DEBUG ThreadId(13) send_tx_commit{id=ConnectionOpenConfirm}:send_tx_with_account_sequence_retry{id=chain2}: broadcast_tx_sync: Response { code: Ok, data: Data([]), log: Log("[]"), hash: transaction::Hash(DED572F1C2930C49C43C057C6D1D959472F7027DE24704E3464483DED83C8DDD) }
And we can use this hash to get the transaction information using Tendermint's /tx
RPC endpoint:
http://localhost:27010/tx?hash=0xDED572F1C2930C49C43C057C6D1D959472F7027DE24704E3464483DED83C8DDD
See sample JSON result.
The transaction was successfully executed and included in the block at height 177. The value in the field result.tx
is a base64-encoded string of the bytes of the messages that were executed as part of the transaction. This transaction contains two messages: MsgUpdateClient
and MsgConnectionOpenConfirm
; and if we decode these bytes we can retrieve back the message data that the relayer submitted:
> simd tx decode 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
See sample JSON result.
In particular the MsgUpdateClient
and MsgConnectionOpenConfirm
look like this:
{
"@type":"/ibc.core.client.v1.MsgUpdateClient",
"client_id":"07-tendermint-0",
"header":{
"@type":"/ibc.lightclients.tendermint.v1.Header",
"signed_header":{
"header":{
"version":{
"block":"11",
"app":"0"
},
"chain_id":"chain1",
"height":"243",
"time":"2022-09-24T19:31:48.249022Z",
"last_block_id":{
"hash":"2fqqgJHqD6OcRsDPjWt/FXp8e4bAFbReM1j/JSXZFCk=",
"part_set_header":{
"total":1,
"hash":"oGwfj/3awy+HM57pdHshs5BtbkYI5pExLeH6Xz2dfDk="
}
},
"last_commit_hash":"Q6xU2wQlJNI8Knmf+33MlAJeO6Nqy2JJ5cOZdgO7wg8=",
"data_hash":"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"validators_hash":"lGCNkT5hD2Ieag9s+6qfYt873x14YOJnVK1lLcyKWJw=",
"next_validators_hash":"lGCNkT5hD2Ieag9s+6qfYt873x14YOJnVK1lLcyKWJw=",
"consensus_hash":"BICRvH3cKD93v7+R1zxE2ljD34qcvIZ0Bdi389qtoi8=",
"app_hash":"l9p2DWT0ypIf/AKrS2vCMGvV6tSMK5Jjt1VUOFeYWN0=",
"last_results_hash":"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"evidence_hash":"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
"proposer_address":"/KFlfQw/mhsgX7gFKSYhHuxsUak="
},
"commit":{
"height":"243",
"round":0,
"block_id":{
"hash":"/pwSlut6yNiK62vk752aepcgkag2QO7n1L3fqwM3uew=",
"part_set_header":{
"total":1,
"hash":"sxPVv/RFfr/inkb5OeATcq2fYKneyGEFnCMW8l7cnY4="
}
},
"signatures":[
{
"block_id_flag":"BLOCK_ID_FLAG_COMMIT",
"validator_address":"/KFlfQw/mhsgX7gFKSYhHuxsUak=",
"timestamp":"2022-09-24T19:31:53.311985Z",
"signature":"A7t/oLH8qs8+1fJWorXFpAlB3DHL2GhVrHYxnyI9aR++xeR2Qu5VZrd/JqJwmXSI6jQDrts07NhaPCKyc4l9AQ=="
}
]
}
},
"validator_set":{
"validators":[
{
"address":"/KFlfQw/mhsgX7gFKSYhHuxsUak=",
"pub_key":{
"ed25519":"Vf2Pj3yaGBnAtQLbKCb9tWC7WpLyn8u9nWkoNKB7PbU="
},
"voting_power":"10",
"proposer_priority":"0"
}
],
"proposer":{
"address":"/KFlfQw/mhsgX7gFKSYhHuxsUak=",
"pub_key":{
"ed25519":"Vf2Pj3yaGBnAtQLbKCb9tWC7WpLyn8u9nWkoNKB7PbU="
},
"voting_power":"10",
"proposer_priority":"0"
},
"total_voting_power":"10"
},
"trusted_height":{
"revision_number":"0",
"revision_height":"209"
},
"trusted_validators":{
"validators":[
{
"address":"/KFlfQw/mhsgX7gFKSYhHuxsUak=",
"pub_key":{
"ed25519":"Vf2Pj3yaGBnAtQLbKCb9tWC7WpLyn8u9nWkoNKB7PbU="
},
"voting_power":"10",
"proposer_priority":"0"
}
],
"proposer":{
"address":"/KFlfQw/mhsgX7gFKSYhHuxsUak=",
"pub_key":{
"ed25519":"Vf2Pj3yaGBnAtQLbKCb9tWC7WpLyn8u9nWkoNKB7PbU="
},
"voting_power":"10",
"proposer_priority":"0"
},
"total_voting_power":"10"
}
},
"signer":"cosmos1flqvlaum0drg089fhzqq3n042sdlvukj8dae3z"
},
{
"@type":"/ibc.core.connection.v1.MsgConnectionOpenConfirm",
"connection_id":"connection-0",
"proof_ack":"CusCCugCChhjb25uZWN0aW9ucy9jb25uZWN0aW9uLTASYAoPMDctdGVuZGVybWludC0wEiMKATESDU9SREVSX09SREVSRUQSD09SREVSX1VOT1JERVJFRBgDIiYKDzA3LXRlbmRlcm1pbnQtMBIMY29ubmVjdGlvbi0wGgUKA2liYxoMCAEYASABKgQAAqgDIioIARImAgSoAyDlbr3KBdkhO9gmoe5A0xaatAsH8Kk9yc5vwABz70CLuCAiKggBEiYECKgDIIppJtp7gjOIwGGhf7Xv5STrvNVgzIlUw2wTAGUU+66zICIqCAESJgYMqAMgBsyLghpxZViP3Bn2D7ICunvDOA0BtcaYu6a2Kw8AwmwgIiwIARIFCBKoAyAaISALd3gy24VzkapNzyUyos73DaskOFqMunB5PDe0+zbjqCIqCAESJgokqAMgUNKdevm8B/IUbQRYaZUxhXXVTyI4zR8I8ltYwiwIP08gCv4BCvsBCgNpYmMSIMQoajHoCPnsqtJK36RxsYCbtrJ706zB4FnuDG/dP9gTGgkIARgBIAEqAQAiJQgBEiEBRrIPWyoPk3eSgkugPkJ6eniFTnDkezXSqYrL5TiW+RMiJwgBEgEBGiCqZQQG6g12453UPS6mqR4/2qHJCPwhp8po5eYsyBFWOSInCAESAQEaII1jsf/SXAG2WkIS5ZCEqfA0nd8JgIjeauLJEaIr5/99IiUIARIhAQZ0ZeeWt9xWHkd3kghUybRanh6FiJS3BHHHZDSBPo6PIicIARIBARogmwRTLRVy5fnOCdMBg7OSc+eu49EqwQmx+RyG6Ek/u90=",
"proof_height":{
"revision_number":"0",
"revision_height":"243"
},
"signer":"cosmos1flqvlaum0drg089fhzqq3n042sdlvukj8dae3z"
}
After the message reaches the message server the execution continues in ConnectionKeeper
's ConnOpenConfirm
function, to which the message fields are passed in. The steps in ConnOpenConfirm
consist of:
-
Retrieve the connection end previously stored during execution of
ConnOpenTry
and check that the state isSTATE_TRYOPEN
. -
Construct the expected connection end that
chain2
expectschain1
to have stored in its state. The expected connection end ischain1
's connection end updated during theConnectionOpenAck
step (i.e. the state was updated fromSTATE_INIT
toSTATE_OPEN
). -
Verify against the app hash at height
proofHeight
thatchain1
stored in state the expected connection end. This verification checks thatchain1
has stored in the pathconnections/connection-0
of theibc
store the expected connection end with stateSTATE_OPEN
updated during the execution ofMsgConnectionOpenAck
. It does this by computing the merkle root hash usingproofAck
and the expected connection end and checking that it matches the root hash atproofHeight
of the consensus state stored for the light client ofchain1
. -
Update the state of the connection end on
chain2
toSTATE_OPEN
. - An event is emitted signalling that the connection open confirm has finished successfully:
[
{
"events":[
{
"type":"message",
"attributes":[
{
"key":"action",
"value":"/ibc.core.client.v1.MsgUpdateClient"
},
{
"key":"module",
"value":"ibc_client"
}
]
},
{
"type":"update_client",
"attributes":[
{
"key":"client_id",
"value":"07-tendermint-0"
},
{
"key":"client_type",
"value":"07-tendermint"
},
{
"key":"consensus_height",
"value":"0-243"
},
{
"key":"header",
"value":"0a262f6962632e6c69676874636c69656e74732e74656e6465726d696e742e76312e48656164657212d1060ac9040a8c030a02080b1206636861696e3118f301220b08a4b6bd990610b08cdf762a480a20d9faaa8091ea0fa39c46c0cf8d6b7f157a7c7b86c015b45e3358ff2525d91429122408011220a06c1f8ffddac32f87339ee9747b21b3906d6e4608e691312de1fa5f3d9d7c39322043ac54db042524d23c2a799ffb7dcc94025e3ba36acb6249e5c3997603bbc20f3a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855422094608d913e610f621e6a0f6cfbaa9f62df3bdf1d7860e26754ad652dcc8a589c4a2094608d913e610f621e6a0f6cfbaa9f62df3bdf1d7860e26754ad652dcc8a589c5220048091bc7ddc283f77bfbf91d73c44da58c3df8a9cbc867405d8b7f3daada22f5a2097da760d64f4ca921ffc02ab4b6bc2306bd5ead48c2b9263b7555438579858dd6220e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8556a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8557214fca1657d0c3f9a1b205fb8052926211eec6c51a912b70108f3011a480a20fe9c1296eb7ac8d88aeb6be4ef9d9a7a972091a83640eee7d4bddfab0337b9ec122408011220b313d5bff4457ebfe29e46f939e01372ad9f60a9dec861059c2316f25edc9d8e226808021214fca1657d0c3f9a1b205fb8052926211eec6c51a91a0c08a9b6bd990610e886e29401224003bb7fa0b1fcaacf3ed5f256a2b5c5a40941dc31cbd86855ac76319f223d691fbec5e47642ee5566b77f26a270997488ea3403aedb34ecd85a3c22b273897d01127e0a3c0a14fca1657d0c3f9a1b205fb8052926211eec6c51a912220a2055fd8f8f7c9a1819c0b502db2826fdb560bb5a92f29fcbbd9d692834a07b3db5180a123c0a14fca1657d0c3f9a1b205fb8052926211eec6c51a912220a2055fd8f8f7c9a1819c0b502db2826fdb560bb5a92f29fcbbd9d692834a07b3db5180a180a1a0310d101227e0a3c0a14fca1657d0c3f9a1b205fb8052926211eec6c51a912220a2055fd8f8f7c9a1819c0b502db2826fdb560bb5a92f29fcbbd9d692834a07b3db5180a123c0a14fca1657d0c3f9a1b205fb8052926211eec6c51a912220a2055fd8f8f7c9a1819c0b502db2826fdb560bb5a92f29fcbbd9d692834a07b3db5180a180a"
}
]
}
]
},
{
"msg_index":1,
"events":[
{
"type":"connection_open_confirm",
"attributes":[
{
"key":"connection_id",
"value":"connection-0"
},
{
"key":"client_id",
"value":"07-tendermint-0"
},
{
"key":"counterparty_client_id",
"value":"07-tendermint-0"
},
{
"key":"counterparty_connection_id",
"value":"connection-0"
}
]
},
{
"type":"message",
"attributes":[
{
"key":"action",
"value":"/ibc.core.connection.v1.MsgConnectionOpenConfirm"
},
{
"key":"module",
"value":"ibc_connection"
}
]
}
]
}
]
After MsgConnectionOpenConfirm
successfully executes, the connection end on both chain1
and chain2
is in state STATE_OPEN
. We can use ibc-go's REST interface to check; first for chain1
by simply entering http://localhost:27001/ibc/core/connection/v1/connections/connection-0
:
{
"connection": {
"client_id": "07-tendermint-0",
"versions": [
{
"identifier": "1",
"features": [
"ORDER_ORDERED",
"ORDER_UNORDERED"
]
}
],
"state": "STATE_OPEN",
"counterparty": {
"client_id": "07-tendermint-0",
"connection_id": "connection-0",
"prefix": {
"key_prefix": "aWJj"
}
},
"delay_period": "0"
},
"proof": null,
"proof_height": {
"revision_number": "0",
"revision_height": "251"
}
}
And then also for chain2
by simply entering http://localhost:27011/ibc/core/connection/v1/connections/connection-0
:
{
"connection": {
"client_id": "07-tendermint-0",
"versions": [
{
"identifier": "1",
"features": [
"ORDER_ORDERED",
"ORDER_UNORDERED"
]
}
],
"state": "STATE_OPEN",
"counterparty": {
"client_id": "07-tendermint-0",
"connection_id": "connection-0",
"prefix": {
"key_prefix": "aWJj"
}
},
"delay_period": "0"
},
"proof": null,
"proof_height": {
"revision_number": "0",
"revision_height": "189"
}
}
The sequence diagram was generated with the following code:
sequenceDiagram
participant chain1
participant hermes
participant chain2
critical ConnectionOpenInit
hermes->>chain1: MsgConnectionOpenInit
Note over chain1: ConnOpenInit succeeds at height 142
end
critical ConnectionOpenTry
hermes->>chain1: MsgUpdateClient
Note over chain1: chain2's consensus state updated to height 106
Note over chain1: UpdateClient succeeds at height 174
hermes-->>chain1: query chain2's client_state (height 174)
hermes-->>chain1: query proof_init (height 174)
hermes-->>chain1: query proof_client (height 174)
hermes-->>chain1: query proof_consensus (height 174)
hermes->>chain2: MsgUpdateClient
Note over chain2: chain1's consensus state updated to height 175
hermes->>chain2: MsgConnectionOpenTry
Note over chain2: Verify proofs against chain1's consensus state at height 175
Note over chain2: UpdateClient and ConnOpenTry succeed at height 109
end
critical ConnectionOpenAck
hermes->>chain2: MsgUpdateClient
Note over chain2: chain1's consensus state updated to height 209
Note over chain2: UpdateClient succeeds at height 144
hermes-->>chain2: query chain1's client_state (height 144)
hermes-->>chain2: query proof_try (height 144)
hermes-->>chain2: query proof_client (height 144)
hermes-->>chain2: query proof_consensus (height 144)
hermes->>chain1: MsgUpdateClient
Note over chain1: chain2's consensus state updated to height 145
hermes->>chain1: MsgConnectionOpenAck
Note over chain1: Verify proofs against chain2's consensus state at height 145
Note over chain1: UpdateClient and ConnOpenAck succeed at height 212
end
critical ConnectionOpenConfirm
hermes-->>chain1: query proof_ack (height 242)
hermes->>chain2: MsgUpdateClient
Note over chain2: chain1's consensus state updated to height 243
hermes->>chain2: MsgConnectionOpenConfirm
Note over chain2: Verify proof against chain1's consensus state at height 243
Note over chain2: UpdateClient and ConnOpenConfirm succeed at height 177
end
using Mermaid's live editor.