IBC calls
Four calls move a packet in IBC:sendPacket is an application’s own call, and the router accepts it only from the application registered on the payload’s source port. The other three are role-gated: a relayer submits them, and each chain’s role map decides which addresses it admits to those calls. On a chain the IBC CLI brings up, that map admits any address.
A relayer stands between every pair of steps. Each chain learns what the other wrote only through a proof its own client verifies, and the store keeps only a hash of the packet, so the send event is what carries its fields to a relayer. That client has to be current for the proof to verify. So a relayer packs updateClient into the same transaction, ahead of the call it is relaying.
Sending a packet
Sending a packet begins inside the application. The application runs its own logic first, then callssendPacket on the source router with a source client, a timeout, and a payload which carries the application’s message. The source router then runs the following:
- Check the caller: it must be the application registered on the payload’s source port.
- Look up the counterparty: it reads the counterparty registered for the source client.
- Check the deadline: the timeout must be in the future and not past a maximum.
- Fill in the packet: it sets that counterparty as the destination client and takes a sequence from a per-client counter.
- Write the commitment: it writes a fixed-length hash over the destination client, the timeout, and the payload, at a path built from the source client and the sequence.
- Emit
SendPacket: the event carries all packet fields, which is how a relayer reconstructs the packet.
Receiving a packet
A relayer submitsrecvPacket to the destination router, with the packet, a proof of the packet commitment, and the height that proof is against.
Then the destination router runs the following steps:
- Check the route: ensure the packet’s source client is the counterparty registered for its destination client.
- Check the deadline: ensure the packet’s timeout is still in the future.
- Verify the proof: the destination client confirms that the packet’s commitment matches the commitment in the router store on the source chain, at the packet’s path under the counterparty’s merkle prefix.
- Write the receipt: a receipt is written to the router store proving the packet was delivered. If a receipt is already written, the handler records a no-op and returns without calling the application, preventing the same packet from being delivered twice.
- Call the application: the router passes the payload to the destination application. The destination application’s
onRecvPacketruns and returns acknowledgement bytes to the router. - Commit the acknowledgement: the destination router writes an acknowledgement commitment to its store. It then emits
WriteAcknowledgement, carrying the full packet and the acknowledgement bytes for the relayer to carry back to the source chain.
IBCAppRecvPacketCallbackError on the destination chain. The reserved value is the same for every failure, so the reason never crosses to the source chain.
A few failures produce no acknowledgement at all. One of those leaves the packet to be relayed again, and the rest leave a timeout as the only ending. See acknowledgements and callback failures.
Acknowledging on the source chain
The relayer carries the packet, the acknowledgement bytes, and a proof back to the source chain, and submits them toackPacket on the source router. The source router then runs the following:
- Check the route: the packet’s destination client must be the counterparty registered for its source client.
- Verify the proof: the router recomputes the acknowledgement commitment locally, and its client must prove the destination chain stored exactly that value at the acknowledgement path.
- Delete the commitment: the router first hashes the relayer-supplied packet and requires it to match what is stored, so an acknowledgement only ever applies to the packet that was sent. If no commitment is there, the handler records a no-op, which is what makes a repeated relay harmless.
- Call the application: the sending application’s
onAcknowledgementPacketruns with the payload and the acknowledgement bytes, and the router emitsAckPacket.
Timing out a packet
A timeout is a proof of absence: the destination chain never wrote a receipt, and its clock has already passed the packet’s deadline. After the deadline, a relayer callstimeoutPacket on the source router with a non-existence proof in place of an acknowledgement. The source router then runs the following:
- Check the route: the packet’s destination client must be the counterparty registered for its source client.
- Verify the absence: the client proves non-membership of the receipt path on the destination chain, and returns the counterparty’s timestamp at that height. The router requires that timestamp to have reached the packet’s timeout.
- Delete the commitment: the router first hashes the relayer-supplied packet and requires it to match what is stored, so a timeout only ever applies to the packet that was sent. If no commitment is there, the handler records a no-op.
- Call the application: the sending application’s
onTimeoutPacketruns, and the router emitsTimeoutPacket.
What the protocol guarantees
The lifecycle rests on three proofs:- Receive: the source committed the packet.
- Acknowledge: the destination committed an answer.
- Timeout: the destination recorded no receipt before the deadline.
recvPacket wraps the receiving application’s callback, so a revert there carrying a reason becomes an error acknowledgement. ackPacket and timeoutPacket call the sending application with no such wrapper. If that callback always reverts, the call reverts with it, the commitment is never deleted, and neither ending can complete.
The tutorial sends a packet through this lifecycle and reports where it got to.