Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cosmos.network/llms.txt

Use this file to discover all available pages before exploring further.

This step creates an attestation light client on each chain and registers each client’s counterparty. Each client is initialized with the attestor address and an initial trusted height and timestamp from the counterparty chain. Once both clients exist and their counterparties are registered, the two chains can verify each other’s packets. Run the following:
./setup.sh create-clients
The logic for this command is in lib/ibc.sh.

Attestation light client

An attestation light client verifies IBC packets using ECDSA signatures from a registered set of off-chain attestors. There are two implementations: Go (cosmos/ibc-go) for the Cosmos side and Solidity (cosmos/solidity-ibc-eureka) for the EVM side. Each client is initialized with a list of attestor Ethereum addresses and a quorum threshold. When a packet arrives with an attestation proof, the light client:
  1. Validates that the proof value is non-empty and the path has exactly one element.
  2. Looks up the trusted consensus timestamp stored at proofHeight.
  3. Decodes the proof into attestationData and signatures.
  4. Recomputes the expected digest: sha256(0x02 || sha256(attested_data)).
  5. For each signature: recovers the signer address via ECDSA, checks it against the registered attestor set, and rejects duplicates. Verifies that signatures.length >= minRequiredSigs.
  6. Decodes attestationData as a PacketAttestation and verifies the attested height matches proofHeight.
  7. Checks that the packet commitment is present in the attested packets array, matching on both the keccak256 hash of the path and the commitment value.

What the script does

1. Generate or read the attestor keystore

Both light clients are initialized with the attestor’s Ethereum address. The script first ensures the keystore exists, generating a new one if needed, then reads the address from it:
ibc_attestor key show
The keystore is written to ibc/local/.ibc-attestor/ibc-attestor-keystore and reused by the attestor services started in a later step.
Ethereum addresses configured in the light client must be in EIP-55 checksummed format.

2. Create the Cosmos-side client

The Cosmos-side client verifies EVM packets on the Cosmos chain. It is initialized with:
  • The attestor’s Ethereum address
  • The current EVM block height and timestamp as the initial trusted state
The script renders two JSON files from templates and submits them:
sandboxd tx ibc client create client-state.json consensus-state.json
The client state holds the client’s configuration: which attestor addresses are trusted, the quorum threshold, the latest known height, and whether the client is frozen:
{
  "@type": "/ibc.lightclients.attestations.v1.ClientState",
  "attestor_addresses": ["<ATTESTOR_ETH_ADDR>"],
  "min_required_sigs": 1,
  "latest_height": <EVM_BLOCK_HEIGHT>,
  "is_frozen": false
}
The consensus state records the block timestamp at the initial trusted height, which anchors proof verification:
{
  "@type": "/ibc.lightclients.attestations.v1.ConsensusState",
  "timestamp": "<EVM_BLOCK_TIMESTAMP_NANOSECONDS>"
}
Output: COSMOS_CLIENT_ID in the format attestations-N.

3. Create and register the EVM-side client

The EVM-side client verifies Cosmos packets on the EVM chain. It is a Solidity contract (AttestationLightClient) deployed from prebuilt bytecode and registered with the ICS26Router. The script initializes it with:
  • The attestor’s Ethereum address
  • The current Cosmos block height and timestamp as the initial trusted state
After deployment, the contract is registered with the ICS26Router on the EVM chain. The CounterpartyInfo passed to addClient includes the Cosmos client ID, so the EVM client knows its counterparty at registration time:
ICS26Router.addClient((COSMOS_CLIENT_ID, [0x]), lcAddress)
The EVM client ID (EVM_CLIENT_ID, in the format client-N) is assigned by the router on registration. Output: EVM_CLIENT_ID in the format client-N.

4. Register the Cosmos-side counterparty

With both client IDs now known, the script registers the Cosmos-side counterparty. This is the on-chain record that maps the Cosmos attestation client to its EVM peer:
sandboxd tx ibc client add-counterparty $COSMOS_CLIENT_ID $EVM_CLIENT_ID ""
The EVM client registers its counterparty at addClient time (the Cosmos client ID is passed in the CounterpartyInfo). The Cosmos client registers its counterparty here after the EVM client ID is known.

Configuration reference

Cosmos client state

FieldDescription
attestor_addressesList of registered attestor Ethereum addresses
min_required_sigsQuorum threshold: minimum signatures required to accept a proof
latest_heightEVM block height at time of client creation (initial trusted state)
is_frozenIf true, the client rejects all proofs — used as an emergency stop

Cosmos consensus state

FieldDescription
timestampEVM block timestamp at latest_height, in nanoseconds

EVM client constructor

ArgumentDescription
attestorAddressesList of registered attestor Ethereum addresses
minRequiredSigsMinimum signatures required to accept a proof
initialHeightCosmos block height at time of deployment (initial trusted state)
initialTimestampSecondsCosmos block timestamp at initialHeight, in Unix seconds
roleManagerAddress granted DEFAULT_ADMIN_ROLE (full role administration) and PROOF_SUBMITTER_ROLE on the contract. Use address(0) to grant PROOF_SUBMITTER_ROLE to everyone, allowing any caller to submit proofs (demo only)

Applying this to your own setup

Initial trusted state

The initial height and timestamp anchor the light client to a specific point in the counterparty chain’s history.

Quorum threshold

The demo uses min_required_sigs: 1 because there is a single attestor. For production, set this to the threshold of your attestor set. It is recommended to use a threshold of greater than 1.

roleManager in production

The demo passes address(0) as the roleManager, which allows anyone to submit proofs. For production, pass the ICS26Router proxy address so only the router can submit proofs to the light client.

Next steps

With both light clients created, counterparties registered, and their IDs written to state, the next step is to register the IFT bridges.