变更记录
- 26-11-2020:初始草案
- 26-05-2023:在 02-client 重构以及 Strangelove 重新实现后更新
- 13-12-2023:在模块上游合并到 ibc-go 后更新
状态
已接受,并已在 08-wasm 的 v0.1.0 中应用摘要
在 Cosmos SDK 中,轻客户端当前是以 Go 代码硬编码实现的。这使得升级现有 IBC 轻客户端,或为新的轻客户端添加支持,都变成一个涉及链上治理的多步骤流程,既耗时又低效。 为了解决这个问题,我们提议使用一个 Wasm VM 来承载轻客户端字节码,从而能够更容易地升级现有 IBC 轻客户端,并在不需要发布代码版本和对应硬分叉事件的情况下,为新的 IBC 轻客户端添加支持。背景
目前在 ibc-go 中,轻客户端被定义为代码库的一部分,并作为modules/light-clients 下的模块实现。无论是为新的轻客户端添加支持,还是在发生安全问题或共识升级时更新现有轻客户端,都是一个多步骤流程,既耗时又容易出错。为了启用新的 IBC 轻客户端实现,需要修改 ibc-go 的代码库(如果该轻客户端属于其代码库的一部分)、重新构建各链的二进制文件、通过治理提案,并由验证者升级他们的节点。
由上述流程带来的另一个问题是,如果某条链想升级自身共识,它需要说服所有与其连接的链或枢纽同步升级对应的轻客户端,才能保持连接。由于升级轻客户端所需流程较为耗时,一条拥有大量连接的链在升级其共识后,可能需要在相当长一段时间内处于断连状态,这在时间和精力成本上都可能非常高昂。
我们提议通过集成一个 Wasm 轻客户端模块来简化这一工作流,使得为新轻客户端添加支持只需一次受治理控制的简单交易。使用可编译为 Wasm 的 Rust 编写的轻客户端字节码,会在 Wasm VM 中运行。Wasm 轻客户端子模块暴露出一个代理轻客户端接口,将传入消息路由到 Wasm VM 内部相应的处理函数执行。
有了 Wasm 轻客户端模块,任何人都可以以 Wasm 字节码的形式添加新的 IBC 轻客户端(前提是他们能够提交治理提案交易且提案获得通过),也可以使用任何已创建的客户端类型实例化客户端。这使得任何链都可以在其他链上更新自己的轻客户端,而无需经历上述步骤。
决策
我们决定将 Wasm 轻客户端模块实现为一个轻客户端代理,由它与实际上传为 Wasm 字节码的轻客户端交互。为了启用 Wasm 轻客户端模块,用户需要通过更新 core IBC 中 02-client 子模块的AllowedClients 参数,将其加入允许的客户端列表。
sdk.Msg 的治理 v1 提案。所需消息为 MsgStoreCode,字节码通过 wasm_byte_code 字段提供:
MsgStoreCode 的 RPC 处理器会确保消息签名者与被授权提交该消息的地址一致(通常就是治理模块的地址)。
checksums 的条目存储在状态中,该条目包含所有已存储字节码的校验和。
轻客户端代理如何工作?
轻客户端代理在底层会调用一个 CosmWasm 智能合约实例,并将传入参数连同适当的环境信息一起序列化为 JSON 格式。智能合约返回的数据会被反序列化后返回给调用方。 以ClientState 接口中的 VerifyClientMessage 函数为例。传入参数会被封装进一个 payload 对象,然后序列化为 JSON 并传递给 queryContract,后者执行 WasmVm.Query 并返回智能合约返回的字节切片。该数据随后会被反序列化,并作为返回参数传出。
全局 Wasm VM 变量
08-wasm keeper 结构体会保留一个对 Wasm VM 的引用,该 VM 在 keeper 构造函数中完成实例化。keeper 使用 Wasm VM 来存储轻客户端合约的字节码。不过,08-wasm 中对ClientState 接口某些函数的实现,同样需要 Wasm VM 来初始化合约、对合约执行调用以及查询合约。由于这些 ClientState 函数无法访问 08-wasm keeper,因此决定保留一个全局指针变量,使其指向与 08-wasm keeper 中相同的实例。随后,这个全局指针变量会在 ClientState 函数的实现中使用。
影响
正面影响
- 为新的轻客户端添加支持或升级现有轻客户端比以前容易得多,只需要一次交易,而不再需要硬分叉。
- 提高了 ibc-go 的可维护性,因为支持新客户端或升级客户端都不需要修改代码库。
- 轻客户端可以使用 Rust 依赖,而这些依赖在 Go 中可能并不存在相应支持。
负面影响
- 用 Rust 编写的轻客户端必须使用能够编译为 Wasm 的 Rust 子集来实现。
- 轻客户端代码较难分析,因为区块链上只存在编译后的字节码。
Changelog
- 26-11-2020: Initial Draft
- 26-05-2023: Update after 02-client refactor and re-implementation by Strangelove
- 13-12-2023: Update after upstreaming of module to ibc-go
Status
Accepted and applied in v0.1.0 of 08-wasmAbstract
In the Cosmos SDK light clients are currently hardcoded in Go. This makes upgrading existing IBC light clients or adding support for new light client a multi step process involving on-chain governance which is time-consuming. To remedy this, we are proposing a Wasm VM to host light client bytecode, which allows easier upgrading of existing IBC light clients as well as adding support for new IBC light clients without requiring a code release and corresponding hard-fork event.Context
Currently in ibc-go light clients are defined as part of the codebase and are implemented as modules undermodules/light-clients. Adding support for new light clients or updating an existing light client in the event
of a security issue or consensus update is a multi-step process which is both time-consuming and error-prone.
In order to enable new IBC light client implementations it is necessary to modify the codebase of ibc-go (if the light
client is part of its codebase), re-build chains’ binaries, pass a governance proposal and validators upgrade their nodes.
Another problem stemming from the above process is that if a chain wants to upgrade its own consensus, it will
need to convince every chain or hub connected to it to upgrade its light client in order to stay connected. Due
to the time-consuming process required to upgrade a light client, a chain with lots of connections needs to be
disconnected for quite some time after upgrading its consensus, which can be very expensive in terms of time and effort.
We are proposing simplifying this workflow by integrating a Wasm light client module that makes adding support for
new light clients a simple governance-gated transaction. The light client bytecode, written in Wasm-compilable Rust,
runs inside a Wasm VM. The Wasm light client submodule exposes a proxy light client interface that routes incoming
messages to the appropriate handler function, inside the Wasm VM for execution.
With the Wasm light client module, anybody can add new IBC light client in the form of Wasm bytecode (provided they are
able to submit the governance proposal transaction and that it passes) as well as instantiate clients using any created
client type. This allows any chain to update its own light client in other chains without going through the steps outlined above.
Decision
We decided to implement the Wasm light client module as a light client proxy that will interface with the actual light client uploaded as Wasm bytecode. To enable usage of the Wasm light client module, users need to add it to the list of allowed clients by updating theAllowedClients parameter in the 02-client submodule of core IBC.
sdk.Msg for storing
the Wasm contract’s bytecode. The required message is MsgStoreCode and the bytecode is provided in the field wasm_byte_code:
MsgStoreCode will make sure that the signer of the message matches the address of authority allowed to
submit this message (which is normally the address of the governance module).
checksums that contains the checksums for the bytecodes that have been stored.
How light client proxy works?
The light client proxy behind the scenes will call a CosmWasm smart contract instance with incoming arguments serialized in JSON format with appropriate environment information. Data returned by the smart contract is deserialized and returned to the caller. Consider the example of theVerifyClientMessage function of ClientState interface. Incoming arguments are
packaged inside a payload object that is then JSON serialized and passed to queryContract, which executes WasmVm.Query
and returns the slice of bytes returned by the smart contract. This data is deserialized and passed as return argument.
Global Wasm VM variable
The 08-wasm keeper structure keeps a reference to the Wasm VM instantiated in the keeper constructor function. The keeper uses the Wasm VM to store the bytecode of light client contracts. However, the Wasm VM is also needed in the 08-wasm implementations of some of theClientState interface functions to initialise a contract, execute calls on the contract and query the contract. Since
the ClientState functions do not have access to the 08-wasm keeper, then it has been decided to keep a global pointer variable that
points to the same instance as the one in the 08-wasm keeper. This global pointer variable is then used in the implementations of
the ClientState functions.
Consequences
Positive
- Adding support for new light client or upgrading existing light client is way easier than before and only requires single transaction instead of a hard-fork.
- Improves maintainability of ibc-go, since no change in codebase is required to support new client or upgrade it.
- The existence of support for Rust dependencies in light clients which may not exist in Go.
Negative
- Light clients written in Rust need to be written in a subset of Rust which could compile in Wasm.
- Introspecting light client code is difficult as only compiled bytecode exists in the blockchain.