概要
本文档标准规定了独立链之间通过 IBC 通道进行账户管理系统的数据包结构、状态机处理逻辑以及编码细节。背景动机
ICS-27 Interchain Accounts 描述了一种构建在 IBC 之上的跨链账户管理协议。支持 ICS-27 的链可以在其他支持 ICS-27 的链上以编程方式创建账户,并通过 IBC 交易控制这些账户(而不是使用私钥签名)。链间账户保留了普通账户的全部能力(例如质押、转账、投票),但它们改为由另一条链通过 IBC 进行管理,从而使控制链上的所有者账户始终能够完全控制其在主机链上注册的任意链间账户。定义
Host Chain:注册链间账户的链。主机链会监听来自控制链的 IBC 数据包,其中包含该链间账户将要执行的指令(例如 cosmos SDK 消息)。Controller Chain:在主机链上注册并控制账户的链。控制链通过向主机链发送 IBC 数据包来控制该账户。Interchain Account:主机链上的一个账户。链间账户具备普通账户的全部能力。但是,它不通过私钥签名交易,而是由控制链向主机链发送 IBC 数据包,以指示该链间账户必须执行哪些交易。Interchain Account Owner:控制链上的一个账户。主机链上的每个链间账户,在控制链上都有一个对应的所有者账户。
期望属性
- 无许可:任何参与方都可以创建链间账户,而无需第三方批准(例如链治理)。注意:具体实现可以自行加入权限控制方案,但协议本身不能依赖受信任第三方的许可机制来保证安全。
- 故障隔离:控制链不得控制由其他控制链注册的账户。例如,在控制链遭受分叉攻击时,只有由该分叉链注册的链间账户会受到影响。
- 发送到主机链上某个链间账户的交易顺序必须被保持。交易必须按照控制链发送的顺序由链间账户执行。
- 如果通道关闭,控制链必须能够仅通过打开新通道来重新获得对已注册链间账户的访问能力。
- 每个链间账户都归属于控制链上的单一账户。只有控制链上的所有者账户才有权控制该链间账户。控制链负责强制执行这一逻辑。
- 控制链必须存储其在主机链上注册并拥有的所有链间账户地址。
- 主机链必须能够按需限制链间账户在本链上的功能(例如,主机链可以决定在该链上注册的链间账户不能参与质押)。
技术规范
通用设计
一条链可以使用链间账户协议的一个部分或两个部分(控制 与 托管)。在其他主机链上注册账户的控制链(前提是这些主机链支持链间账户)并不一定需要允许其他控制链在本链上注册账户,反之亦然。 本规范定义了注册链间账户以及发送代表所有者账户执行的交易字节的通用方式。主机链负责反序列化并执行这些交易字节,而控制链在发送数据包之前必须预先知道主机链将如何处理这些交易字节,因此这必须在通道创建期间协商完成。控制链合约
RegisterInterchainAccount
RegisterInterchainAccount 是注册链间账户的入口。
它使用所有者账户地址生成新的 controller portID。
它会绑定到该 controller portID,并调用 04-channel ChanOpenInit。
如果该 controller portID 已被占用,则返回错误。
会发出一个 ChannelOpenInit 事件,供链下进程(例如 relayer)捕获。
该账户将在主机链的 OnChanOpenTry 步骤中完成注册。
此函数必须在给定 connection identifier 已建立 OPEN 连接后调用。
调用方必须提供完整的通道版本。该版本必须包含带完整元数据的 ICA 版本,也可以包含通道两端封装 ICA 的其他中间件版本。请注意,这将要求了解通道两端启用了哪些中间件的上下文信息。因此,建议 ICA-auth 应用自动构造 ICA 版本,并允许用户按需启用额外的中间件版本信息。
SendTx
SendTx 用于向主机链上的链间账户发送一个包含指令(消息)的 IBC 数据包,适用于给定的链间账户所有者。
主机链合约
RegisterInterchainAccount
RegisterInterchainAccount 会在通道创建握手期间的 OnChanOpenTry 步骤中被调用。
AuthenticateTx
AuthenticateTx 会在 ExecuteTx 之前调用。
AuthenticateTx 会检查某条消息的签名者,是否为发送该 IBC 数据包所经过通道的 counterparty portID 对应的链间账户。
ExecuteTx
执行由控制链上的所有者账户发送的每条消息。工具函数
注册与控制流程
账户注册流程
要注册链间账户,需要一个链下进程(relayer)监听ChannelOpenInit 事件,并具备在给定连接上完成通道创建握手的能力。
- 控制链使用给定的链间账户所有者地址对应的 controller portID 绑定一个新的 IBC 端口。
{owner-account-address} 对应账户,才有权通过使用该 controller portID 创建的通道发送 IBC 数据包。控制链一侧的端口注册与访问控制由各控制链自行负责强制执行。
- 控制链发出一个事件,表示要基于某个连接在该端口上打开新通道。
- 监听
ChannelOpenInit事件的 relayer 将继续完成通道创建握手。 - 在主机链的
OnChanOpenTry回调期间,将注册一个链间账户,并在状态中保存链间账户地址到所有者账户地址的映射(这用于主机链在执行时对交易进行认证)。 - 在控制链的
OnChanOpenAck回调期间,会将主机链在OnChanOpenTry期间注册的链间账户地址记录到状态中,保存为(controller portID, controller connectionID) -> 链间账户地址的映射。关于如何实现这一点,请参见下文的 metadata negotiation 部分。 - 在控制链和主机链各自的
OnChanOpenAck与OnChanOpenConfirm回调期间,会将该链间账户/所有者对的 active-channel 写入状态。
活跃通道
控制链和宿主链必须为每个已注册的跨链账户维护一个active-channel。active-channel 会在通道创建握手过程中设置。这是一种安全机制,用于在通道关闭时,让控制链能够重新获得对宿主链上跨链账户的访问权限。
控制链上的活跃通道示例如下:
{owner-account-address})和 connectionID 组合重新打开新的 ICS-27 通道并重置活跃通道的能力。
控制链和宿主链必须验证任何新通道是否保持与先前活跃通道相同的元数据,以确保即使替换活跃通道,跨链账户的参数也保持不变。元数据中的 Address 不应被校验,因为在 INIT 阶段它预期为空;宿主链会在 TRY 阶段重新生成完全相同的地址,因为它预期会基于控制链 portID 和 connectionID 以确定性方式生成跨链账户地址(这两者都必须保持不变)。
元数据协商
ICS-27 利用 ICS-04 通道版本协商,在通道握手期间协商元数据和通道参数。元数据将包含编码格式以及交易类型,以便对手方就跨链交易的结构和编码达成一致。宿主链在 TRY 步骤发送的元数据还会包含跨链账户地址,以便将其中继回控制链。在通道握手结束时,控制链和宿主链都会存储一个从(控制链 portID、控制链/宿主链 connectionID)到新注册的跨链账户地址的映射(账户注册流程)。 ICS-04 允许每次通道版本协商都由应用自行定义。对于跨链账户,通道版本将是一个 JSON 结构体的字符串,其中包含所有相关元数据,用于在通道握手阶段中继给对手方(见下方总结)。 结合每个跨链账户仅对应一个通道的做法,这种元数据协商方式使我们能够在OnChanOpenAck 回调期间,将跨链账户地址传回控制链,并创建从(控制链 portID、控制链 connection ID)到跨链账户地址的映射。正如控制流程中所述,控制链需要知道已注册跨链账户的地址,才能向宿主链上的该账户发送交易。
元数据协商总结
interchain-account-address 是控制链在宿主链上注册的跨链账户地址。
- INIT
- TRY
- ACK
控制流程
一旦跨链账户在宿主链上注册完成,控制链就可以开始向宿主链发送指令(消息)来控制该账户。- 控制链调用
SendTx,并传入将由关联跨链账户在宿主侧执行的消息(由控制侧端口标识符确定)
-
宿主链在接收到 IBC 数据包后,将调用
DeserializeTx。 -
宿主链随后会对每条消息调用
AuthenticateTx和ExecuteTx,并返回一个包含成功或错误信息的确认。
GetInterchainAccountAddress(controllerPortId, hostConnectionId),以得到当前控制端口和连接标识符对应的预期跨链账户地址。如果该消息的签名者与预期账户地址不匹配,则认证失败。
数据包数据
InterchainAccountPacketData 包含跨链账户可执行的消息数组、发送到宿主链的 memo 字符串,以及数据包 type。ICS-27 第 1 版只有一种类型:EXECUTE_TX。
自定义逻辑
ICS-27 依赖 ICS-30 中间件架构,为应用开发者提供在 ICS-27 数据包成功或失败时应用自定义逻辑的能力。 控制链会包装OnAcknowledgementPacket 和 OnTimeoutPacket,以处理 ICS-27 数据包的成功或失败场景。
端口与通道设置
宿主链上的跨链账户模块必须始终绑定到 id 为icahost 的端口。控制链则会按照标识符格式一节中的说明动态绑定端口。
下面的示例假设某个模块实现了完整的 InterchainAccountModule 接口。setup 函数必须在模块创建时且仅调用一次(例如在区块链自身初始化时),以绑定到合适的端口。
setup 函数后,即可通过 IBC 路由模块创建通道。
通道生命周期管理
当且仅当通道初始化步骤是由控制链发起调用时,跨链账户模块才会接受来自另一台机器上任意模块的新通道。关闭握手
升级握手
数据包中继
当发送到该模块的数据包被接收时,路由模块会调用onRecvPacket。
onAcknowledgePacket。
OnRecvPacket 回调;如果它被调用,也应仅返回一个错误确认:
标识符格式
以下是跨链账户通道两侧端口标识符的默认格式。控制器端口 ID 必须 包含所有者地址,这样当消息发送到控制器模块时,就可以在发送 ICA 数据包之前,根据 portID 校验消息发送者。控制器链负责实现适当的访问控制,以确保 ICA 消息的发送者在消息到达控制器模块之前已经成功完成身份验证。 控制器端口标识符:可选前缀icacontroller- + 必选 {owner-account-address}
主机端口标识符:icahost
控制器端口标识符上的 icacontroller- 前缀是可选的,主机链不得强制要求对手方端口标识符包含该前缀。控制器链可以自行决定是否包含该前缀,并校验其自身端口标识符中是否存在该前缀。
实现示例
- 可在 ibc-go 仓库中找到 ICS 27 的 Go 实现。
未来改进
未来版本的跨链账户可以通过引入一种 IBC 通道类型而大幅简化:该通道是 ORDERED,但在超时时不会关闭通道,而是继续接受并接收下一个数据包。如果核心 IBC 提供了这种通道类型,跨链账户就可以要求使用该通道类型,并移除所有与“活跃通道”相关的逻辑和状态。元数据格式也可以简化,删除对底层连接标识符的所有引用。 当前之所以需要设置和取消设置“活跃通道”,是为了允许跨链账户所有者在当前活跃通道因通道超时而关闭时创建新通道。连接标识符之所以是元数据的一部分,是为了确保任何新打开的通道都建立在原始连接之上。一旦通道既是有序的 并且 不可关闭,这些逻辑就都不再必要,而这只有通过在核心 IBC 中引入一种新的通道类型才能实现。历史
2019 年 8 月 1 日 - 概念讨论 2019 年 9 月 24 日 - 提出草案 2019 年 11 月 8 日 - 重大修订 2019 年 12 月 2 日 - 小幅修订(添加更具体的描述,并添加以太坊上的跨链账户) 2020 年 7 月 14 日 - 重大修订 2021 年 4 月 27 日 - 重新设计 ics27 规范 2021 年 11 月 11 日 - 根据实现的最新变更进行更新 2021 年 12 月 14 日 - 基于审计和维护者评审对规范进行修订 2023 年 8 月 1 日 - 实现通道升级回调版权
此处所有内容均基于 Apache 2.0 许可证授权。Synopsis
This standard document specifies packet data structure, state machine handling logic, and encoding details for the account management system over an IBC channel between separate chains.Motivation
ICS-27 Interchain Accounts outlines a cross-chain account management protocol built upon IBC. ICS-27 enabled chains can programmatically create accounts on other ICS-27 enabled chains & control these accounts via IBC transactions (instead of signing with a private key). Interchain accounts retain all of the capabilities of a normal account (i.e. stake, send, vote) but instead are managed by a separate chain via IBC in a way such that the owner account on the controller chain retains full control over any interchain account(s) it registers on host chain(s).Definitions
Host Chain: The chain where the interchain account is registered. The host chain listens for IBC packets from a controller chain which contain instructions (e.g. cosmos SDK messages) that the interchain account will execute.Controller Chain: The chain registering and controlling an account on a host chain. The controller chain sends IBC packets to the host chain to control the account.Interchain Account: An account on a host chain. An interchain account has all the capabilities of a normal account. However, rather than signing transactions with a private key, a controller chain will send IBC packets to the host chain which signals what transactions the interchain account must execute.Interchain Account Owner: An account on the controller chain. Every interchain account on a host chain has a respective owner account on the controller chain.
Desired properties
- Permissionless: An interchain account may be created by any actor without the approval of a third party (e.g. chain governance). Note: Individual implementations may implement their own permissioning scheme, however the protocol must not require permissioning from a trusted party to be secure.
- Fault isolation: A controller chain must not be able to control accounts registered by other controller chains. For example, in the case of a fork attack on a controller chain, only the interchain accounts registered by the forked chain will be vulnerable.
- The ordering of transactions sent to an interchain account on a host chain must be maintained. Transactions must be executed by an interchain account in the order in which they are sent by the controller chain.
- If a channel closes, the controller chain must be able to regain access to registered interchain accounts by simply opening a new channel.
- Each interchain account is owned by a single account on the controller chain. Only the owner account on the controller chain is authorized to control the interchain account. The controller chain is responsible for enforcing this logic.
- The controller chain must store the account address of any owned interchain accounts registered on host chains.
- A host chain must have the ability to limit interchain account functionality on its chain as necessary (e.g. a host chain can decide that interchain accounts registered on the host chain cannot take part in staking).
Technical specification
General design
A chain can utilize one or both parts of the interchain accounts protocol (controlling and hosting). A controller chain that registers accounts on other host chains (that support interchain accounts) does not necessarily have to allow other controller chains to register accounts on its chain, and vice versa. This specification defines the general way to register an interchain account and send tx bytes to be executed on behalf of the owner account. The host chain is responsible for deserializing and executing the tx bytes and the controller chain must know how the host chain will handle the tx bytes in advance of sending a packet, thus this must be negotiated during channel creation.Controller chain contract
RegisterInterchainAccount
RegisterInterchainAccount is the entry point to registering an interchain account.
It generates a new controller portID using the owner account address.
It will bind to the controller portID and
call 04-channel ChanOpenInit. An error is returned if the controller portID is already in use.
A ChannelOpenInit event is emitted which can be picked up by an offchain process such as a relayer.
The account will be registered during the OnChanOpenTry step on the host chain.
This function must be called after an OPEN connection is already established with the given connection identifier.
The caller must provide the complete channel version. This MUST include the ICA version with complete metadata and it MAY include
versions of other middleware that is wrapping ICA on both sides of the channel. Note this will require contextual information
on what middleware is enabled on either end of the channel. Thus it is recommended that an ICA-auth application construct the ICA
version automatically and allow for users to optionally enable additional middleware versioning.
SendTx
SendTx is used to send an IBC packet containing instructions (messages) to an interchain account on a host chain for a given interchain account owner.
Host chain contract
RegisterInterchainAccount
RegisterInterchainAccount is called on the OnChanOpenTry step during the channel creation handshake.
AuthenticateTx
AuthenticateTx is called before ExecuteTx.
AuthenticateTx checks that the signer of a particular message is the interchain account associated with the counterparty portID of the channel that the IBC packet was sent on.
ExecuteTx
Executes each message sent by the owner account on the controller chain.Utility functions
Register & controlling flows
Register account flow
To register an interchain account we require an off-chain process (relayer) to listen forChannelOpenInit events with the capability to finish a channel creation handshake on a given connection.
- The controller chain binds a new IBC port with the controller portID for a given interchain account owner address.
{owner-account-address} matching the bound port will be authorized to send IBC packets over channels created with the controller portID. It is up to each controller chain to enforce this port registration and access on the controller side.
- The controller chain emits an event signaling to open a new channel on this port given a connection.
- A relayer listening for
ChannelOpenInitevents will continue the channel creation handshake. - During the
OnChanOpenTrycallback on the host chain an interchain account will be registered and a mapping of the interchain account address to the owner account address will be stored in state (this is used for authenticating transactions on the host chain at execution time). - During the
OnChanOpenAckcallback on the controller chain a record of the interchain account address registered on the host chain duringOnChanOpenTryis set in state with a mapping from (controller portID, controller connectionID) -> interchain account address. See metadata negotiation section below for how to implement this. - During the
OnChanOpenAck&OnChanOpenConfirmcallbacks on the controller & host chains respectively, the active-channel for this interchain account/owner pair, is set in state.
Active channels
The controller and host chain must keep track of anactive-channel for each registered interchain account. The active-channel is set during the channel creation handshake process. This is a safety mechanism that allows a controller chain to regain access to an interchain account on a host chain in case of a channel closing.
An example of an active channel on the controller chain can look like this:
{owner-account-address}) and connectionID pair.
The controller and host chains must verify that any new channel maintains the same metadata as the previous active channel to ensure that the parameters of the interchain account remain the same even after replacing the active channel. The Address of the metadata should not be verified since it is expected to be empty at the INIT stage, and the host chain will regenerate the exact same address on TRY, because it is expected to generate the interchain account address deterministically from the controller portID and connectionID (both of which must remain the same).
Metadata negotiation
ICS-27 takes advantage of ICS-04 channel version negotiation to negotiate metadata and channel parameters during the channel handshake. The metadata will contain the encoding format along with the transaction type so that the counterparties can agree on the structure and encoding of the interchain transactions. The metadata sent from the host chain on the TRY step will also contain the interchain account address, so that it can be relayed to the controller chain. At the end of the channel handshake, both the controller and host chains will store a mapping of (controller chain portID, controller/host connectionID) to the newly registered interchain account address (account registration flow). ICS-04 allows for each channel version negotiation to be application-specific. In the case of interchain accounts, the channel version will be a string of a JSON struct containing all the relevant metadata intended to be relayed to the counterparty during the channel handshake step (see summary below). Combined with the one channel per interchain account approach, this method of metadata negotiation allows us to pass the address of the interchain account back to the controller chain and create a mapping from (controller portID, controller connection ID) -> interchain account address during theOnChanOpenAck callback. As outlined in the controlling flow, a controller chain will need to know the address of a registered interchain account in order to send transactions to the account on the host chain.
Metadata negotiation summary
interchain-account-address is the address of the interchain account registered on the host chain by the controller chain.
- INIT
- TRY
- ACK
Controlling flow
Once an interchain account is registered on the host chain a controller chain can begin sending instructions (messages) to the host chain to control the account.- The controller chain calls
SendTxand passes message(s) that will be executed on the host side by the associated interchain account (determined by the controller side port identifier)
-
The host chain upon receiving the IBC packet will call
DeserializeTx. -
The host chain will then call
AuthenticateTxandExecuteTxfor each message and return an acknowledgment containing a success or error.
GetInterchainAccountAddress(controllerPortId, hostConnectionId) to get the expected interchain account address for the current controller port and connection identifier. If the signer of this message does not match the expected account address then authentication will fail.
Packet Data
InterchainAccountPacketData contains an array of messages that an interchain account can execute and a memo string that is sent to the host chain as well as the packet type. ICS-27 version 1 has only one type EXECUTE_TX.
Custom logic
ICS-27 relies on ICS-30 middleware architecture to provide the option for application developers to apply custom logic on the success or fail of ICS-27 packets. Controller chains will wrapOnAcknowledgementPacket & OnTimeoutPacket to handle the success or fail cases for ICS-27 packets.
Port & channel setup
The interchain account module on a host chain must always bind to a port with the idicahost. Controller chains will bind to ports dynamically, as specified in the identifier format section.
The example below assumes a module is implementing the entire InterchainAccountModule interface. The setup function must be called exactly once when the module is created (perhaps when the blockchain itself is initialized) to bind to the appropriate port.
setup function has been called, channels can be created via the IBC routing module.
Channel lifecycle management
An interchain account module will accept new channels from any module on another machine, if and only if the channel initialization step is being invoked from the controller chain.Closing handshake
Upgrade handshake
Packet relay
onRecvPacket is called by the routing module when a packet addressed to this module has been received.
onAcknowledgePacket is called by the routing module when a packet sent by this module has been acknowledged.
OnRecvPacket callback should not be called, and in case it is called, it should simply return an error acknowledgement:
Identifier formats
These are the default formats that the port identifiers on each side of an interchain accounts channel. The controller portID must include the owner address so that when a message is sent to the controller module, the sender of the message can be verified against the portID before sending the ICA packet. The controller chain is responsible for proper access control to ensure that the sender of the ICA message has successfully authenticated before the message reaches the controller module. Controller Port Identifier: optional prefixicacontroller- + mandatory {owner-account-address}
Host Port Identifier: icahost
The icacontroller- prefix on the controller port identifier is optional and host chains must not enforce that the counterparty port identifier includes it. Controller chains may decide to include it and validate that it is present in their own port identifier.
Example Implementations
- Implementation of ICS 27 in Go can be found in ibc-go repository.