CCIP 读取 ISM
使用 CcipReadIsm
为开发者提供了灵活性,以验证跨链消息。最终,所有其他类型的 ISM 都可以作为 CCIP 读取 ISM 实现,因此在构建新的 ISM 类型时,建议构建 CCIP 读取 ISM,因为所有的中继器集成工作已经完成。
需要注意的是,CCIP 读取 ISM 引入了对外部(区块链外部但可自托管)API 的依赖。如果这对您的用例是一个硬性障碍,您可能需要考虑其他消息验证技术。
在构建 CCIP 读取 ISM 之前,值得熟悉一下 CCIP 读取规范。该规范描述了一种通用协议,允许 EVM 兼容链上的智能合约查询和消费链外数据。
工作原理
中继器将不断监听来自 Hyperlane 邮箱 发出的 Dispatch
事件。当消息被发送并被中继器接收时,中继器将查询目标 ISM 以获取有关如何处理消息的信息,以及交付是否成功。
正确的 moduleType
变量需要在您的 ISM 上设置,以便中继器知道它是一个 CCIP 读取 ISM。为了确保这一点配置正确,您可以从 @hyperlane-xyz/core
中的 AbstractCcipReadIsm
继承。
然后,中继器将调用 ISM 上的 getOffchainVerifyInfo(bytes)
函数,传入要交付的消息内容。该函数应以 接口 部分中描述的 OffchainLookup
错误进行回退。
中继器将查询在此回退消息中指定的端点,并将提供的响应和原始消息传递给目标 Mailbox
的 process(bytes,bytes)
函数。
接口
CcipReadIsm
必须实现 ICcipReadIsm
接口,并应扩展 AbstractCcipReadIsm
,这是一个便利合约,可以正确设置 moduleType
。
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
import {IInterchainSecurityModule} from "../IInterchainSecurityModule.sol";
interface ICcipReadIsm is IInterchainSecurityModule {
/// @dev https://eips.ethereum.org/EIPS/eip-3668
/// @param sender 发起调用的合约地址,通常是 address(this)
/// @param urls 查询链外数据的 URL
/// @param callData 链外服务请求所需的上下文
/// @param callbackFunction 带有链外信息的函数选择器
/// @param extraData 额外的传递信息,用于调用 callbackFunction
error OffchainLookup(
address sender,
string[] urls,
bytes callData,
bytes4 callbackFunction,
bytes extraData
);
/**
* @notice 以所需的数据回退,以查询链外信息
* 并通过源邮箱提交
* @dev 有关更多信息,请参见 https://eips.ethereum.org/EIPS/eip-3668
* @param _message 将帮助构建链外查询的数据
*/
function getOffchainVerifyInfo(bytes calldata _message) external view;
}
配置
在开发 CCIP 读取 ISM 时,有一个很好的参考示例是 ChainlinkISM。ChainlinkISM
使用一组 Chainlink 预言机进行初始化,并验证提供的价格数据是否已由某些签名者的子集签名。
API
根据 CCIP 读取,链外 API 需要返回 JSON 数据,格式为:
{
"data": "..."
}
中继器将把此 data
属性作为 metadata
参数传递给 Mailbox.process(bytes metadata, bytes message)
。
请注意,在 Chainlink ISM 的情况下,数据的接收者也充当验证 ISM,data
只是提交价格数据的原始交易,附带相关签名。message
属性在某种程度上是多余的。
合约
在设置 ISM 时,getOffchainVerifyInfo
和 verify
函数是需要指定的重要函数。
-
getOffchainVerifyInfo
函数应以OffchainLookup
错误回退,指示中继器查询给定的 API 端点。OffchainLookup
错误允许提供一组 API 端点,因此您可以强制执行任何级别的冗余。 -
verify
必须接受提供的metadata
并验证其合法性。同样, ChainlinkISM 实现 在为您自己的 ISM 开发此逻辑时可以作为有用的参考。
以下是一个 CCIP 读取 ISM 的框架示例,其中 ISM 也是消息的接收者,符合 Chainlink ISM 的要求。
pragma solidity ^0.8.13;
import {AbstractCcipReadIsm} from "@hyperlane-xyz/core/contracts/isms/ccip-read/AbstractCcipReadIsm.sol";
import {IInterchainSecurityModule, ISpecifiesInterchainSecurityModule} from "@hyperlane-xyz/core/contracts/interfaces/IInterchainSecurityModule.sol";
import {IMailbox} from "@hyperlane-xyz/core/contracts/interfaces/IMailbox.sol";
import {Message} from "@hyperlane-xyz/core/contracts/libs/Message.sol";
contract MyCcipReadIsm is AbstractCcipReadIsm, ISpecifiesInterchainSecurityModule {
using Message for bytes;
IMailbox mailbox;
...
/**
* 无操作,所有操作都在验证函数中进行
*/
function handle(uint32, bytes32, bytes calldata _report) public {}
/**
* @param _metadata ABI 编码的模块元数据
* @param _message 格式化的 Hyperlane 消息(见 Message.sol)。
*/
function verify(
bytes calldata _metadata,
bytes calldata _message
) external returns (bool) {
...
}
function interchainSecurityModule()
external
view
returns (IInterchainSecurityModule)
{
return IInterchainSecurityModule(address(this));
}
function getOffchainVerifyInfo(
bytes calldata _message
) external view override {
revert OffchainLookup(
address(this),
offchainUrls,
_message,
MyCcipReadIsm.process.selector,
_message
);
}
/**
* 为了与完整的 CCIP 读取规范兼容。中继器
* 将直接调用邮箱,而不管在 `OffchainLookup` 错误中指定的选择器
*/
function process(
bytes calldata _metadata,
bytes calldata _message
) external {
mailbox.process(_metadata, _message);
}
}