概述
中继算法是 IBC 的“物理”连接层,即链下进程,负责通过扫描各链的状态、构造合适的数据报,并在协议允许的情况下在对端链上执行这些数据报,从而在两条运行 IBC 协议的链之间中继数据。动机
在 IBC 协议中,区块链只能记录向另一条链发送特定数据的意图,但它本身无法直接访问网络传输层。物理数据报中继必须由能够访问 TCP/IP 等传输层的链下基础设施来执行。本标准定义了中继者算法这一概念,可由具备查询链状态能力的链下进程执行,以完成这种中继。定义
中继者是一个链下进程,能够读取并向一组使用 IBC 协议的账本提交交易。期望属性
- IBC 的“恰好一次”或“交付或超时”安全属性不应依赖中继者的行为(假设中继者可能是拜占庭式的)。
- IBC 的数据包中继活性属性应只依赖至少存在一个正确且存活的中继者。
- 中继应当是无需许可的,所有必要的验证都应在链上完成。
- IBC 用户与中继者之间所需的通信应尽可能少。
- 应用层应能够提供中继者激励机制。
技术规范
基本中继算法
该中继算法定义在一组实现 IBC 协议的链C 上。每个中继者未必都能访问跨链网络中的所有链,以读取状态和写入数据报(尤其是在许可链或私有链场景下);不同中继者可能在不同的子集之间执行中继。
pendingDatagrams 根据两条链的状态,计算从一条链中继到另一条链的全部有效数据报集合。中继者必须事先知道其所服务的链集合实现了 IBC 协议的哪些子集(例如通过阅读源代码)。下文给出了一个示例。
submitDatagram 是按链定义的过程(提交某种交易)。数据报既可以作为单独交易逐个提交,也可以在链支持的情况下作为一个交易原子地一起提交。
relay 由中继者定期调用:频率不应高于任一链每个区块一次,也可以更低,具体取决于中继者希望多频繁地执行中继。
不同中继者可以在不同链之间执行中继。只要每一对链之间至少存在一个正确且存活的中继者,并且这些链保持存活,网络中所有在链间流动的数据包最终都会被中继。
数据包、确认、超时
在有序通道中中继数据包
有序通道中的数据包可以采用基于事件的方式或基于查询的方式进行中继。 对于前者,中继者应监视源链上每次发送数据包时发出的事件, 然后根据事件日志中的数据组装该数据包。对于后者,中继者应定期 查询源链上的发送序号,并记录上一次已中继的序号,这样两者之间的任意序号 对应的就是需要查询并中继的数据包。无论采用哪种方式,随后中继进程 都应通过检查接收序号来确认目标链尚未接收到该数据包,然后再执行中继。在无序通道中中继数据包
无序通道中的数据包可以采用基于事件的方式进行中继。 中继者应监视源链上每次发送数据包时发出的事件, 然后根据事件日志中的数据组装该数据包。随后, 中继者应通过查询该数据包序号位置是否存在确认, 来检查目标链是否已经接收过该数据包;如果尚未存在确认, 中继者就应中继该数据包。中继确认
确认可以采用基于事件的方式进行中继。中继者应 监视目标链上每次接收数据包并写入确认时发出的事件, 然后根据事件日志中的数据组装确认, 检查源链上的数据包承诺是否仍然存在(该承诺会在 确认被中继后删除),如果存在,则将该确认中继到 源链。中继超时(普通情况,无 TIMEOUT 回执)
超时中继稍微更复杂一些,因为当 数据包超时时并不会发出特定事件;它只是变成无法继续被中继的状态, 因为目标链上的超时高度或时间戳已经过去。中继 进程必须选择跟踪一组数据包(可通过扫描事件日志构建), 并在目标链的高度或时间戳一旦超过某个被跟踪 数据包的对应值时,检查该数据包承诺在源链上是否仍然存在(它会 在超时被中继后删除),如果存在,则向源链中继一个超时。为会写入 TIMEOUT 回执的通道中继超时
某些通道类型(例如ORDERED_ALLOW_TIMEOUT)只有在目标链上写入超时回执时,
才能使一个数据包超时。这要求中继者即使在数据包已经超时的情况下,
也必须先尝试在目标链上执行一次接收,之后才能向发送链中继超时。因此,在这些通道上,
中继者必须通过查询数据包回执路径来检查该数据包是否已经在目标链上被接收。
如果对应值尚不存在,则尝试在目标链上接收该数据包。如果写入了超时回执,
则携带该超时回执的证明,将超时中继回发送方链。
待处理中继报文
pendingDatagrams 会汇总需要从一台机器发送到另一台机器的报文。该函数的实现将取决于两台机器共同支持的 IBC 协议子集,以及源机器的状态布局。具体的中继器通常也会希望实现自己的过滤函数,以便只中继理论上可被中继的报文中的一部分(例如,它们通过某种链下方式收取费用后才负责中继的那一部分)。
下面给出了一个在两条链之间执行单向中继的示例实现。通过交换 chain 和 counterparty,它可以改为执行双向中继。
由哪个中继进程负责哪些报文是一个灵活的选择;在这个示例中,中继进程会中继所有从 chain 发起的握手流程(向两条链都发送报文),中继所有从 chain 发送到 counterparty 的数据包,以及中继所有从 counterparty 发送到 chain 的数据包确认。
顺序约束
中继进程隐含地受到顺序约束,这决定了哪些报文必须按什么顺序提交。例如,在一个数据包能够被中继之前,必须先向轻客户端提交某个区块高度的头信息,以最终确定该高度已存储的共识状态和承诺根。中继进程有责任频繁查询其所中继的链之间的状态,以确定何时必须中继什么内容。打包
如果宿主状态机支持,中继进程可以将多个报文打包到一笔交易中,这会使它们按顺序执行,并分摊任何额外开销(例如用于支付费用的签名校验)。竞争条件
当多个中继器在同一对模块和链之间进行中继时,它们可能会尝试同时中继同一个数据包(或提交同一个头信息)。如果两个中继器这样做,第一笔交易会成功,第二笔会失败。需要通过中继器之间,或原始数据包发送方与中继器之间的带外协调来缓解这一问题。进一步讨论超出本标准的范围。激励机制
中继进程必须能够访问两条链上的账户,并具有足够余额来支付交易费用。中继器可以采用应用层方法来收回这些费用,例如在数据包数据中给自己附带一笔小额支付;关于中继器费用支付的协议将在本 ICS 的未来版本或单独的 ICS 中描述。 任意数量的中继进程都可以安全地并行运行(而且实际上,预期会由不同的中继器服务跨链网络中的不同子集)。不过,如果它们多次提交相同的证明,可能会消耗不必要的费用,因此进行一些最小化协调可能更理想(例如将特定中继器分配给特定数据包,或扫描内存池中的待处理交易)。向后兼容性
不适用。中继进程位于链下,可以根据需要升级或降级。向前兼容性
不适用。中继进程位于链下,可以根据需要升级或降级。示例实现
- ICS 18 的 Go 实现可见于 cosmos/relayer repository。
- ICS 18 的 Rust 实现可见于 informalsystems/hermes repository。
历史
2019 年 3 月 30 日 - 提交初始草案 2019 年 4 月 15 日 - 针对格式和清晰度进行了修订 2019 年 4 月 23 日 - 根据评论修订;草案已合并版权
本文所有内容均基于 Apache 2.0 许可证授权。Synopsis
Relayer algorithms are the “physical” connection layer of IBC — off-chain processes responsible for relaying data between two chains running the IBC protocol by scanning the state of each chain, constructing appropriate datagrams, and executing them on the opposite chain as allowed by the protocol.Motivation
In the IBC protocol, a blockchain can only record the intention to send particular data to another chain — it does not have direct access to a network transport layer. Physical datagram relay must be performed by off-chain infrastructure with access to a transport layer such as TCP/IP. This standard defines the concept of a relayer algorithm, executable by an off-chain process with the ability to query chain state, to perform this relay.Definitions
A relayer is an off-chain process with the ability to read the state of and submit transactions to some set of ledgers utilising the IBC protocol.Desired Properties
- No exactly-once or deliver-or-timeout safety properties of IBC should depend on relayer behaviour (assume Byzantine relayers).
- Packet relay liveness properties of IBC should depend only on the existence of at least one correct, live relayer.
- Relaying should be permissionless, all requisite verification should be performed on-chain.
- Requisite communication between the IBC user and the relayer should be minimised.
- Provision for relayer incentivisation should be possible at the application layer.
Technical Specification
Basic relayer algorithm
The relayer algorithm is defined over a setC of chains implementing the IBC protocol. Each relayer may not necessarily have access to read state from and write datagrams to all chains in the interchain network (especially in the case of permissioned or private chains) — different relayers may relay between different subsets.
pendingDatagrams calculates the set of all valid datagrams to be relayed from one chain to another based on the state of both chains. The relayer must possess prior knowledge of what subset of the IBC protocol is implemented by the blockchains in the set for which they are relaying (e.g. by reading the source code). An example is defined below.
submitDatagram is a procedure defined per-chain (submitting a transaction of some sort). Datagrams can be submitted individually as single transactions or atomically as a single transaction if the chain supports it.
relay is called by the relayer every so often — no more frequently than once per block on either chain, and possibly less frequently, according to how often the relayer wishes to relay.
Different relayers may relay between different chains — as long as each pair of chains has at least one correct & live relayer and the chains remain live, all packets flowing between chains in the network will eventually be relayed.
Packets, acknowledgements, timeouts
Relaying packets in an ordered channel
Packets in an ordered channel can be relayed in either an event-based fashion or a query-based fashion. For the former, the relayer should watch the source chain for events emitted whenever packets are sent, then compose the packet using the data in the event log. For the latter, the relayer should periodically query the send sequence on the source chain, and keep the last sequence number relayed, so that any sequences in between the two are packets that need to be queried & then relayed. In either case, subsequently, the relayer process should check that the destination chain has not yet received the packet by checking the receive sequence, and then relay it.Relaying packets in an unordered channel
Packets in an unordered channel can be relayed in an event-based fashion. The relayer should watch the source chain for events emitted whenever packets are sent, then compose the packet using the data in the event log. Subsequently, the relayer should check whether the destination chain has received the packet already by querying for the presence of an acknowledgement at the packet’s sequence number, and if one is not yet present the relayer should relay the packet.Relaying acknowledgements
Acknowledgements can be relayed in an event-based fashion. The relayer should watch the destination chain for events emitted whenever packets are received & acknowledgements are written, then compose the acknowledgement using the data in the event log, check whether the packet commitment still exists on the source chain (it will be deleted once the acknowledgement is relayed), and if so relay the acknowledgement to the source chain.Relaying timeouts (ordinary case, no TIMEOUT receipt)
Timeout relay is slightly more complex since there is no specific event emitted when a packet times-out - it is simply the case that the packet can no longer be relayed, since the timeout height or timestamp has passed on the destination chain. The relayer process must elect to track a set of packets (which can be constructed by scanning event logs), and as soon as the height or timestamp of the destination chain exceeds that of a tracked packet, check whether the packet commitment still exists on the source chain (it will be deleted once the timeout is relayed), and if so relay a timeout to the source chain.Relaying timeouts for channels that write TIMEOUT receipts
Some channel types (e.g. ORDERED_ALLOW_TIMEOUT), can only timeout a packet if a timeout receipt is written on the destination chain. This requires a relayer to first attempt a receive on the destination chain even if the packet is already timed out, before they can relay a timeout to the sending chain. Thus on these channels, relayers must check if packet has already been received on the destination chain by querying the packet receipt path. If a value does not already exist, then attempt to receive the packet on the destination chain. If a timeout receipt is written, then relay the timeout with a proof of the timeout receipt back to the sender chain.Pending datagrams
pendingDatagrams collates datagrams to be sent from one machine to another. The implementation of this function will depend on the subset of the IBC protocol supported by both machines & the state layout of the source machine. Particular relayers will likely also want to implement their own filter functions in order to relay only a subset of the datagrams which could possibly be relayed (e.g. the subset for which they have been paid to relay in some off-chain manner).
An example implementation which performs unidirectional relay between two chains is outlined below. It can be altered to perform bidirectional relay by switching chain and counterparty.
Which relayer process is responsible for which datagrams is a flexible choice - in this example, the relayer process relays all handshakes which started on chain (sending datagrams to both chains), relays all packets sent from chain to counterparty, and relays all acknowledgements of packets sent from counterparty to chain.
Ordering constraints
There are implicit ordering constraints imposed on the relayer process determining which datagrams must be submitted in what order. For example, a header must be submitted to finalise the stored consensus state & commitment root for a particular height in a light client before a packet can be relayed. The relayer process is responsible for frequently querying the state of the chains between which they are relaying in order to determine what must be relayed when.Bundling
If the host state machine supports it, the relayer process can bundle many datagrams into a single transaction, which will cause them to be executed in sequence, and amortise any overhead costs (e.g. signature checks for fee payment).Race conditions
Multiple relayers relaying between the same pair of modules & chains may attempt to relay the same packet (or submit the same header) at the same time. If two relayers do so, the first transaction will succeed and the second will fail. Out-of-band coordination between the relayers or between the actors who sent the original packets and the relayers is necessary to mitigate this. Further discussion is out of scope of this standard.Incentivisation
The relay process must have access to accounts on both chains with sufficient balance to pay for transaction fees. Relayers may employ application-level methods to recoup these fees, such as by including a small payment to themselves in the packet data — protocols for relayer fee payment will be described in future versions of this ICS or in separate ICSs. Any number of relayer processes may be safely run in parallel (and indeed, it is expected that separate relayers will serve separate subsets of the interchain). However, they may consume unnecessary fees if they submit the same proof multiple times, so some minimal coordination may be ideal (such as assigning particular relayers to particular packets or scanning mempools for pending transactions).Backwards Compatibility
Not applicable. The relayer process is off-chain and can be upgraded or downgraded as necessary.Forwards Compatibility
Not applicable. The relayer process is off-chain and can be upgraded or downgraded as necessary.Example Implementations
- Implementation of ICS 18 in Go can be found in cosmos/relayer repository.
- Implementation of ICS 18 in Rust can be found in informalsystems/hermes repository.