概述

了解如何将 Interchain Accounts 的 host 和 controller 功能集成到你的链中。以下文档仅适用于 Cosmos SDK 链。 Interchain Accounts 模块包含两个子模块。每个子模块都有自己的 IBC 应用。Interchain Accounts 模块应像链上注册所有 SDK 模块一样被注册为 AppModule,但每个子模块都应根据需要创建各自的 IBCModule。此外,应为每个将要使用的子模块在 IBC router 中添加一条路由。 希望支持 ICS-27 的链可以选择充当 host 链、controller 链,或同时承担两者角色。禁用 host 或 controller 功能既可以通过在 app.go 文件中完全排除对应子模块的方式静态完成,也可以通过链上参数动态启用或禁用 host 或 controller 子模块。 Interchain Account 认证模块(无论是自定义模块还是通用模块,例如 x/gov 或 x/auth 这类 Cosmos SDK 模块)都可以向 controller 子模块的 MsgServer 发送消息,以注册 interchain account 并向该 interchain account 发送数据包。要实现这一点,认证模块需要与 baseapp 的 MsgServiceRouter 组合使用。 ica-v6.png

集成示例

/ app.go

/ 为 Interchain Accounts 模块和认证模块注册 AppModule
/ 注意:这里不存在 `icaauth`,必须替换为实际使用的 Interchain Accounts 认证模块
ModuleBasics = module.NewBasicManager(
  ...
  ica.AppModuleBasic{
},
  icaauth.AppModuleBasic{
},
  ...
)

...

/ 为 Interchain Accounts 模块添加模块账户权限
/ 仅在 host 链功能下需要
/ 在 host 链上创建的每个 Interchain Account 都派生自所创建的模块账户
maccPerms = map[string][]string{
  ...
  icatypes.ModuleName:            nil,
}

...

/ 为所使用的每个子模块以及认证模块添加 Interchain Accounts Keeper
/ 如果某个子模块被静态禁用,则无需添加对应的 Keeper。
type App struct {
  ...

  ICAControllerKeeper icacontrollerkeeper.Keeper
  ICAHostKeeper       icahostkeeper.Keeper
  ICAAuthKeeper       icaauthkeeper.Keeper

  ...
}

...

/ 为每个子模块 Keeper 和认证模块创建 store key
    keys := sdk.NewKVStoreKeys(
  ...
  icacontrollertypes.StoreKey,
  icahosttypes.StoreKey,
  icaauthtypes.StoreKey,
  ...
)

...

/ 为每个子模块创建 Keeper
app.ICAControllerKeeper = icacontrollerkeeper.NewKeeper(
  appCodec, keys[icacontrollertypes.StoreKey], app.GetSubspace(icacontrollertypes.SubModuleName),
  app.IBCKeeper.ChannelKeeper, / 可替换为诸如 ics29 fee 之类的中间件
  app.IBCKeeper.ChannelKeeper, app.IBCKeeper.PortKeeper,
  app.MsgServiceRouter(),
  authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)

app.ICAHostKeeper = icahostkeeper.NewKeeper(
  appCodec, keys[icahosttypes.StoreKey], app.GetSubspace(icahosttypes.SubModuleName),
  app.IBCKeeper.ChannelKeeper, / 可替换为诸如 ics29 fee 之类的中间件
  app.IBCKeeper.ChannelKeeper, app.IBCKeeper.PortKeeper, app.AccountKeeper,
  app.MsgServiceRouter(), app.GRPCQueryRouter(),
  authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)

/ 创建 Interchain Accounts AppModule
    icaModule := ica.NewAppModule(&app.ICAControllerKeeper, &app.ICAHostKeeper)

/ 创建你的 Interchain Accounts 认证模块
app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.MsgServiceRouter())

/ ICA 认证 AppModule
    icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper)

/ 按需创建 controller IBC 应用栈和 host IBC 模块
    icaControllerStack := icacontroller.NewIBCMiddleware(app.ICAControllerKeeper)
    icaHostIBCModule := icahost.NewIBCModule(app.ICAHostKeeper)

/ 注册 host 路由和认证路由
ibcRouter.
  AddRoute(icacontrollertypes.SubModuleName, icaControllerStack).
  AddRoute(icahosttypes.SubModuleName, icaHostIBCModule)
...

/ 注册 Interchain Accounts 和认证模块的 AppModule
app.moduleManager = module.NewManager(
  ...
  icaModule,
  icaAuthModule,
)

...

/ 将 Interchain Accounts 添加到 begin blocker 执行逻辑中
app.moduleManager.SetOrderBeginBlockers(
  ...
  icatypes.ModuleName,
  ...
)

/ 将 Interchain Accounts 添加到 end blocker 执行逻辑中
app.moduleManager.SetOrderEndBlockers(
  ...
  icatypes.ModuleName,
  ...
)

/ 添加 Interchain Accounts 模块的 InitGenesis 执行逻辑
app.moduleManager.SetOrderInitGenesis(
  ...
  icatypes.ModuleName,
  ...
)

/ initParamsKeeper 初始化 params keeper 及其子空间
func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey sdk.StoreKey)

paramskeeper.Keeper {
  ...
  paramsKeeper.Subspace(icahosttypes.SubModuleName)

paramsKeeper.Subspace(icacontrollertypes.SubModuleName)
  ...
}
如果不需要自定义认证模块,并且可以直接使用通用的 Cosmos SDK 认证模块,那么在上面的集成示例代码中,所有对 ICAAuthKeeper 和 icaAuthModule 的引用都可以删除。也就是说,下面这段代码将不再需要:
/ 创建你的 Interchain Accounts 认证模块
app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.MsgServiceRouter())

/ ICA 认证 AppModule
    icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper)

仅使用单个子模块

如上所述,Interchain Accounts 应用模块的结构支持仅启用 controller 或 host 功能。 这可以通过在 Interchain Accounts 的 NewAppModule 构造函数中省略 controller 或 host 的 Keeper,并且只通过 IBCRouter 挂载所需子模块来实现。 另外,也可以通过链上参数动态启用或禁用子模块。 下面的代码片段展示了如何在 app.go 中以静态方式禁用子模块的基本示例。

禁用控制链功能

/ 创建 Interchain Accounts AppModule,省略 controller keeper
    icaModule := ica.NewAppModule(nil, &app.ICAHostKeeper)

/ 创建 host IBC Module
    icaHostIBCModule := icahost.NewIBCModule(app.ICAHostKeeper)

/ 注册 host 路由
ibcRouter.AddRoute(icahosttypes.SubModuleName, icaHostIBCModule)

禁用 host 链功能

/ 创建 Interchain Accounts AppModule,省略 host keeper
    icaModule := ica.NewAppModule(&app.ICAControllerKeeper, nil)

/ 如有需要,可选择实例化你的自定义认证模块,否则可以省略
...

/ 创建 controller IBC 应用栈
    icaControllerStack := icacontroller.NewIBCMiddleware(app.ICAControllerKeeper)

/ 注册 controller 路由
ibcRouter.AddRoute(icacontrollertypes.SubModuleName, icaControllerStack)

Synopsis

Learn how to integrate Interchain Accounts host and controller functionality to your chain. The following document only applies for Cosmos SDK chains. The Interchain Accounts module contains two submodules. Each submodule has its own IBC application. The Interchain Accounts module should be registered as an AppModule in the same way all SDK modules are registered on a chain, but each submodule should create its own IBCModule as necessary. A route should be added to the IBC router for each submodule which will be used. Chains who wish to support ICS-27 may elect to act as a host chain, a controller chain or both. Disabling host or controller functionality may be done statically by excluding the host or controller submodule entirely from the app.go file or it may be done dynamically by taking advantage of the on-chain parameters which enable or disable the host or controller submodules. Interchain Account authentication modules (both custom or generic, such as the x/gov or x/auth Cosmos SDK modules) can send messages to the controller submodule’s MsgServer to register interchain accounts and send packets to the interchain account. To accomplish this, the authentication module needs to be composed with baseapp’s MsgServiceRouter. ica-v6.png

Example integration

/ app.go

/ Register the AppModule for the Interchain Accounts module and the authentication module
/ Note: No `icaauth` exists, this must be substituted with an actual Interchain Accounts authentication module
ModuleBasics = module.NewBasicManager(
  ...
  ica.AppModuleBasic{
},
  icaauth.AppModuleBasic{
},
  ...
)

...

/ Add module account permissions for the Interchain Accounts module
/ Only necessary for host chain functionality
/ Each Interchain Account created on the host chain is derived from the module account created
maccPerms = map[string][]string{
  ...
  icatypes.ModuleName:            nil,
}

...

/ Add Interchain Accounts Keepers for each submodule used and the authentication module
/ If a submodule is being statically disabled, the associated Keeper does not need to be added.
type App struct {
  ...

  ICAControllerKeeper icacontrollerkeeper.Keeper
  ICAHostKeeper       icahostkeeper.Keeper
  ICAAuthKeeper       icaauthkeeper.Keeper

  ...
}

...

/ Create store keys for each submodule Keeper and the authentication module
    keys := sdk.NewKVStoreKeys(
  ...
  icacontrollertypes.StoreKey,
  icahosttypes.StoreKey,
  icaauthtypes.StoreKey,
  ...
)

...

/ Create the Keeper for each submodule
app.ICAControllerKeeper = icacontrollerkeeper.NewKeeper(
  appCodec, keys[icacontrollertypes.StoreKey], app.GetSubspace(icacontrollertypes.SubModuleName),
  app.IBCKeeper.ChannelKeeper, / may be replaced with middleware such as ics29 fee
  app.IBCKeeper.ChannelKeeper, app.IBCKeeper.PortKeeper,
  app.MsgServiceRouter(),
  authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)

app.ICAHostKeeper = icahostkeeper.NewKeeper(
  appCodec, keys[icahosttypes.StoreKey], app.GetSubspace(icahosttypes.SubModuleName),
  app.IBCKeeper.ChannelKeeper, / may be replaced with middleware such as ics29 fee
  app.IBCKeeper.ChannelKeeper, app.IBCKeeper.PortKeeper, app.AccountKeeper,
  app.MsgServiceRouter(), app.GRPCQueryRouter(),
  authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)

/ Create Interchain Accounts AppModule
    icaModule := ica.NewAppModule(&app.ICAControllerKeeper, &app.ICAHostKeeper)

/ Create your Interchain Accounts authentication module
app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.MsgServiceRouter())

/ ICA auth AppModule
    icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper)

/ Create controller IBC application stack and host IBC module as desired
    icaControllerStack := icacontroller.NewIBCMiddleware(app.ICAControllerKeeper)
    icaHostIBCModule := icahost.NewIBCModule(app.ICAHostKeeper)

/ Register host and authentication routes
ibcRouter.
  AddRoute(icacontrollertypes.SubModuleName, icaControllerStack).
  AddRoute(icahosttypes.SubModuleName, icaHostIBCModule)
...

/ Register Interchain Accounts and authentication module AppModule's
app.moduleManager = module.NewManager(
  ...
  icaModule,
  icaAuthModule,
)

...

/ Add Interchain Accounts to begin blocker logic
app.moduleManager.SetOrderBeginBlockers(
  ...
  icatypes.ModuleName,
  ...
)

/ Add Interchain Accounts to end blocker logic
app.moduleManager.SetOrderEndBlockers(
  ...
  icatypes.ModuleName,
  ...
)

/ Add Interchain Accounts module InitGenesis logic
app.moduleManager.SetOrderInitGenesis(
  ...
  icatypes.ModuleName,
  ...
)

/ initParamsKeeper init params keeper and its subspaces
func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey sdk.StoreKey)

paramskeeper.Keeper {
  ...
  paramsKeeper.Subspace(icahosttypes.SubModuleName)

paramsKeeper.Subspace(icacontrollertypes.SubModuleName)
  ...
}
If no custom athentication module is needed and a generic Cosmos SDK authentication module can be used, then from the sample integration code above all references to ICAAuthKeeper and icaAuthModule can be removed. That’s it, the following code would not be needed:
/ Create your Interchain Accounts authentication module
app.ICAAuthKeeper = icaauthkeeper.NewKeeper(appCodec, keys[icaauthtypes.StoreKey], app.MsgServiceRouter())

/ ICA auth AppModule
    icaAuthModule := icaauth.NewAppModule(appCodec, app.ICAAuthKeeper)

Using submodules exclusively

As described above, the Interchain Accounts application module is structured to support the ability of exclusively enabling controller or host functionality. This can be achieved by simply omitting either controller or host Keeper from the Interchain Accounts NewAppModule constructor function, and mounting only the desired submodule via the IBCRouter. Alternatively, submodules can be enabled and disabled dynamically using on-chain parameters. The following snippets show basic examples of statically disabling submodules using app.go.

Disabling controller chain functionality

/ Create Interchain Accounts AppModule omitting the controller keeper
    icaModule := ica.NewAppModule(nil, &app.ICAHostKeeper)

/ Create host IBC Module
    icaHostIBCModule := icahost.NewIBCModule(app.ICAHostKeeper)

/ Register host route
ibcRouter.AddRoute(icahosttypes.SubModuleName, icaHostIBCModule)

Disabling host chain functionality

/ Create Interchain Accounts AppModule omitting the host keeper
    icaModule := ica.NewAppModule(&app.ICAControllerKeeper, nil)

/ Optionally instantiate your custom authentication module if needed, or not otherwise
...

/ Create controller IBC application stack
    icaControllerStack := icacontroller.NewIBCMiddleware(app.ICAControllerKeeper)

/ Register controller route
ibcRouter.AddRoute(icacontrollertypes.SubModuleName, icaControllerStack)