What a client is
Every claim that crosses between two chains has to be proven to the chain receiving it. Receiving a packet needs proof that the sender committed it, acknowledging needs proof that the receiver wrote an acknowledgement, and settling a timeout needs proof that the packet was never received. A client is what checks those proofs. A client does not follow the counterparty’s whole block history to do this. Instead, a client holds only what it needs to check a claim, one height at a time. For packets to move both between chains, each chain holds a client of the other. Each client records the other as its counterparty. That mirrored pair is a connection, and it is what lets two chains verify each other.
Each client lives under a client identifier in the router’s client registry, which is how the router reaches it. In IBC-solidity each registry entry is two things: the address of the light-client contract that does the verifying, and a record of the counterparty client on the counterparty chain.
Client state and consensus state
A client keeps two kinds of state:- Client state is its configuration and status: how it decides what to trust, the latest height it has verified, and whether it has been frozen. A client reports it on request. For the attestation light client that is four fields: the fixed attestor list, the quorum threshold, the latest known height, and a frozen flag.
- Consensus state is what the client has accepted as true about the counterparty at one height, and it keeps one entry per height. What an entry holds depends on the client type. For example, the attestation light client stores a trusted timestamp.
What a light client verifies
A proof is evidence that a claim about the counterparty’s state is true. A client answers five calls, and in IBC-solidity they are the wholeILightClient interface.
value, because there is nothing to expect. It proves that the counterparty held no record at a path, at the height proven. That is how a timeout is settled: no receipt for the packet, at a height whose timestamp is already past the packet’s timeout.
Either call returns the counterparty’s timestamp at the verified height, which the router uses only when settling a timeout.
Client types
IBC supports many client types. What differs between them is only how each one decides what to trust. They all have the same job: verify a claim about a record in the counterparty’s store. In addition, they all must implement the interface above. The attestation light client is one example. It trusts a fixed set of attestors, and accepts a claim signed by a quorum of them at a height it already holds. The Besu light client is another: it trusts the counterparty’s validator set, accepts headers they sealed, and checks claims with storage proofs against those headers. Because a proof is opaque bytes to IBC, the router never needs to know how a client decides what to trust. That is what lets IBC connect chains with different security models.Updating a client
An update advances a client’s view of the counterparty to a later height, so a packet proven at that height has something to be checked against. It usually travels with the packet it serves, submitted in the same transaction. Relayers do the updating. In IBC-solidity they call the router’supdateClient, which is role-gated and forwards the update to the client as opaque bytes. The contracts’ role library names a relayer role for that selector, and no deployment path that ships leaves it there. ibc deploy core binds that selector to the public role instead, so any address may relay on a chain the IBC CLI brings up.
Update: the evidence held, and a new trusted height was added.NoOp: the client already knew that height, so nothing changed.Misbehaviour: the evidence contradicted something the client already trusted. The attestation light client freezes when that happens, and a frozen client verifies nothing afterwards.
misbehaviour call. The attestation light client rejects that call, so the Misbehaviour result above is the only way it learns of misbehaviour.
Connecting two chains
Before any packet moves, each chain adds a client of its counterparty. Each side gains a verified view of the other, and the two views are paired into a connection.clientIdis the counterparty’s own identifier for the client on its side. It pairs the two clients into one connection, and the router reads it for two purposes: to fill in a packet’s destination client on a send, and to check that every incoming message names the client recorded as the counterparty on this side, on receive, acknowledge, and timeout alike .merklePrefixis where the counterparty keeps its IBC state. The router puts it in front of every path before asking the client to verify. Without it a path would point at the wrong place in the counterparty’s state, and verification would fail.
- Deploy the light client for the counterparty chain, configured with whatever it will check that chain’s claims against. Every light client also takes the router it is bound to, the only address that may submit updates and proofs to it, and whose access manager decides who may pause it.
ibc deploy clientnames this chain’s router there. - Register it with
addClient, naming the counterparty’s client identifier and merkle prefix. - Register each application on a port, so the router can deliver payloads to it. See ports and registration.