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: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.
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:- Validates that the proof value is non-empty and the path has exactly one element.
- Looks up the trusted consensus timestamp stored at
proofHeight. - Decodes the proof into
attestationDataandsignatures. - Recomputes the expected digest:
sha256(0x02 || sha256(attested_data)). - For each signature: recovers the signer address via ECDSA, checks it against the registered attestor set, and rejects duplicates. Verifies that
signatures.length >= minRequiredSigs. - Decodes
attestationDataas aPacketAttestationand verifies the attested height matchesproofHeight. - 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/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
client-state.json(template):
consensus-state.json(template):
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
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:
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: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
| Field | Description |
|---|---|
attestor_addresses | List of registered attestor Ethereum addresses |
min_required_sigs | Quorum threshold: minimum signatures required to accept a proof |
latest_height | EVM block height at time of client creation (initial trusted state) |
is_frozen | If true, the client rejects all proofs — used as an emergency stop |
Cosmos consensus state
| Field | Description |
|---|---|
timestamp | EVM block timestamp at latest_height, in nanoseconds |
EVM client constructor
| Argument | Description |
|---|---|
attestorAddresses | List of registered attestor Ethereum addresses |
minRequiredSigs | Minimum signatures required to accept a proof |
initialHeight | Cosmos block height at time of deployment (initial trusted state) |
initialTimestampSeconds | Cosmos block timestamp at initialHeight, in Unix seconds |
roleManager | Address 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 usesmin_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.