Skip to content

IBC solo machine

Carlos Rodriguez edited this page Feb 11, 2023 · 1 revision

This is a tutorial that mostly follows crypto.org's IBC solo machine documentation, but adding more details and output results.

Table of contents

Resources

Prerequisites

  • Pull the solo machine repository and follow the instructions to either build or install stag (from branch next at the time of writing). I personally built the CLI in release mode with just build-cli-release.

Setup

  • One chain with chain ID chain1 running ib-go's simd binary (version v7.0.0-rc0 at the time of writing).
  • chain1's RPC endpoint runs on http://localhost:27000 and gRPC endpoint runs on http://localhost:27002.

Signer configuration

The solo machine requires a file that configure the signer for the chain it connects to. I created a file called signer.yaml with the following contents:

chains:
- chain_id: chain1
  mnemonic: voice orient old radar urge urge dirt put hawk fiscal charge vapor canal garbage broccoli loan fog cash antique during oxygen honey return gentle
  hd_path: m/44'/118'/0'/0/0
  account_prefix: cosmos
  algo: secp256k1

The mnemonic used is for an account on chain1. I placed the file in the same directory as the stag binary.

Blockchain configuration

The solo machine also requires a file with configuration information for the chain it connects to. I created a file called chain.yaml with the following contents:

grpc_addr: http://localhost:27002
rpc_addr: http://localhost:27000
fee:
  amount: '1000'
  denom: stake
  gas_limit: 300000
trust_level: 1/3
trusting_period: 14days
max_clock_drift: 3s
rpc_timeout: 1m
diversifier: stag
trusted_height: 2
trusted_hash: 7381754AA8BFEDE794E4EF7E639125C653292030E10F37647B14AE724043803D
packet_timeout_height_offset: 20

I chose an arbitrary trusted_height and picked the trusted_hash (which is the block hash at the trusted_height).

I placed the file in the same directory as the stag binary and run:

> stag core add-chain chain.yaml

This adds the IBC-enabled chain to the solo machine.

Open connection

We establish an IBC connection with chain1:

> stag core connect chain1

When the command succeeds, we check that the light client for the solo machine is created on chain1:

> simd q ibc client state 06-solomachine-0 --node http://localhost:27000
client_state:
  '@type': /ibc.lightclients.solomachine.v3.ClientState
  consensus_state:
    diversifier: stag
    public_key:
      '@type': /cosmos.crypto.secp256k1.PubKey
      key: A72+urUIyqjxSST3ptVNc309MgRPbsIPIq0rOqA54Q2x
    timestamp: "1676025901"
  is_frozen: false
  sequence: "4"
proof: CsgCCsUCCiRjbGllbnRzLzA2LXNvbG9tYWNoaW5lLTAvY2xpZW50U3RhdGUSiAEKLC9pYmMubGlnaHRjbGllbnRzLnNvbG9tYWNoaW5lLnYzLkNsaWVudFN0YXRlElgIBBpUCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA72+urUIyqjxSST3ptVNc309MgRPbsIPIq0rOqA54Q2xEgRzdGFnGK3AmJ8GGgsIARgBIAEqAwACFCIrCAESBAIEFCAaISBYKtoyywJAdIUgqjGgvknEFy+9fUfwQxVKybOP43wuFyIrCAESBAQIFCAaISAN4qCtohJvN8rxDXl/F30aAdMwno8E7wHG66v0GcUrESIrCAESBAYMFCAaISA3ZLMr0Y+TbE4rGd1C1sMGGc76OzbdV7pt+NCnF8R5ZQr+AQr7AQoDaWJjEiApRvnrfRQnH7JhR5WFmCOFVcQvYGrg79pGvXBIR3YIoRoJCAEYASABKgEAIicIARIBARogxQXA/UixzytlYZ8SsxROGcYLTmtiUl7pNr6T0EFiPr8iJwgBEgEBGiDT9PlIvZOv6t0huJTUCr25G4tHtE/c8cAbNa+ql0HHvyIlCAESIQGIpP7BaQpATeMuv2fLbLd4QUAmHz/dMRhpbs/3cryXKyIlCAESIQGFvC3OCG+IaA28s8GhnzHzWXf80+2uOAlpl5ykrbVg1yInCAESAQEaIFSBVFbas2qPRPRDBJrT3dx155riWOPbGBfpFDMM+HtK
proof_height:
  revision_height: "27"
  revision_number: "0"

We query the connections on chain1:

> simd q ibc connection connections --node http://localhost:27000
connections:
- client_id: 06-solomachine-0
  counterparty:
    client_id: 07-tendermint-zmLS
    connection_id: connection-vAKG
    prefix:
      key_prefix: aWJj
  delay_period: "0"
  id: connection-0
  state: STATE_OPEN
  versions:
  - features:
    - ORDER_ORDERED
    - ORDER_UNORDERED
    identifier: "1"
height:
  revision_height: "37"
  revision_number: "0"
pagination:
  next_key: null
  total: "0"

And we see that the connection has been successfully created. The channel ID on the counterparty is connection-vAKG.

We also query the solo machine for the current state stored for chain1:

> stag query chain chain1
id: chain1
node_id: c566ddc717d720afe334a38f5bc937e74ca71f05
config:
  grpc_addr: http://localhost:27002/
  rpc_addr: http://localhost:27000/
  fee:
    amount: '1000'
    denom: stake
    gas_limit: 300000
  trust_level: 1/3
  trusting_period: 14days
  max_clock_drift: 3s
  rpc_timeout: 1m
  diversifier: stag
  trusted_height: 2
  trusted_hash: 7381754aa8bfede794e4ef7e639125c653292030e10f37647b14ae724043803d
  packet_timeout_height_offset: 20
consensus_timestamp: 2023-02-10T10:45:01Z
sequence: 4
connection_details:
  solo_machine_client_id: 07-tendermint-zmLS
  tendermint_client_id: 06-solomachine-0
  solo_machine_connection_id: connection-vAKG
  tendermint_connection_id: connection-0
  channels: {}
created_at: 2023-02-10T10:45:01Z
updated_at: 2023-02-10T10:45:24.472871Z

We see that there are no channels created on the connection.

Using ICS-20

We create a transfer channel between the solo machine and chain1:

> stag core channel create transfer chain1 

When the command succeeds, we can query the solo machine client state on chain1:

> simd q ibc client state 06-solomachine-0 --node http://localhost:27000
client_state:
  '@type': /ibc.lightclients.solomachine.v3.ClientState
  consensus_state:
    diversifier: stag
    public_key:
      '@type': /cosmos.crypto.secp256k1.PubKey
      key: A72+urUIyqjxSST3ptVNc309MgRPbsIPIq0rOqA54Q2x
    timestamp: "1676025901"
  is_frozen: false
  sequence: "5"
proof: CsoCCscCCiRjbGllbnRzLzA2LXNvbG9tYWNoaW5lLTAvY2xpZW50U3RhdGUSiAEKLC9pYmMubGlnaHRjbGllbnRzLnNvbG9tYWNoaW5lLnYzLkNsaWVudFN0YXRlElgIBRpUCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA72+urUIyqjxSST3ptVNc309MgRPbsIPIq0rOqA54Q2xEgRzdGFnGK3AmJ8GGgwIARgBIAEqBAACkgEiKggBEiYCBJIBIFQc3cZJM4fEs9e9xBn68EkEJgiPSWPD4gKlBMKIqe8xICIsCAESBQQGkgEgGiEgWCraMssCQHSFIKoxoL5JxBcvvX1H8EMVSsmzj+N8LhciLAgBEgUIFJIBIBohINJay+SseLRzx8ABYl0toJ53XjV6o1B8DHCizlDzrJJfCv4BCvsBCgNpYmMSIIE8IXa+tWDn7pyNfeXARajwGW2hy/Sf+DRrNfijHILnGgkIARgBIAEqAQAiJwgBEgEBGiDFBcD9SLHPK2VhnxKzFE4ZxgtOa2JSXuk2vpPQQWI+vyInCAESAQEaINP0+Ui9k6/q3SG4lNQKvbkbi0e0T9zxwBs1r6qXQce/IiUIARIhAYik/sFpCkBN4y6/Z8tst3hBQCYfP90xGGluz/dyvJcrIiUIARIhAdVw7e54YXhgjRqEFGkrBOUhs8Iosx4UoI0a0DM30zwzIicIARIBARogJQ9fTXSMPDfIRg1891ajVvTpDsSWAhA4QjIgq6FNQzA=
proof_height:
  revision_height: "86"
  revision_number: "0"

We see that the sequence has been increased because as part of the channel handshake, when chain1 processes MsgChannelOpenAck it has to verify the channel state of the counterparty (i.e. the solo machine) and this operation involves performing a membership verification, as part of which the sequence of the client state is increased.

We query the channels on chain1:

> simd q ibc channel channels --node http://localhost:27000
channels:
- channel_id: channel-0
  connection_hops:
  - connection-0
  counterparty:
    channel_id: channel-QQsp
    port_id: transfer
  ordering: ORDER_UNORDERED
  port_id: transfer
  state: STATE_OPEN
  version: ics20-1
height:
  revision_height: "88"
  revision_number: "0"
pagination:
  next_key: null
  total: "0"

And we see that the channel has been successfully created. The channel ID on the counterparty is channel-QQsp.

We can also use the solo machine to query the current state stored for chain1:

> stag query chain chain1
id: chain1
node_id: c566ddc717d720afe334a38f5bc937e74ca71f05
config:
  grpc_addr: http://localhost:27002/
  rpc_addr: http://localhost:27000/
  fee:
    amount: '1000'
    denom: stake
    gas_limit: 300000
  trust_level: 1/3
  trusting_period: 14days
  max_clock_drift: 3s
  rpc_timeout: 1m
  diversifier: stag
  trusted_height: 2
  trusted_hash: 7381754aa8bfede794e4ef7e639125c653292030e10f37647b14ae724043803d
  packet_timeout_height_offset: 20
consensus_timestamp: 2023-02-10T10:45:01Z
sequence: 5
connection_details:
  solo_machine_client_id: 07-tendermint-zmLS
  tendermint_client_id: 06-solomachine-0
  solo_machine_connection_id: connection-vAKG
  tendermint_connection_id: connection-0
  channels:
    transfer:
      packet_sequence: 1
      solo_machine_port_id: transfer
      tendermint_port_id: transfer
      solo_machine_channel_id: channel-QQsp
      tendermint_channel_id: channel-0
created_at: 2023-02-10T10:45:01Z
updated_at: 2023-02-10T10:50:46.909027Z

We see that packet_sequence is 1, which is the next sequence receive on chain1.

Mint tokens

We use the solo machine to mint tokens on chain1:

> stag transfer mint chain1 1000 gld

When the command succeeds, we query the balance of the account on chain1:

> simd q bank  balances cosmos1ay5j4j76xatjwlc2vhnu8mqn48v4hj5hn9kvck  --node http://localhost:27000
balances:
- amount: "1000"
  denom: ibc/1435A36F8027331ED1935FD47055044A33165F18F22F6270D5312075186F7027
- amount: "100000000"
  denom: samoleans
- amount: "99994000"
  denom: stake
pagination:
  next_key: null
  total: "0"

And we see that the balance contains 1000 units of denom ibc/1435A36F8027331ED1935FD47055044A33165F18F22F6270D5312075186F7027. We convert this IBC denom to denom trace information:

> simd q ibc-transfer denom-trace ibc/1435A36F8027331ED1935FD47055044A33165F18F22F6270D5312075186F7027 --node http://localhost:27000
denom_trace:
  base_denom: gld
  path: transfer/channel-0

And see that the base denom is gld, as we expected.

We can also use the solo machine to query the on-chain balance for gld:

> stag query balance chain1 gld --ibc-denom
current balance: 1000 gld

Minting tokens with the solo machine is achieved by submitting a MsgRecvPacket to chain1 with the same address for sender and receiver.

Burn tokens

We use the solo machine to burn tokens on chain1:

> stag transfer burn chain1 200 gld

When the command succeeds, we query the balance of the account on chain1:

> simd q bank  balances cosmos1ay5j4j76xatjwlc2vhnu8mqn48v4hj5hn9kvck  --node http://localhost:27000
balances:
- amount: "800"
  denom: ibc/1435A36F8027331ED1935FD47055044A33165F18F22F6270D5312075186F7027
- amount: "100000000"
  denom: samoleans
- amount: "99992000"
  denom: stake
pagination:
  next_key: null
  total: "0"

And see that the balance for ibc/1435A36F8027331ED1935FD47055044A33165F18F22F6270D5312075186F7027 has been reduced by 200 units.

The solo machine also provides a command to fetch the transaction history for a given chain:

> stag query history chain1
- id: 2
  request_id: null
  chain_id: chain1
  port_id: transfer
  operation_type:
    type: Burn
    from: cosmos1ay5j4j76xatjwlc2vhnu8mqn48v4hj5hn9kvck
    denom: gld
    amount: '200'
  transaction_hash: B8E3E0ED91D22B2716319BDFA0740EF69F291014DD027E930AD62931B508820D
  created_at: 2023-02-10T11:02:33Z
- id: 1
  request_id: null
  chain_id: chain1
  port_id: transfer
  operation_type:
    type: Mint
    to: cosmos1ay5j4j76xatjwlc2vhnu8mqn48v4hj5hn9kvck
    denom: gld
    amount: '1000'
  transaction_hash: F7321F2228948C024480BE5732D67B848E214581504F891EB1CACBB97A16B74A
  created_at: 2023-02-10T10:54:16Z

We see that we have performed 2 transactions so far with chain1, and the response includes the hashes of both.

Burning tokens with the solo machine is achieved by submitting a MsgTransfer to chain1 with the same address for sender and receiver.

Using ICS-27

We create an interchain accounts channel between the solo machine and chain1:

> stag core channel create ica chain1

When the command succeeds, we can query the channels on chain1:

> simd q ibc channel channels --node http://localhost:27000
channels:
- channel_id: channel-1
  connection_hops:
  - connection-0
  counterparty:
    channel_id: channel-Dojp
    port_id: icacontroller-0
  ordering: ORDER_ORDERED
  port_id: icahost
  state: STATE_OPEN
  version: '{"version":"ics27-1","controller_connection_id":"connection-vAKG","host_connection_id":"connection-0","address":"cosmos1ue2h5v2pd0rlayf6zs7p7hnqcus2uprpsahdvf4q9eenapnsjdvqukacpt","encoding":"proto3","tx_type":"sdk_multi_msg"}'
- channel_id: channel-0
  connection_hops:
  - connection-0
  counterparty:
    channel_id: channel-QQsp
    port_id: transfer
  ordering: ORDER_UNORDERED
  port_id: transfer
  state: STATE_OPEN
  version: ics20-1
height:
  revision_height: "351"
  revision_number: "0"
pagination:
  next_key: null
  total: "0"

We see that there are now 2 channels, one for transfer and one for interchain accounts. From the channel version of the interchain accounts channel we can get the interchain account address cosmos1ue2h5v2pd0rlayf6zs7p7hnqcus2uprpsahdvf4q9eenapnsjdvqukacpt. We also retrieved the interchain account address with the following command on the solo machine:

> stag query ica-address chain1
cosmos1ue2h5v2pd0rlayf6zs7p7hnqcus2uprpsahdvf4q9eenapnsjdvqukacpt

If we use the solo machine to query the state stored for chain1:

> stag query chain chain1
id: chain1
node_id: c566ddc717d720afe334a38f5bc937e74ca71f05
config:
  grpc_addr: http://localhost:27002/
  rpc_addr: http://localhost:27000/
  fee:
    amount: '1000'
    denom: stake
    gas_limit: 300000
  trust_level: 1/3
  trusting_period: 14days
  max_clock_drift: 3s
  rpc_timeout: 1m
  diversifier: stag
  trusted_height: 2
  trusted_hash: 7381754aa8bfede794e4ef7e639125c653292030e10f37647b14ae724043803d
  packet_timeout_height_offset: 20
consensus_timestamp: 2023-02-10T10:45:01Z
sequence: 9
connection_details:
  solo_machine_client_id: 07-tendermint-zmLS
  tendermint_client_id: 06-solomachine-0
  solo_machine_connection_id: connection-vAKG
  tendermint_connection_id: connection-0
  channels:
    icacontroller-0:
      packet_sequence: 1
      solo_machine_port_id: icacontroller-0
      tendermint_port_id: icahost
      solo_machine_channel_id: channel-Dojp
      tendermint_channel_id: channel-1
    transfer:
      packet_sequence: 2
      solo_machine_port_id: transfer
      tendermint_port_id: transfer
      solo_machine_channel_id: channel-QQsp
      tendermint_channel_id: channel-0
created_at: 2023-02-10T10:45:01Z
updated_at: 2023-02-10T11:14:24.754399Z

We can also see that there is an interchain accounts channel.

We transfer some funds to the interchain account from another account on chain1:

> simd tx bank send cosmos1ay5j4j76xatjwlc2vhnu8mqn48v4hj5hn9kvck cosmos1ue2h5v2pd0rlayf6zs7p7hnqcus2uprpsahdvf4q9eenapnsjdvqukacpt 2000samoleans \
--keyring-backend test \
--chain-id chain1 \
--home ../../gm/chain1 \
--node http://localhost:27000

We check that the funds are received:

> simd q bank balances  cosmos1ue2h5v2pd0rlayf6zs7p7hnqcus2uprpsahdvf4q9eenapnsjdvqukacpt  --node http://localhost:27000
balances:
- amount: "2000"
  denom: samoleans
pagination:
  next_key: null
  total: "0"

So now we can execute a x/bank MsgSend on the interchain account using ICA:

> stag ica bank send chain1 cosmos1ay5j4j76xatjwlc2vhnu8mqn48v4hj5hn9kvck 1000 samoleans
successfully sent 1000 samoleans to cosmos1ay5j4j76xatjwlc2vhnu8mqn48v4hj5hn9kvck

We check again the balance of the interchain account:

> simd q bank balances  cosmos1ue2h5v2pd0rlayf6zs7p7hnqcus2uprpsahdvf4q9eenapnsjdvqukacpt  --node http://localhost:27000
balances:
- amount: "1000"
  denom: samoleans
pagination:
  next_key: null
  total: "0"

To check that the balance has been accordingly reduced.

We use the solo machine to fetch the transaction history for chain1:

> stag query history chain1
- id: 3
  request_id: null
  chain_id: chain1
  port_id: icacontroller-0
  operation_type:
    type: IcaSend
    to: cosmos1ay5j4j76xatjwlc2vhnu8mqn48v4hj5hn9kvck
    denom: samoleans
    amount: '1000'
  transaction_hash: 63BEDDF0DB4648A18551E8E76A121CA6711B43451D82DF1C0AE52AFB58216C1B
  created_at: 2023-02-10T11:23:44Z
- id: 2
  request_id: null
  chain_id: chain1
  port_id: transfer
  operation_type:
    type: Burn
    from: cosmos1ay5j4j76xatjwlc2vhnu8mqn48v4hj5hn9kvck
    denom: gld
    amount: '200'
  transaction_hash: B8E3E0ED91D22B2716319BDFA0740EF69F291014DD027E930AD62931B508820D
  created_at: 2023-02-10T11:02:33Z
- id: 1
  request_id: null
  chain_id: chain1
  port_id: transfer
  operation_type:
    type: Mint
    to: cosmos1ay5j4j76xatjwlc2vhnu8mqn48v4hj5hn9kvck
    denom: gld
    amount: '1000'
  transaction_hash: F7321F2228948C024480BE5732D67B848E214581504F891EB1CACBB97A16B74A
  created_at: 2023-02-10T10:54:16Z

So that we can see that a third transaction has been added for the ICA message.

Close channel

We can use the solo machine to close any of the open channels. For example, to close the transfer channel:

> stag core channel close transfer chain1 

Closing of the channel works by submitting a MsgChannelCloseConfirm to chain1.