构建指南
一个新链架构的 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);
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 test case here.
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
进行测试,您可以静态地将其设置为接受或拒绝任何消息。参见Foundry 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
所有代理必须索引来自源邮箱的邮件。在solid邮箱中,我们为每一个已发送的消息(https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/59e89afc5cbdec5362da5e13327eab4cb640b6b5/solidity/contracts/Mailbox.sol#L221-L222)发出一个事件。其他链可能有不同的方式来显示这些信息,但是代理必须能够可靠地获得消息内容,并且具有一致的顺序——参见[消息索引器](https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/rust/hyperlane-core/src/traits/indexer.rs)特征。
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,
}
验证器使用邮箱trait上的最新检查点方法从邮箱获取最新的检查点,并使用检查点同步器trait将签名提交到一些高度可用的存储。
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
中继器将尝试处理目标邮箱上的消息(参见消息处理器)。如果:
- 消息接收方 ISM 返回一个未知的模块类型
- 模块类型已知,但元数据验证失败
- 元数据验证成功,但是进行燃料估算消息处理失败
那么消息将被推送到指数回退重试队列。中继器依赖于 mailbox 和 ism 特性的实现来进行这些检查。
Gas Payment Enforcement
中继器在处理目标链上的消息之前,可能还需要在源链上为特定的消息ID支付燃料费用。为了实现这一点,他们必须部署一个 IGP(燃料支付)合约,并将其地址设置为受益者,然后索引燃料支付事件。请参阅gas payment enforcement trait。我们建议开始时不使用气体支付强制执行策略,然后逐步支持限制性更强的策略。
Testing
一旦你实现了中继器的MVP,你应该创建一个端到端测试:
- 转换本地链和目的链。
- 将您的合约部署到两个链上。
- 运行起源链的验证器。
- 在两条链之间运行中继器。
- 请注意,在发送源链上的报文时,验证器会观察报文、创建签名,中继器会通过指定目的链上验证器的 ISM 适当处理报文。
请参阅 Rust 代码库的端到端测试 ,从中获得启发。
通过本地端到端测试验证代理后,建议您还通过真实测试网运行端到端测试。
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;
}