本节从 IBC 参与者的视角说明如何使用 callbacks 中间件。callbacks 中间件提供两类回调:
  • 源端回调:
    • SendPacket 回调
    • OnAcknowledgementPacket 回调
    • OnTimeoutPacket 回调
  • 目标端回调:
    • ReceivePacket 回调
对于给定通道,如果源链在该通道的 IBC 栈中接入了 callbacks 中间件,则支持源端回调。类似地,如果目标链在该通道的 IBC 栈中接入了 callbacks 中间件,则支持目标端回调。
回调总是在底层 IBC 模块处理完数据包之后执行。
如果底层应用模块在接收数据包时采用异步 acknowledgement(例如,栈中包含 packet forward middleware,且该数据包正在使用它),那么 callbacks 中间件会在收到 acknowledgement 之后再执行 ReceivePacket 回调。

源端回调

源端回调在以下 ibc 模块中原生受支持(前提是它们被 callbacks 中间件包装):
  • transfer
  • icacontroller
要让 callbacks 中间件处理你的源端回调,你必须将应用数据包数据中的 memo 设置为以下格式:
{
  "src_callback": {
  "address": "callbackAddressString",
    / optional
    "gas_limit": "userDefinedGasLimitString",
  }
}

目标端回调

目标端回调目前仅在 transfer 模块中原生受支持。注意,不支持包装 icahost。这是因为 icahost 本身就应该能够执行任意交易,并且可以直接调用合约或模块。 要让 callbacks 中间件处理你的目标端回调,你必须将应用数据包数据中的 memo 设置为以下格式:
{
  "dest_callback": {
  "address": "callbackAddressString",
    / optional
    "gas_limit": "userDefinedGasLimitString",
  }
}
请注意,一个数据包可以同时具有源端和目标端回调。
{
  "src_callback": {
  "address": "callbackAddressString",
    / optional
    "gas_limit": "userDefinedGasLimitString",
  
},
  "dest_callback": {
  "address": "callbackAddressString",
    / optional
    "gas_limit": "userDefinedGasLimitString",
  }
}

用户自定义 Gas 限制

添加用户自定义 gas 限制有以下原因:
  • 防止回调阻塞数据包生命周期。
  • 防止中继器通过发送低 gas 的数据包,对回调执行发起 DoS 攻击。
链上有一个全局参数,用于设定用户可为回调设置的最大 gas 限制。这样可以防止用户设置对中继器而言过高的 gas 限制。如果未在数据包 memo 中设置 "gas_limit",则会使用该最大 gas 限制。
这些目标是通过为回调执行设定一个所需的最小 gas 数量来实现的。如果中继器为回调执行提供的 gas 至少达到这个最小限制,那么即使回调在执行期间耗尽 gas,数据包生命周期也不会被阻塞,并且该回调不能被重试。如果中继器未提供最小 gas 数量,且回调执行过程中耗尽 gas,则整个 tx 会被回滚,并且之后可能再次执行。
如果 SendPacket 回调执行失败,或因任何原因返回错误,总是会触发回滚。这样可以确保在回调执行失败时不会发送数据包。

This section explains how to use the callbacks middleware from the perspective of an IBC Actor. Callbacks middleware provides two types of callbacks:
  • Source callbacks:
    • SendPacket callback
    • OnAcknowledgementPacket callback
    • OnTimeoutPacket callback
  • Destination callbacks:
    • ReceivePacket callback
For a given channel, the source callbacks are supported if the source chain has the callbacks middleware wired up in the channel’s IBC stack. Similarly, the destination callbacks are supported if the destination chain has the callbacks middleware wired up in the channel’s IBC stack.
Callbacks are always executed after the packet has been processed by the underlying IBC module.
If the underlying application module is doing an asynchronous acknowledgement on packet receive (for example, if the packet forward middleware is in the stack, and is being used by this packet), then the callbacks middleware will execute the ReceivePacket callback after the acknowledgement has been received.

Source Callbacks

Source callbacks are natively supported in the following ibc modules (if they are wrapped by the callbacks middleware):
  • transfer
  • icacontroller
To have your source callbacks be processed by the callbacks middleware, you must set the memo in the application’s packet data to the following format:
{
  "src_callback": {
  "address": "callbackAddressString",
    / optional
    "gas_limit": "userDefinedGasLimitString",
  }
}

Destination Callbacks

Destination callbacks are natively only supported in the transfer module. Note that wrapping icahost is not supported. This is because icahost should be able to execute an arbitrary transaction anyway, and can call contracts or modules directly. To have your destination callbacks processed by the callbacks middleware, you must set the memo in the application’s packet data to the following format:
{
  "dest_callback": {
  "address": "callbackAddressString",
    / optional
    "gas_limit": "userDefinedGasLimitString",
  }
}
Note that a packet can have both a source and destination callback.
{
  "src_callback": {
  "address": "callbackAddressString",
    / optional
    "gas_limit": "userDefinedGasLimitString",
  
},
  "dest_callback": {
  "address": "callbackAddressString",
    / optional
    "gas_limit": "userDefinedGasLimitString",
  }
}

User Defined Gas Limit

User defined gas limit was added for the following reasons:
  • To prevent callbacks from blocking packet lifecycle.
  • To prevent relayers from being able to DOS the callback execution by sending a packet with a low amount of gas.
There is a chain wide parameter that sets the maximum gas limit that a user can set for a callback. This is to prevent a user from setting a gas limit that is too high for relayers. If the "gas_limit" is not set in the packet memo, then the maximum gas limit is used.
These goals are achieved by creating a minimum gas amount required for callback execution. If the relayer provides at least the minimum gas limit for the callback execution, then the packet lifecycle will not be blocked if the callback runs out of gas during execution, and the callback cannot be retried. If the relayer does not provided the minimum amount of gas and the callback executions runs out of gas, the entire tx is reverted and it may be executed again.
SendPacket callback is always reverted if the callback execution fails or returns an error for any reason. This is so that the packet is not sent if the callback execution fails.