概述

IBC 通过提供由 ICS-4 数据包处理器认证的安全数据包流,实现远程状态机之间的模块到模块通信。IBC 核心协议负责两条链之间数据包的 TAO(传输、认证、有序性)。这些数据包包含一个或多个载荷,用于承载两个 ICS26 应用之间传递的应用特定信息。载荷中的数据本身对于 IBC 核心协议是不透明的,IBC 核心只验证该数据是否由发送方正确发送,然后将该数据提供给接收方,以便进行应用特定的解释和处理。 本规范为所有数据包流消息标准化定义了 ICS-4(核心 IBC/TAO)与 IBC 应用(即 ICS26 应用)之间的接口。 默认的 IBC 处理器使用接收方调用模式,其中模块必须分别调用 IBC 处理器来发送数据包。相应地,IBC 处理器会验证传入的数据包流消息,例如 ReceivePacket、AcknowledgePacket 和 TimeoutPacket,并按照 ICS5 端口分配 中的描述调用相应的 ICS26 应用。

技术规范

载荷结构

载荷结构摘录自 ICS-4,因为下面所有应用函数都作用于数据包中发送的载荷。
interface Payload {
    sourcePort: bytes, // identifier of the sending application on the sending chain
    destPort: bytes, // identifier of the receiving application on the receiving chain
    version: string, // payload version only interpretable by sending/receiving applications
    encoding: string, // payload encoding only interpretable by sending/receiving applications
    value: bytes // application-specific data that can be parsed by receiving application given the version and encoding
}

暴露给 ICS26 应用的核心处理器接口

IBC 核心处理器必须向注册在端口路由器上的 ICS26 应用暴露以下函数签名,以便应用可以发送数据包。

SendPacket

SendPacket 输入: payloads: Payload:这是应用希望发送给接收链上某个应用的载荷。 sourceClientId: bytes:发送链上存在的、用于标识接收链客户端的标识符。 timeoutTimestamp: uint64:以 UNIX 秒表示的超时时间,超过该时间后,数据包在接收链上将不再可接收。注意:该时间戳是相对于接收链时钟进行判断的,因为发送链和接收链的时钟之间可能存在漂移 SendPacket 前置条件:
  • 应用已使用 payload.SourcePortId 注册到端口路由器
  • 应用必须已成功完成发送给定载荷所需的任何应用特定逻辑。
  • sourceClientId 对应的发送客户端存在
SendPacket 后置条件:
  • 以下数据包会被提交,并按 ICS24 的规定存储在数据包承诺路径下:
interface Packet {
    sourceClientId: sourceClientId,
    destClientId: getCounterparty(sourceClientId).ClientId,  // destClientId should be filled in with the registered counterparty id for provided sourceClientId
    sequence: generateUniqueSequence(sourceClientId),
    timeoutTimestamp: msg.timeoutTimestamp
    data: msg.Payloads
}
  • 序列号会返回给 ICS26 应用
SendPacket 错误条件:
  • 发送客户端无效(已过期或已冻结)
  • 提供的 timeoutTimstamp 已经过期
  • 发送应用无权将提供的载荷发送给由 payload.DestPort 标识的目标接收应用
注意:IBC v2 允许将来自多个应用的多个载荷放在同一个数据包中发送。如果某个实现选择支持该特性,可以选择在核心处理器中提供一个发送多个数据包的入口点,该入口点随后必须调用每个应用各自的 OnSendPacket 回调,以校验各自的载荷并执行应用特定的发送逻辑;或者也可以将来自各个应用的载荷排队,直到数据包准备好被提交。

WriteAcknowledgement

IBC 核心处理器可以向注册在端口路由器上的 ICS26 应用暴露以下函数签名,以便应用能够异步写入确认。 只有在实现支持异步处理数据包时才有此必要。在这种情况下,应用可以在 IBC 核心处理器收到数据包之后异步处理该数据包。因此,确认不能作为 OnRecvPacket 回调的一部分返回,而必须在稍后的时间由 ICS26 应用提交给核心 IBC 处理器。因此,我们必须在 IBC 处理器上引入一个新的端点,供 ICS26 应用在完成接收数据包处理并希望写入确认时调用。 WriteAcknowledgement 输入: destClientId: bytes:接收链(即执行链)上存在的、用于标识发送链客户端的标识符 sequence: uint64:用于标识从发送链到接收链的数据包的唯一序列号 ack: bytes:接收应用针对其收到的载荷返回的确认。如果接收失败,ack 必须为 SENTINEL_ERROR_ACKNOWLEDGEMENT;否则,它可以是某些应用特定数据。 WriteAcknowledgement 前置条件:
  • 已按指定的 ICS24,在 destClientId 和 sequence 对应的位置存储了数据包回执
  • 尚未在 ICS24 路径下为 destClientId 和 sequence 写入确认
WriteAcknowledgement 后置条件:
  • 确认会被提交,并按 ICS24 的规定写入确认路径
  • 如果确认成功,则所有接收应用都必须已经执行其 recvPacket 逻辑并写入状态
  • 如果确认失败(即 ERROR ACK),则接收应用所做的任何状态变更都必须全部回滚。这可确保多载荷数据包的原子执行。
注意:如果数据包包含多个载荷,IBC 核心处理器必须等待所有应用都返回该数据包各自的确认后,才能提交该确认。如果任意应用返回错误确认,则整个数据包的确认只包含 ERROR_SENTINEL_ACKNOWLEDGEMENT。否则,该确认是一个列表,按其关联载荷在数据包中出现的相同顺序,包含每个应用各自的确认。

暴露给核心处理器的 ICS26 接口

模块必须向路由模块暴露以下函数签名,这些函数会在收到各种数据报时被调用:

OnRecvPacket

OnRecvPacket 输入: sourceClientId: bytes:这是发送链上客户端的标识符。注意:这是对手链上的一个标识符,作为信息提供给应用,但不应被视为接收链上的唯一标识符。 destClientId: bytes:这是接收链(即执行链)的标识符 sequence: uint64:这是从发送链到目标链的数据包流中的唯一序列号。元组 (destClientId, sequence) 在本链上唯一标识该数据包。 payload: Payload。这是由发送链上通过 payload.SourcePort 注册的应用发送给执行应用的载荷 OnRecvPacket 前置条件:
  • 应用已使用 payload.DestPort 注册到端口路由器
  • destClientId 对应的目标客户端存在
  • 所有 IBC/TAO 验证检查都已经由 IBC 核心处理器完成认证。因此,当应用收到数据包时,可以保证其真实性,只需要针对给定载荷执行相关的应用逻辑。
OnRecvPacket 后置条件:
  • 应用已经针对给定载荷执行了所有应用特定逻辑,并进行了适当的状态变更
  • 应用向核心 IBC 处理器返回应用确认 ack: bytes,以便将其写入为该数据包中该载荷的确认。
OnRecvPacket 错误条件:
  • 由 payload.SourcePortId 标识的发送应用无权向接收应用发送载荷
  • 由 payload.Version 标识的请求版本不受支持
  • 由 payload.Encoding 标识的请求编码不受支持
  • 使用 payload.Encoding 解码 payload.Value 后,并按照 payload.Version 所期望的方式处理载荷时发生错误。
重要:如果 OnRecvPacket 回调因任何原因报错,则该回调期间所做的状态变更都必须回滚,并且 IBC 核心处理器必须为该数据包写入 SENTINEL_ERROR_ACKNOWLEDGEMENT,即使该数据包中的其他载荷已成功接收也是如此。

OnAcknowledgePacket

OnAcknowledgePacket 输入: sourceClientId: bytes:这是发送链上客户端(即执行链)的标识符。 destClientId: bytes:这是接收链的标识符。注意:这是对手链上的一个标识符,作为信息提供给应用,但不应被视为接收链上的唯一标识符。 sequence: uint64:这是从发送链到目标链的数据包流中的唯一序列号。元组 (sourceClientId, sequence) 在本链上唯一标识该数据包。 acknowledgement: bytes:这是接收应用针对我们之前发送的载荷返回的确认。它可以是包含应用特定信息的成功确认,也可以是 SENTINEL_ERROR_ACKNOWLEDGEMENT,在这种情况下,我们应处理发送失败数据包所需的任何应用特定逻辑。 payload: Payload:这是我们之前发送的原始载荷 OnAcknowledgementPreconditions:
  • 该应用此前曾在一个具有给定 sourceClientId 和 sequence 的数据包中发送过所提供的载荷。
  • 所有 IBC/TAO 验证检查都已经由 IBC 核心处理器完成认证。因此,当应用收到确认时,可以保证其真实性,只需要针对给定确认和载荷执行相关的应用逻辑。
OnAcknowledgement 后置条件:
  • 应用已经针对给定载荷和确认执行了所有应用特定逻辑,并进行了适当的状态变更
  • 如果确认是 SENTINEL_ERROR_ACKNOWLEDGEMENT,这通常意味着需要回滚在 SendPacket 期间所做的应用状态变更(例如为转账解托管代币)
OnAcknowledgement 错误条件:
  • 在处理确认时可能发生应用特定错误。数据包生命周期已经完成。实现可以选择允许重试,也可以选择不允许。

OnTimeoutPacket

OnTimeoutPacket 输入: sourceClientId: bytes:这是发送链(即执行链)上客户端的标识符。 destClientId: bytes:这是接收链的标识符。注意:这是对手方链上的一个标识符,作为信息提供给应用,但不应将其视为接收链上的唯一标识符。 sequence: uint64:这是数据包在从发送链到目标链的数据包流中的唯一序列号。元组 (sourceClientId, sequence) 在本链上唯一标识该数据包。 payload: Payload:这是我们此前发送的原始负载。 OnTimeoutPacket 前置条件:
  • 此应用此前曾使用提供的 sourceClientId 和 sequence,在一个数据包中发送过所提供的负载。
  • 所有 IBC/TAO 验证检查都已经由 IBC 核心处理器完成认证。因此,当应用接收到超时时,可以保证其真实性,只需针对给定负载执行相关的应用层超时逻辑。
OnTimeoutPacket 后置条件:
  • 应用已针对给定负载执行所有应用特定逻辑,并完成适当的状态变更。这通常包括回滚在 SendPacket 期间进行的应用状态变更(例如,对 transfer 场景中的代币解除托管)。
OnTimeoutPacket 错误条件:
  • 在处理超时时,可能会发生应用特定错误。数据包生命周期已结束。实现可以选择允许重试,也可以选择不允许重试。

Synopsis

IBC enables module to module communication across remote state machines by providing a secure packet flow authenticated by the ICS-4 packet handler. The IBC core protocol is responsible for TAO (transport, authentication, ordering) of packets between two chains. These packets contain payload(s) that carry the application-specific information that is being communicated between two ICS26 applications. The data in the payload is itself opaque to the IBC core protocol, IBC core only verifies that it was correctly sent by the sender and then provides that data to the receiver for application-specific interpretation and processing. This specification standardizes the interface between ICS-4 (core IBC/TAO) and an IBC application (i.e. ICS26 app) for all the packet flow messages. The default IBC handler uses a receiver call pattern, where modules must individually call the IBC handler in order to send packets. In turn, the IBC handler verifies incoming packet flow messages like ReceivePacket, AcknowledgePacket and TimeoutPacket and calls into the appropriate ICS26 application as described in ICS5 Port Allocation.

Technical Specification

Payload Structure

The payload structure is reproduced from ICS-4 since all of the following application functions are operating on the payloads that are being sent in the packets.
interface Payload {
    sourcePort: bytes, // identifier of the sending application on the sending chain
    destPort: bytes, // identifier of the receiving application on the receiving chain
    version: string, // payload version only interpretable by sending/receiving applications
    encoding: string, // payload encoding only interpretable by sending/receiving applications
    value: bytes // application-specific data that can be parsed by receiving application given the version and encoding
}

Core Handler Interface Exposed to ICS26 Applications

The IBC core handler MUST expose the following function signature to the ICS26 applications registered on the port router, so that the application can send packets.

SendPacket

SendPacket Inputs: payloads: Payload: This is the payload that the application wishes to send to an application on the receiver chain. sourceClientId: bytes: Identifier of the receiver chain client that exists on the sending chain. timeoutTimestamp: uint64: The timeout in UNIX seconds after which the packet is no longer receivable on the receiving chain. NOTE: This timestamp is evaluated against the receiving chain clock as there may be drift between the sending chain and receiving chain clocks SendPacket Preconditions:
  • The application is registered on the port router with payload.SourcePortId
  • The application MUST have successfully conducted any application specific logic necessary for sending the given payload.
  • The sending client exists for sourceClientId
SendPacket Postconditions:
  • The following packet gets committed and stored under the packet commitment path as specified by ICS24:
interface Packet {
    sourceClientId: sourceClientId,
    destClientId: getCounterparty(sourceClientId).ClientId,  // destClientId should be filled in with the registered counterparty id for provided sourceClientId
    sequence: generateUniqueSequence(sourceClientId),
    timeoutTimestamp: msg.timeoutTimestamp
    data: msg.Payloads
}
  • The sequence is returned to the ICS26 application
SendPacket ErrorConditions:
  • The sending client is invalid (expired or frozen)
  • The provided timeoutTimstamp has already elapsed
  • The sending application is not allowed to send the provided payload to the requested receiving application as identified by payload.DestPort
NOTE: IBC v2 allows multiple payloads coming from multiple applications to be sent in the same packet. If an implementation chooses to support this feature, they may either provide an entrypoint in the core handler to send multiple packets, which must then call each individual application OnSendPacket callback to validate their individual payload and do application-specific sending logic; or they may queue the payloads coming from each application until the packet is ready to be committed.

WriteAcknowledgement

The IBC core handler MAY expose the following function signature to the ICS26 applications registed on the port router, so that the application can write acknowledgements asynchronously. This is only necessary if the implementation supports processing packets asynchronously. In this case, an application may process the packet asynchronously from when the IBC core handler receives the packet. Thus, the acknowledgement cannot be returned as part of the OnRecvPacket callback and must be submitted to the core IBC handler by the ICS26 application at a later time. Thus, we must introduce a new endpoint on the IBC handler for the ICS26 application to call when it is done processing a receive packet and wants to write the acknowledgement. WriteAcknowledgement Inputs: destClientId: bytes: Identifier of the sender chain client that exist on the receiving chain (i.e. executing chain) sequence: uint64: Unique sequence identifying the packet from sending chain to receiving chain ack: bytes: Acknowledgement from the receiving application for the payload it was sent by the application. If the receive was unsuccessful, the ack must be the SENTINEL_ERROR_ACKNOWLEDGEMENT, otherwise it may be some application-specific data. WriteAcknowledgement Preconditions:
  • A packet receipt is stored under the specified ICS24 with the destClientId and sequence
  • An acknowledgement for the destClientId and sequence has not already been written under the ICS24 path
WriteAcknowledgement Postconditions:
  • The acknowledgement is committed and written to the acknowledgement path as specified in ICS24
  • If the acknowledgement is successful, then all receiving applications must have executed their recvPacket logic and written state
  • If the acknowledgement is unsuccessful (ie ERROR ACK), any state changes made by the receiving applications MUST all be reverted. This ensure atomic execution of the multi-payload packet.
NOTE: In the case that the packet contained multiple payloads, the IBC core handler MUST wait for all applications to return their individual acknowledgements for the packet before commiting the acknowledgment. If ANY application returns the error acknowledgement, then the acknowledgement for the entire packet only contains the ERROR_SENTINEL_ACKNOWLEDGEMENT. Otherwise, the acknowledgment is a list containing each applications individual acknowledgment in the same order that their associated payload existed in the packet.

ICS26 Interface Exposed to Core Handler

Modules must expose the following function signatures to the routing module, which are called upon the receipt of various datagrams:

OnRecvPacket

OnRecvPacket Inputs: sourceClientId: bytes: This is the identifier of the client on the sending chain. NOTE: This is an identifier on the counterparty chain provided as information for the application, but it should not be treated as a unique identifier on the receiving chain. destClientId: bytes: This is the identifier of the receiving chain (i.e. executing chain) sequence: uint64: This is the unique sequence for the packet in the stream of packets from sending chain to destination chain. The tuple (destClientId, sequence) uniquely identifies the packet on this chain. payload: Payload. This is the payload that an application registered by payload.SourcePort on the sending chain sends to the executing application OnRecvPacket Preconditions:
  • The application is registered on the port router with payload.DestPort
  • The destination client exists for destClientId
  • All IBC/TAO verification checks have already been authenticated by IBC core handler. Thus, when the application receives a packet; it can be guaranteed of its authenticity and need only perform the relevant application logic for the given payload.
OnRecvPacket Postconditions:
  • The application has executed all app-specific logic for the given payload and made the appropriate state changes
  • The application returns an app acknowledgment ack: bytes to the core IBC handler to be written as an acknowledgement of the payload in this packet.
OnRecvPacket ErrorConditions:
  • The sending application as identified by payload.SourcePortId is not allowed to send a payload to the receiving application
  • The requested version as identified by payload.Version is unsupported
  • The requested encoding as identified by payload.Encoding is unsupported
  • An error occured while processing the payload.Value after decoding with payload.Encoding and processing the payload in the manner expected by payload.Version.
IMPORTANT: If the OnRecvPacket callback errors for any reason, the state changes made during the callback MUST be reverted and the IBC core handler MUST write the SENTINEL_ERROR_ACKNOWLEDGEMENT for this packet even if other payloads in the packet are received successfully.

OnAcknowledgePacket

OnAcknowledgePacket Inputs: sourceClientId: bytes: This is the identifier of the client on the sending chain (i.e. executing chain). destClientId: bytes: This is the identifier of the receiving chain. NOTE: This is an identifier on the counterparty chain provided as information for the application, but it should not be treated as a unique identifier on the receiving chain. sequence: uint64: This is the unique sequence for the packet in the stream of packets from sending chain to destination chain. The tuple (sourceClientId, sequence) uniquely identifies the packet on this chain. acknowledgement: bytes: This is the acknowledgement that the receiving application sent for the payload that we previously sent. It may be a successful acknowledgement with app-specific information or it may be the SENTINEL_ERROR_ACKNOWLEDGEMENT in which case we should handle any app-specific logic needed for a packet that failed to be sent. payload: Payload: This is the original payload that we previously sent OnAcknowledgementPreconditions:
  • This application had previously sent the provided payload in a packet with the provided sourceClientId and sequence.
  • All IBC/TAO verification checks have already been authenticated by IBC core handler. Thus, when the application receives an acknowledgement; it can be guaranteed of its authenticity and need only perform the relevant application logic for the given acknowledgement and payload.
OnAcknowledgement Postconditions:
  • The application has executed all app-specific logic for the given payload and acknowledgment and made the appropriate state changes
  • If the acknowledgement was the SENTINEL_ERROR_ACKNOWLEDGEMENT, this will usually involve reverting whatever application state changes were made during SendPacket (e.g. unescrowing tokens for transfer)
OnAcknowledgement Errorconditions:
  • Application specific errors may occur while processing the acknowledgement. The packet lifecycle is already complete. Implementations MAY choose to allow retries or not.

OnTimeoutPacket

OnTimeoutPacket Inputs: sourceClientId: bytes: This is the identifier of the client on the sending chain (i.e. executing chain). destClientId: bytes: This is the identifier of the receiving chain. NOTE: This is an identifier on the counterparty chain provided as information for the application, but it should not be treated as a unique identifier on the receiving chain. sequence: uint64: This is the unique sequence for the packet in the stream of packets from sending chain to destination chain. The tuple (sourceClientId, sequence) uniquely identifies the packet on this chain. payload: Payload: This is the original payload that we previously sent OnTimeoutPacket Preconditions:
  • This application had previously sent the provided payload in a packet with the provided sourceClientId and sequence.
  • All IBC/TAO verification checks have already been authenticated by IBC core handler. Thus, when the application receives an timeout; it can be guaranteed of its authenticity and need only perform the relevant application timeout logic for the given payload.
OnTimeoutPacket Postconditions:
  • The application has executed all app-specific logic for the given payload and made the appropriate state changes. This will usually involve reverting whatever application state changes were made during SendPacket (e.g. unescrowing tokens for transfer)
OnTimeoutPacket Errorconditions:
  • Application specific errors may occur while processing the timeout. The packet lifecycle is already complete. Implementations MAY choose to allow retries or not.