Skip to main content

构建指南

一个新链架构的 Hyperlane构建由以下内容组成:

  1. Contracts: 公开应用程序开发人员用于发送和接收消息的接口
  2. Agents: 通过添加安全性并中继消息来操作协议
  3. Applications: 使用协议并展示其功能的应用程序

在开始之前,建议先查阅协议文档。

info

如果您想深入了解,请查看目前可用的一些 Hyperlane 实现:

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:

info

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);
info

在实现了这三个合约之后,您可以到达要测试的第一个里程碑,通过调用 Mailboxdispatch 函数将消息发送给接收者并判断接收者收到了消息,从而模拟消息传输。参见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
);
info

在实现MultisigISM之后,您将到达第二个里程碑,以测试您的邮箱仅在收件人的ISM返回true后才进行处理。您可以使用TestISM 进行测试,您可以静态地将其设置为接受或拒绝任何消息。参见Foundry test case here.

Interchain Gas Paymaster

燃料支付器用于支付目的链上信息处理所需的燃料。如果中继者愿意补贴信息处理费用,则不一定需要这样做。

payForGas

将msg.value存入作为向目标链中继消息的支付。

warning

虽然你可以指定一个 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,
}

他们还将这些增强的检查点发布到他们的同步器上。

tip

您可以通过将验证器配置为具有上述合约的链,并观察它是否创建有效的签名来测试验证器。

Relayer

除了索引从邮箱分派的消息外,中继器还会在目标链上处理消息。这需要构建满足消息接收者 ISM 验证要求的元数据,并签署处理消息的交易,以在目标邮箱上处理消息。

Metadata Builders

每个模块类型都意味着消息验证成功所需的不同元数据格式。中继器需要实现每个模块特性(例如 multisig)来实现。

Message Processor

中继器将尝试处理目标邮箱上的消息(参见消息处理器)。如果:

  • 消息接收方 ISM 返回一个未知的模块类型
  • 模块类型已知,但元数据验证失败
  • 元数据验证成功,但是进行燃料估算消息处理失败

那么消息将被推送到指数回退重试队列。中继器依赖于 mailboxism 特性的实现来进行这些检查。

Gas Payment Enforcement

中继器在处理目标链上的消息之前,可能还需要在源链上为特定的消息ID支付燃料费用。为了实现这一点,他们必须部署一个 IGP(燃料支付)合约,并将其地址设置为受益者,然后索引燃料支付事件。请参阅gas payment enforcement trait。我们建议开始时不使用气体支付强制执行策略,然后逐步支持限制性更强的策略。

Testing

一旦你实现了中继器的MVP,你应该创建一个端到端测试:

  1. 转换本地链和目的链。
  2. 将您的合约部署到两个链上。
  3. 运行起源链的验证器。
  4. 在两条链之间运行中继器。
  5. 请注意,在发送源链上的报文时,验证器会观察报文、创建签名,中继器会通过指定目的链上验证器的 ISM 适当处理报文。

请参阅 Rust 代码库的端到端测试 ,从中获得启发。

tip

通过本地端到端测试验证代理后,建议您还通过真实测试网运行端到端测试。

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;
}