概述

了解如何使用核心 IBC 与 02-client 子模块来配置轻客户端模块并创建客户端。 完成轻客户端开发的最后一步,是实现 AppModuleBasic 接口,以便能够将其与链启用的其他轻客户端类型一起添加到链的 app.go 中。 最后,本文还会简要说明让轻客户端真正投入运行所需的剩余步骤,包括通过治理启用该轻客户端类型以及创建客户端。

配置轻客户端模块

IBC 轻客户端模块必须实现 AppModuleBasic 接口,才能将其具体类型注册到 modules/core/exported 中定义的核心 IBC 接口上。这是通过 RegisterInterfaces 方法完成的,该方法使轻客户端模块能够使用链的 InterfaceRegistry 注册编解码类型。请参考 07-tendermint 编解码器注册。 AppModuleBasic 接口也可用于为轻客户端模块用户安装自定义 CLI 处理器。对于不打算实现的接口方法,轻客户端模块可以安全地采用 no-op 实现。 关于如何在 app.go 中与 07-tendermint 一起配置额外的轻客户端模块,请参考核心 IBC 文档。 下面给出 07-tendermint 对 AppModuleBasic 的实现示例。
var _ module.AppModuleBasic = AppModuleBasic{
}

/ AppModuleBasic defines the basic application module used by the tendermint light client.
/ Only the RegisterInterfaces function needs to be implemented. All other function perform
/ a no-op.
type AppModuleBasic struct{
}

/ Name returns the tendermint module name.
func (AppModuleBasic)

Name()

string {
    return ModuleName
}

/ RegisterLegacyAminoCodec performs a no-op. The Tendermint client does not support amino.
func (AppModuleBasic)

RegisterLegacyAminoCodec(*codec.LegacyAmino) {
}

/ RegisterInterfaces registers module concrete types into protobuf Any. This allows core IBC
/ to unmarshal tendermint light client types.
func (AppModuleBasic)

RegisterInterfaces(registry codectypes.InterfaceRegistry) {
    RegisterInterfaces(registry)
}

/ DefaultGenesis performs a no-op. Genesis is not supported for the tendermint light client.
func (AppModuleBasic)

DefaultGenesis(cdc codec.JSONCodec)

json.RawMessage {
    return nil
}

/ ValidateGenesis performs a no-op. Genesis is not supported for the tendermint light client.
func (AppModuleBasic)

ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage)

error {
    return nil
}

/ RegisterGRPCGatewayRoutes performs a no-op.
func (AppModuleBasic)

RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
}

/ GetTxCmd performs a no-op. Please see the 02-client cli commands.
func (AppModuleBasic)

GetTxCmd() *cobra.Command {
    return nil
}

/ GetQueryCmd performs a no-op. Please see the 02-client cli commands.
func (AppModuleBasic)

GetQueryCmd() *cobra.Command {
    return nil
}

创建客户端

客户端是通过执行一笔新的 MsgCreateClient 交易来创建的,该交易由有效的 ClientState 和初始 ConsensusState 组成,并编码为 protobuf Any。 通常,这一过程由链下流程执行,也就是常说的 IBC relayer,但这并不是硬性要求。 下面列出了一些 IBC relayer 实现: 无状态检查会在 MsgCreateClient 的 ValidateBasic 方法中执行。
/ MsgCreateClient defines a message to create an IBC client
message MsgCreateClient {
  option (gogoproto.goproto_getters) = false;

  / light client state
  google.protobuf.Any client_state = 1 [(gogoproto.moretags) = "yaml:\"client_state\""];
  / consensus state associated with the client that corresponds to a given
  / height.
  google.protobuf.Any consensus_state = 2 [(gogoproto.moretags) = "yaml:\"consensus_state\""];
  / signer address
  string signer = 3;
}
借助 protobuf Any 编码,核心 IBC 可以将 ClientState 解包 为其对应的接口类型,而这些类型此前已通过轻客户端模块的 RegisterInterfaces 方法完成注册。 在 02-client 子模块内部,随后会为 ClientState 进行初始化,并为其分配独立的键值存储空间,该存储空间使用唯一的客户端标识符进行命名空间隔离。 要使用新的客户端类型成功创建 IBC 客户端,该类型必须受到支持。IBC 中的轻客户端支持由链上治理控制。可以通过提交新的治理提案来更新 02-client 参数 AllowedClients,从而修改允许列表。 示例如下:
%s tx gov submit-proposal <path/to/proposal.json> --from <key_or_address>
其中 proposal.json 内容如下:
{
  "title": "IBC Clients Param Change",
  "summary": "Update allowed clients",
  "messages": [
    {
  "@type": "/ibc.core.client.v1.MsgUpdateParams",
  "signer": "cosmos1...", / The gov module account address
      "params": {
  "allowed_clients": ["06-solomachine",
  "07-tendermint",
  "0x-new-client"]
      }
    }
  ],
  "metadata": "AQ==",
  "deposit": "100stake"
}
如果 AllowedClients 列表只包含一个元素,且该元素等于通配符 "*",则表示允许所有客户端类型,因此无需再提交治理提案来更新该参数。

Synopsis

Learn how to configure light client modules and create clients using core IBC and the 02-client submodule. A last step to finish the development of the light client, is to implement the AppModuleBasic interface to allow it to be added to the chain’s app.go alongside other light client types the chain enables. Finally, a succinct rundown is given of the remaining steps to make the light client operational, getting the light client type passed through governance and creating the clients.

Configuring a light client module

An IBC light client module must implement the AppModuleBasic interface in order to register its concrete types against the core IBC interfaces defined in modules/core/exported. This is accomplished via the RegisterInterfaces method which provides the light client module with the opportunity to register codec types using the chain’s InterfaceRegistry. Please refer to the 07-tendermint codec registration. The AppModuleBasic interface may also be leveraged to install custom CLI handlers for light client module users. Light client modules can safely no-op for interface methods which it does not wish to implement. Please refer to the core IBC documentation for how to configure additional light client modules alongside 07-tendermint in app.go. See below for an example of the 07-tendermint implementation of AppModuleBasic.
var _ module.AppModuleBasic = AppModuleBasic{
}

/ AppModuleBasic defines the basic application module used by the tendermint light client.
/ Only the RegisterInterfaces function needs to be implemented. All other function perform
/ a no-op.
type AppModuleBasic struct{
}

/ Name returns the tendermint module name.
func (AppModuleBasic)

Name()

string {
    return ModuleName
}

/ RegisterLegacyAminoCodec performs a no-op. The Tendermint client does not support amino.
func (AppModuleBasic)

RegisterLegacyAminoCodec(*codec.LegacyAmino) {
}

/ RegisterInterfaces registers module concrete types into protobuf Any. This allows core IBC
/ to unmarshal tendermint light client types.
func (AppModuleBasic)

RegisterInterfaces(registry codectypes.InterfaceRegistry) {
    RegisterInterfaces(registry)
}

/ DefaultGenesis performs a no-op. Genesis is not supported for the tendermint light client.
func (AppModuleBasic)

DefaultGenesis(cdc codec.JSONCodec)

json.RawMessage {
    return nil
}

/ ValidateGenesis performs a no-op. Genesis is not supported for the tendermint light client.
func (AppModuleBasic)

ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage)

error {
    return nil
}

/ RegisterGRPCGatewayRoutes performs a no-op.
func (AppModuleBasic)

RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
}

/ GetTxCmd performs a no-op. Please see the 02-client cli commands.
func (AppModuleBasic)

GetTxCmd() *cobra.Command {
    return nil
}

/ GetQueryCmd performs a no-op. Please see the 02-client cli commands.
func (AppModuleBasic)

GetQueryCmd() *cobra.Command {
    return nil
}

Creating clients

A client is created by executing a new MsgCreateClient transaction composed with a valid ClientState and initial ConsensusState encoded as protobuf Anys. Generally, this is performed by an off-chain process known as an IBC relayer however, this is not a strict requirement. See below for a list of IBC relayer implementations: Stateless checks are performed within the ValidateBasic method of MsgCreateClient.
/ MsgCreateClient defines a message to create an IBC client
message MsgCreateClient {
  option (gogoproto.goproto_getters) = false;

  / light client state
  google.protobuf.Any client_state = 1 [(gogoproto.moretags) = "yaml:\"client_state\""];
  / consensus state associated with the client that corresponds to a given
  / height.
  google.protobuf.Any consensus_state = 2 [(gogoproto.moretags) = "yaml:\"consensus_state\""];
  / signer address
  string signer = 3;
}
Leveraging protobuf Any encoding allows core IBC to unpack the ClientState into its respective interface type registered previously using the light client module’s RegisterInterfaces method. Within the 02-client submodule, the ClientState is then initialized with its own isolated key-value store, namespaced using a unique client identifier. In order to successfully create an IBC client using a new client type, it must be supported. Light client support in IBC is gated by on-chain governance. The allow list may be updated by submitting a new governance proposal to update the 02-client parameter AllowedClients. See below for example:
%s tx gov submit-proposal <path/to/proposal.json> --from <key_or_address>
where proposal.json contains:
{
  "title": "IBC Clients Param Change",
  "summary": "Update allowed clients",
  "messages": [
    {
  "@type": "/ibc.core.client.v1.MsgUpdateParams",
  "signer": "cosmos1...", / The gov module account address
      "params": {
  "allowed_clients": ["06-solomachine",
  "07-tendermint",
  "0x-new-client"]
      }
    }
  ],
  "metadata": "AQ==",
  "deposit": "100stake"
}
If the AllowedClients list contains a single element that is equal to the wildcard "*", then all client types are allowed and it is thus not necessary to submit a governance proposal to update the parameter.