Skip to content

Connection handshake

Carlos Rodriguez edited this page Mar 12, 2023 · 17 revisions

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:

Table of Contents

Setup

  • Two chains (chain1 and chain2) running ib-go's simd binary.
  • chain1's RPC endpoint runs on http://localhost:27000 and REST API runs on http://localhost:27001.
  • chain2's RPC endpoint runs on http://localhost:27010 and REST API runs on http://localhost:27011.
  • hermes relayer.

This document is updated for ibc-go v4.0.0 and hermes v1.0.0.

Assumptions

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.

ICS-03 store paths

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

Client creation

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,
      },
    },
  ),
)

Connection handshake

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:

ConnectionOpenInit

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.

Building MsgConnectionOpenInit

The parameters needed to construct MsgConnectionOpenInit are:

client_id

It's the client ID of the light client on chain1 (07-tendermint-0 in this tutorial).

counterparty

It is constructed here:

  • client_id is the client ID of the light client on chain2 (07-tendermint-0 in this tutorial).
  • connection_id is empty, since chain2 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 from chain2. This prefix is the store prefix used by the on-chain IBC module for Cosmos-SDK chains and at the moment is ibc.
version

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"]).

delay_period

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.

signer

It is the address of the relayer that submits the message.

Submitting MsgConnectionOpenInit

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.

Executing MsgConnectionOpenInit

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:

  1. 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 returns true if the proposed version has a matching version in the list of compatible versions.
  2. 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.
  3. A new ConnectionEnd is instantiated with the information from the message and with the state STATE_INIT. The connection end is stored under the key connections/connection-0. The connection ID is also added to the set of connections associated with the client (which is stored under key clients/07-tendermint-0/connections in this tutorial).
  4. 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 included MsgConnectionOpenInit) 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"
  }
}

ConnectionOpenTry

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 ID connection-0 and state STATE_INIT.
  • A proof that chain1 has stored for the light client with client ID 07-tendermint-0 the client state for chain2.
  • A proof that chain1 has stored for the light client with client ID 07-tendermint-0 the consensus state for chain2.

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).

Building MsgConnectionOpenTry

The parameters needed to construct MsgConnectionOpenTry are:

client_id

it is the client ID of the light client on chain2 (07-tendermint-0 in this tutorial).

previous_connection_id

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.

client_state

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.

counterparty

It is constructed here:

  • client_id is the client ID of the light client on chain1 (07-tendermint-0 in this tutorial).
  • connection_id is the connection ID for the connection end on chain1, which is connection-0 in this tutorial.
  • prefix is determined here at the moment in a similar way as it was done for MsgConnectionOpenInit (i.e. reading the value ibc from the config file of hermes).
delay_period

It is determined here.

counterparty_versions

It gets assigned here the versions from the connection end on chain1, if they exist; or the default, otherwise (as done similarly for MsgConnectionOpenInit).

proof_height

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.

proof_init

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.

proof_client

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.

proof_consensus

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).

consensus_height

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).

signer

It is the address of the relayer that submits the message.

Submitting MsgConnectionOpenTry

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 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

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"
}

Executing MsgConnectionOpenTry

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:

  1. 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.
  2. Check to make sure that the height of chain2 is greater than the latest height of chain2 that chain1 has stored in its chain2 client state.
  3. Validate that the parameters for the client state of chain2 stored on chain1 are correct.
  4. Construct the expected consensus state and connection end that chain2 expects chain1 to have stored in its state. The expected consensus state is chain2's consensus state at consensusHeight (106 in this tutorial), which is retrieved by chain2 self-inspecting its own consensus state; the expected connection end is chain1's connection end created during the ConnectionOpenInit step.
  5. A connection end is created that chain2 will store in state.
  6. Verify against the app hash at height proofHeight that chain1 stored in state the expected connection end. This verification checks that chain1 has stored in the path connections/connection-0 of the ibc store the expected connection end with state STATE_INIT created during the execution of MsgConnectionOpenInit. It does this by computing the merkle root hash using proofInit and the expected connection end and checking that it matches the root hash at proofHeight of the consensus state stored for the light client of chain1.
  7. Verify against the app hash at height proofHeight that chain1 stored in state the clientState for chain2. This verification checks that chain1 has stored in the path clients/07-tendermint-0/clientState of the ibc store the clientState submitted in MsgConnectionOpenTry. It does this by computing the merkle root hash using proofClient and the clientState and checking that it matches the root hash at proofHeight of the consensus state stored for the light client of chain1.
  8. Verify against the app hash at height proofHeight that chain1 stored in state the consensus state for chain2. This verification checks that chain1 has stored in the path clients/07-tendermint-0/consensusStates/106 of the ibc store the consensus state for chain2 at consensus_height (106 in this tutorial). It does this by computing the merkle root hash using proofConsensus and the expected consensus state and checking that it matches the root hash at proofHeight of the consensus state stored for the light client of chain1.
  9. 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 key connections/connection-0.
  10. 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 included MsgConnectionOpenTry) 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"
  }
}

ConnectionOpenAck

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 ID connection-0 and state STATE_TRY.
  • A proof that chain2 has stored for the light client with client ID 07-tendermint-0 the client state for chain1.
  • A proof that chain2 has stored for the light client with client ID 07-tendermint-0 the consensus state for chain1.

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).

Building MsgConnectionOpenAck

The parameters needed to construct MsgConnectionOpenAck are:

connection_id

It is the connection ID for the connection end on chain1 (connection-0 in this tutorial).

counterparty_connection_id

It is the connection ID for the connection end on chain2.

version

It is the version selected by chain2.

client_state

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.

proof_height

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.

proof_try

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.

proof_client

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.

proof_consensus

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).

consensus_height

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).

signer

It is the address of the relayer that submits the message.

Submitting MsgConnectionOpenAck

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"
}

Executing MsgConnectionOpenAck

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:

  1. Check to make sure that the height of chain1 is greater than the latest height of chain1 that chain2 has stored in its chain1 client.
  2. Retrieve the connection end previously stored during execution of ConnOpenInit and check that the state is STATE_INIT.
  3. Make sure that the version selected by chain2 is supported by chain1.
  4. Validate that the parameters for the client state of chain1 stored on chain2 are correct.
  5. Construct the expected consensus state and connection end that chain1 expects chain2 to have stored in its state. The expected consensus state is chain1's consensus state at consensusHeight (209 in this example), which is retrieved by chain1 self-inspecting its own consensus state; the expected connection end is chain2's connection end created during the ConnectionOpenTry step.
  6. Verify against the app hash at height proofHeight that chain2 stored in state the expected connection end. This verification checks that chain2 has stored in the path connections/connection-0 of the ibc store the expected connection end with state STATE_TRY created during the execution of MsgConnectionOpenTry. It does this by computing the merkle root hash using proofTry and the expected connection end and checking that it matches the root hash at proofHeight of the consensus state stored for the light client of chain2.
  7. Verify against the app hash at height proofHeight that chain2 stored in state the clientState for chain1. This verification checks that chain2 has stored in the path clients/07-tendermint-0/clientState of the ibc store the clientState submitted in MsgConnectionOpenTry message at block height proofHeight and it does this by computing the merkle root hash using proofClient and the clientState and checking that it matches the root hash at proofHeight of the consensus state stored for the light client of chain2.
  8. Verify against the app hash at height proofHeight that chain2 stored in state the consensus state for chain1. This verification checks that chain2 has stored in the path clients/07-tendermint-0/consensusStates/209 of the ibc store the consensus state for chain1 at consensus_height (209 in this tutorial). It does this by computing the merkle root hash using proofConsensus and the expected consensus state and checking that it matches the root hash at proofHeight of the consensus state stored for the light client of chain2.
  9. Update the state of the connection end on chain1 to STATE_OPEN, the versions and the counterparty connection ID.
  10. 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"
  }
}

ConnectionOpenConfirm

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 ID connection-0 to STATE_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).

Building MsgConnectionOpenConfirm

The parameters needed to construct MsgConnectionOpenConfirm are:

connection_id

It is the connection ID for the connection end on chain2 (connection-0 in this tutorial).

proof_ack

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.

proof_height

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.

signer

It is the address of the relayer that submits the message.

Submitting MsgConnectionOpenConfirm

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"
}

Executing MsgConnectionOpenConfirm

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:

  1. Retrieve the connection end previously stored during execution of ConnOpenTry and check that the state is STATE_TRYOPEN.
  2. Construct the expected connection end that chain2 expects chain1 to have stored in its state. The expected connection end is chain1's connection end updated during the ConnectionOpenAck step (i.e. the state was updated from STATE_INIT to STATE_OPEN).
  3. Verify against the app hash at height proofHeight that chain1 stored in state the expected connection end. This verification checks that chain1 has stored in the path connections/connection-0 of the ibc store the expected connection end with state STATE_OPEN updated during the execution of MsgConnectionOpenAck. It does this by computing the merkle root hash using proofAck and the expected connection end and checking that it matches the root hash at proofHeight of the consensus state stored for the light client of chain1.
  4. Update the state of the connection end on chain2 to STATE_OPEN.
  5. 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"
  }
}

Graphics

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.