了解如何将 callbacks 中间件与 IBC 应用集成。下文面向基于 Cosmos SDK 构建的开发者,仅适用于 Cosmos SDK 链。
可在 ibc-go 模块集成 一节中查看使用 callbacks 中间件的 IBC v2 transfer 栈集成示例
callbacks 中间件是 IBC 中间件接口的一种精简且无状态的实现。它没有 keeper,也不会存储任何状态。它只是将 IBC 中间件消息路由到相应的回调函数,而该函数由次级应用实现。因此,它不需要注册为模块,也不需要添加到模块管理器中。它只需要被加入 IBC 应用栈。

前置阅读

顾名思义,callbacks 中间件扮演的是 IBC 中间件的角色,因此链开发者必须对其进行配置,以便正确路由和处理 IBC 消息。 对于 Cosmos SDK 链,这一设置通过 app/app.go 文件完成;模块会在其中按顺序构建和配置,以引导区块链应用启动。

使用 callbacks 中间件配置应用栈

如 IBC 中间件开发 中所述,应用栈可以由多个中间件或不包含任何中间件组成,这些中间件会嵌套一个基础应用。 这些层共同构成完整的应用逻辑,使开发者能够构建可组合且灵活的 IBC 应用栈。 例如,应用栈可以只是一个像 transfer 这样的基础应用;但如果同一应用栈结合 packet-forward-middleware 与 callbacks 组成,则会通过先由 callbacks 模块封装、再由 packet-forward-middleware 封装的方式,对 transfer 基础应用进行两次嵌套。 callbacks 中间件还要求接收回调的次级应用实现 ContractKeeper。wasmd 的 contract keeper 已在这里实现,并以 WasmKeeper 的名称被引用。

Transfer

下面展示了如何使用 transfer、packet-forward-middleware 和 callbacks 创建应用栈的示例。如果你不打算使用 packet-forward-middleware,可以将其省略。 以下 transferStack 在 app/app.go 中配置,并被添加到 IBC Router。 内联注释描述了数据包在应用栈与 IBC 核心之间的执行流程。
// 创建 Transfer 栈
// SendPacket:由于该调用从应用层发往 IBC 核心:
// transferKeeper.SendPacket -> callbacks.SendPacket -> feeKeeper.SendPacket -> channel.SendPacket

// RecvPacket:消息从 IBC 核心发出并向下传递到应用层,流程相反
// channel.RecvPacket -> fee.OnRecvPacket -> callbacks.OnRecvPacket -> transfer.OnRecvPacket

// transfer 栈包含(从上到下):
// - IBC Packet Forward Middleware
// - IBC Callbacks Middleware
// - Transfer

// 初始化 callbacks 的 gas 上限,推荐在配合 cosmwasm 合约时设置为 10M
    maxCallbackGas := uint64(10_000_000)

// callbacks 中间件所需的 keepers
    wasmStackIBCHandler := wasm.NewIBCHandler(app.WasmKeeper, app.IBCKeeper.ChannelKeeper, app.IBCKeeper.ChannelKeeper)

// 按照从栈底到栈顶的顺序创建 IBC 模块
// 创建 Transfer 栈
	var transferStack porttypes.IBCModule
	transferStack = transfer.NewIBCModule(app.TransferKeeper)
// callbacks 以 transfer 栈作为其基础应用,并使用 PacketForwardKeeper 作为 ICS4Wrapper
// 即 packet-forward-middleware 位于栈中更高层,处于 callbacks 和 ibc channel keeper 之间
// 由于这是 transfer 栈中最底层的中间件,因此它应该作为 transfer keeper 的
// WriteAcknowledgement 的第一个入口点。
    cbStack := ibccallbacks.NewIBCMiddleware(transferStack, app.PacketForwardKeeper, wasmStackIBCHandler, maxCallbackGas)

transferStack = packetforward.NewIBCMiddleware(
		cbStack,
		app.PacketForwardKeeper,
		0,
		packetforwardkeeper.DefaultForwardTransferPacketTimeoutTimestamp,
	)

app.TransferKeeper.WithICS4Wrapper(cbStack)

// 创建静态 IBC router,添加应用路由,然后设置并封存
    ibcRouter := porttypes.NewRouter()

ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack)

ibcRouter.AddRoute(wasmtypes.ModuleName, wasmStackIBCHandler)

app.IBCKeeper.SetRouter(ibcRouter)

Learn how to integrate the callbacks middleware with IBC applications. The following document is intended for developers building on top of the Cosmos SDK and only applies for Cosmos SDK chains.
An example integration for an IBC v2 transfer stack using the callbacks middleware can be found in the ibc-go module integration section
The callbacks middleware is a minimal and stateless implementation of the IBC middleware interface. It does not have a keeper, nor does it store any state. It simply routes IBC middleware messages to the appropriate callback function, which is implemented by the secondary application. Therefore, it doesn’t need to be registered as a module, nor does it need to be added to the module manager. It only needs to be added to the IBC application stack.

Pre-requisite Readings

The callbacks middleware, as the name suggests, plays the role of an IBC middleware and as such must be configured by chain developers to route and handle IBC messages correctly. For Cosmos SDK chains this setup is done via the app/app.go file, where modules are constructed and configured in order to bootstrap the blockchain application.

Configuring an application stack with the callbacks middleware

As mentioned in IBC middleware development an application stack may be composed of many or no middlewares that nest a base application. These layers form the complete set of application logic that enable developers to build composable and flexible IBC application stacks. For example, an application stack may just be a single base application like transfer, however, the same application stack composed with packet-forward-middleware and callbacks will nest the transfer base application twice by wrapping it with the callbacks module and then packet forward middleware. The callbacks middleware also requires a secondary application that will receive the callbacks to implement the ContractKeeper. The wasmd contract keeper has been implemented here and is referenced as the WasmKeeper.

Transfer

See below for an example of how to create an application stack using transfer, packet-forward-middleware, and callbacks. Feel free to omit the packet-forward-middleware if you do not want to use it. The following transferStack is configured in app/app.go and added to the IBC Router. The in-line comments describe the execution flow of packets between the application stack and IBC core.
/ Create Transfer Stack
/ SendPacket, since it is originating from the application to core IBC:
/ transferKeeper.SendPacket -> callbacks.SendPacket -> feeKeeper.SendPacket -> channel.SendPacket

/ RecvPacket, message that originates from core IBC and goes down to app, the flow is the other way
/ channel.RecvPacket -> fee.OnRecvPacket -> callbacks.OnRecvPacket -> transfer.OnRecvPacket

/ transfer stack contains (from top to bottom):
/ - IBC Packet Forward Middleware
/ - IBC Callbacks Middleware
/ - Transfer

/ initialise the gas limit for callbacks, recommended to be 10M for use with cosmwasm contracts
    maxCallbackGas := uint64(10_000_000)

/ the keepers for the callbacks middleware
    wasmStackIBCHandler := wasm.NewIBCHandler(app.WasmKeeper, app.IBCKeeper.ChannelKeeper, app.IBCKeeper.ChannelKeeper)

/ create IBC module from bottom to top of stack
/ Create Transfer Stack
	var transferStack porttypes.IBCModule
	transferStack = transfer.NewIBCModule(app.TransferKeeper)
/ callbacks wraps the transfer stack as its base app, and uses PacketForwardKeeper as the ICS4Wrapper
/ i.e. packet-forward-middleware is higher on the stack and sits between callbacks and the ibc channel keeper
/ Since this is the lowest level middleware of the transfer stack, it should be the first entrypoint for transfer keeper's
/ WriteAcknowledgement.
    cbStack := ibccallbacks.NewIBCMiddleware(transferStack, app.PacketForwardKeeper, wasmStackIBCHandler, maxCallbackGas)

transferStack = packetforward.NewIBCMiddleware(
		cbStack,
		app.PacketForwardKeeper,
		0,
		packetforwardkeeper.DefaultForwardTransferPacketTimeoutTimestamp,
	)

app.TransferKeeper.WithICS4Wrapper(cbStack)

/ Create static IBC router, add app routes, then set and seal it
    ibcRouter := porttypes.NewRouter()

ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack)

ibcRouter.AddRoute(wasmtypes.ModuleName, wasmStackIBCHandler)

app.IBCKeeper.SetRouter(ibcRouter)