构建指南
一个新链架构的 Hyperlane构建由以下内容组成:
- Contracts: 公开应用程序开发人员用于发送和接收消息的接口
- Agents: 通过添加安全性并中继消息来操作协议
- Applications: 使用协议并展示其功能的应用程序
在开始之前,建议先查阅协议文档。
1. Contracts
下面描述了Hyperlane协议的链上合约规范。它使用了Solidity类型以便熟悉,但所有内容都可以推广到其他语言。
address
:应该解释为本地链的地址类型payable
:描述了一个允许调用者传递本地代币的函数
Message
消息是Hyperlane协议使用的核心数据结构。它是一个紧凑的数据结构,包含了将消息从一 个域路由到另一个域所需的所有信息。
struct Message {
// The version of the origin and destination Mailboxes
uint8 version,
// A nonce to uniquely identify the message on its origin Mailbox
uint32 nonce,
// Domain of origin chain
uint32 origin,
// Address of sender on origin chain
bytes32 sender,
// Domain of destination chain
uint32 destination,
// Address of recipient on destination chain
bytes32 recipient,
// Raw bytes of message body
bytes body
}
Mailbox
邮箱是开发人员发送和接收消息的入口。
Implementations:
In addition to default and custom hooks, Hyperlane v3 introduces the concept of a Required Hook that is used for post processing of ALL dispatches.
dispatch
将消息分发到目标域和接收者。
function dispatch(
// Domain of destination chain
uint32 destination,
// Address of recipient on destination chain as bytes32
bytes32 recipient,
// Raw bytes content of message body
bytes body
) returns (
// The message ID inserted into the Mailbox's merkle tree
bytes32 messageId
);
Dispatches a message to the destination domain and recipient, and provides metadata for the default hook.
function dispatch(
// Domain of destination chain
uint32 destination,
// Address of recipient on destination chain as bytes32
bytes32 recipient,
// Raw bytes content of message body
bytes body,
// Metadata used by the default post dispatch hook
bytes defaultHookMetadata
) returns (
// The message ID inserted into the Mailbox's merkle tree
bytes32 messageId
);
Dispatches a message to the destination domain and recipient, and provides metadata for a custom hook to use instead of the default.
function dispatch(
// Domain of destination chain
uint32 destination,
// Address of recipient on destination chain as bytes32
bytes32 recipient,
// Raw bytes content of message body
bytes body,
// Metadata used by the custom post dispatch hook
bytes customHookMetadata,
// Custom hook to use instead of the default
IPostDispatchHook customHook
) returns (
// The message ID inserted into the Mailbox's merkle tree
bytes32 messageId
);
process
尝试将 message
交付给其接收者。使用提供的 metadata
通过接收者的 ISM 验证 message
。
function process(
// Metadata used by the ISM to verify message.
bytes metadata,
// Byte packed message
bytes message
);
latestDispatchedId
返回在派发后钩子中用于验证的最新派发信息 ID。
function latestDispatchedId() public view returns (bytes32);
function latestDispatchedId() public view returns (bytes32);
Message Recipient
想要接收消息的合约必须公开以下处理程序。
function handle(
// Domain of origin chain
uint32 origin,
// Address of sender on origin chain
bytes32 sender,
// Raw bytes content of message body
bytes body
);
它们可以选择性地指定一个安全模块,在消息被处理之前进行验证。
function interchainSecurityModule() returns (address);
实现了这三个合约后,您就可以达到第一个里程碑,即通过调用 Mailbox
的 dispatch
函数来向接收者发送消息,并判断接收者是否收到了消息,以模拟消息传输。参见这里的 Foundry 测试案例。
Interchain Security Module
跨链安全模块用于在消息被处理之前进行验证。
moduleType
返回一个枚举,表示此ISM编码的安全模型的类型。
enum ModuleType {
UNUSED,
ROUTING,
AGGREGATION,
LEGACY_MULTISIG,
MERKLE_ROOT_MULTISIG,
MESSAGE_ID_MULTISIG,
NULL, // used with relayer carrying no metadata
CCIP_READ
}
function moduleType() returns (ModuleType);
中继器推断如何从此类型获取和格式化元数据
verify
定义一个安全模型,负责根据提供的元数据验证跨链消息。
function verify(
// Off-chain metadata provided by a relayer, specific
// to the security model encoded by the module
// (e.g. validator signatures)
bytes metadata,
// Hyperlane encoded interchain message
bytes message
) returns (
// True if the message was verified
bool success
);
Validator Announce
验证器公布其签名存储位置,以便中继器获取并验证其签名。
announce
通知验证者签名存储位置
function announce(
address validator, // The address of the validator
string storageLocation, // Information encoding the location of signed checkpoints
bytes signature // The signed validator announcement
) external returns (bool);
getAnnouncedStorageLocations
返回所有已宣布的存储位置列表
function getAnnouncedStorageLocations(
address[] _validators // The list of validators to get storage locations for
) external view returns (
string[][] // A list of registered storage metadata
);
Multisig ISM
实现一个安全模块,检查提供的元数据是否满足一组配置的验证者的签名法定数量。
Metadata
要与中继器中的MESSAGE_ID_MULTISIG模块类型实现一起使用,元数据必须格式化如下:
struct MultisigMetadata {
// The address of the origin mailbox
bytes32 originMailbox;
// The signed checkpoint root
bytes32 signedCheckpointRoot;
// The concatenated signatures of the validators
bytes signatures;
}
validatorsAndThreshold
返回负责验证消息的验证者集合以及所需签名的数量
可以根据 message 的内容进行更改
function validatorsAndThreshold(
// Hyperlane formatted interchain message
bytes message
) returns (
// The array of validator addresses
address[] validators,
// The number of validator signatures needed
uint8 threshold
);
实现了MultisigISM后,您就可以达到第二个里程碑,即测试您的邮箱仅在接收者的ISM返回true后才处理。您可以使用一个 TestISM
来测试,您可以静态设置它来接受或拒绝任何消息。参见Hardhat test case here.。
Interchain Gas Paymaster
燃料支付器用于支付目的链上信息处理所需的燃料。如果中继者愿意补贴信息处理费用,则不一定需要这样做。
payForGas
将msg.value存入作为向目标链中继消息的支付。
超额支付将导致向refundAddress退还本地令牌。调用者应注意这可能会产生重入问题。
function payForGas(
// The ID of the message to pay for.
bytes32 messageId,
// The domain of the message's destination chain.
uint32 destination,
// The amount of destination gas to pay for.
uint256 gasAmount,
// The local address to refund any overpayment to.
address refundAddress
) payable;
GasPayment
当支付消息燃料费用时发出。
event GasPayment(
bytes32 messageId,
uint32 destinationDomain,
uint256 gasAmount,
uint256 payment
);
DestinationGasConfigSet
Emitted when the gas oracle for a remote domain is set.
event DestinationGasConfigSet(
uint32 remoteDomain, // remote domain
address gasOracle, // gas oracle
uint96 gasOverhead // destination gas overhead
);
2. Agents
下面描述了新链实现的代理规范。Rust 实现希望支持所有链,但该规范的目的是与链无关。
Message Indexing
所有代理必须索引来自原始邮箱的消息。在Solidity邮箱中,我们emit an event for each message。其他链可能有不同的方式来呈现这些信息,但代理必须能够可靠地获取消息内容,并具有一致的顺序 - 请参见message indexer特性。
Validator
除了索引从邮箱分派的消息之外,验证者还会为他们观察到的消息生成认证,以便在目的地链上使用,确保安全。
Checkpoint
验证者从邮箱产生称为检查点的认证,这些认证通过Merkle根对所有已分派的消息ID进行提交。
pub struct Checkpoint {
/// The mailbox address
pub mailbox_address: H256,
/// The mailbox chain
pub mailbox_domain: u32,
/// The checkpointed root
pub root: H256,
/// The index of the checkpoint
pub index: u32,
}
验证者使用邮箱特性中的最新检查点方法来从邮箱获取最新的检查点,并使用检查点同步器特性将签名提交到一些高可用存储中。
Checkpoint with Message ID
验证者使用索引消息将检查点与邮箱发出的相应消息 ID 进行关联。
pub struct CheckpointWithMessageId {
/// existing Hyperlane checkpoint struct
#[deref]
pub checkpoint: Checkpoint,
/// hash of message emitted from mailbox checkpoint.index
pub message_id: H256,
}
他们还将这些增强的检查点发布到他们的同步器上。
您可以通过将验证器配置为具有上述合约的链,并观察它是否创建有效的签名来测试验证器。
Relayer
除了索引从邮箱分派的消息外,中继器还会在目标链上处理消息。这需要构建满足消息接收者 ISM 验证要求的元数据,并签署处理消息的交易,以在目标邮箱上处理消息。
Metadata Builders
每个模块类型都意味着消息验证成功所需的不同元数据格式。中继器需要实现每个模块特性(例如 multisig)来实现。
Message Processor
中继器将尝试在目标邮箱上处理消息(参见message processor)。 如果
- 消息接收方 ISM 返回一个未知的模块类型
- 模块类型已知,但元数据验证失败
- 元数据验证成功,但是进行燃料估算消息处理失败
那么消息将被推送到指数回退重试队列。中继器依赖于 mailbox 和 ism 特性的实现来进行这些检查。
Gas Payment Enforcement
中继器在处理目标链上的消息之前,可能还需要在源链上为特定的消息ID支付燃料费用。为了实现这一点,他们必须部署一个 IGP(燃料支付)合约,并将其地址设置为受益者,然后索引燃料支付事件。请参阅gas payment enforcement trait。我们建议开始时不使用气体支付强制执行策略,然后逐步支持限制性更强的策略。
Testing
一旦您实现了中继器的MVP(最小可行产品),您可以创建一个端到端测试,
- 启动本地源链和目标链
- 将您的智能合约部署到两条链上
- 在源链上运行验证者
- 在两个链之间运行中继器
- 请注意,验证器观察到该消息在源链上布时,会创建签名,然后中继器通过在目标链上指定验证者的ISM适当地处理您的消息。
查看end-to-end test on the Rust codebase 以获得灵感。
After validating the agents with local end-to-end tests, it is recommended that you also run end-to-end tests with real testnets.
3. Applications
Warp Router
令牌路由器应用程序,根据需要在不同域之间路由令牌。
transferRemote
将 amountOrId
令牌转移到目标域上的 recipient
。
function transferRemote(
// The Domain of the destination chain.
uint32 destination,
// The address of the recipient on the destination chain.
bytes32 recipient,
// The amount or identifier of tokens to be sent to the remote recipient.
uint256 amountOrId
) returns (
// The identifier of the dispatched message.
bytes32 messageId
);
Transfer Message
为了与其他链上的Warp Routes实现互操作性,转账消息的body
必须是一个按字节打包的TransferMessage
结构体。
struct TransferMessage {
// The recipient of the remote transfer
bytes32 recipient;
// An amount of tokens or a token identifier to be transferred
uint256 amountOrId;
// Optional metadata e.g. NFT URI information
bytes metadata;
}