跨链消息传递到底怎么工作的?给开发者讲清这件事
我有一个朋友是 Web3 开发者,他做过 DeFi 合约、写过 Solana 程序,但 2026 年初接到一个跨链项目时,他在第一周一直没法把"跨链消息到底怎么传过去"这件事在脑子里跑通。我意识到这种困惑很普遍——大家在用桥的时候只关心几秒到账几块钱,从来没在脑子里画过那张图。如果你打算自己写一个 OApp(Omnichain Application)或者一个 GMP 应用,那张图必须画明白。
这篇文章就是写给那位朋友以及所有处在同样位置的开发者。

一句话定义 GMP
General Message Passing(通用消息传递,简写 GMP)指的是:在源链上的智能合约可以触发一段任意数据 payload,被可靠地传送到目标链,由目标链上的另一个合约接收并执行。
它跟"代币桥"的区别是核心的:代币桥处理的是"资产"(锁定一份资产,在目标链铸造对应的副本),GMP 处理的是"任意数据"。代币桥是 GMP 的一种特殊应用,但 GMP 比代币桥通用得多——你可以传过去一笔 swap 指令、一笔治理投票、一个 NFT 元数据更新、甚至一段调用其他合约的字节码。
所有现代跨链协议(LayerZero、Wormhole、Axelar、Chainlink CCIP、Hyperlane)都是 GMP 协议,桥只是它们之上的应用。
四步框架
我把 GMP 协议拆成四个阶段。这个框架适用于所有主流协议,只是各家在每一阶段的具体实现不同:
- 源链 event 触发 — 应用合约发出消息事件
- 链下 relayer 监听并传递 — 一个或一组链下节点把消息搬到目标链
- 验证集签名/证明 — 一组验证者证明这条消息是真的、未被篡改的
- 目标链合约执行 — 目标链上的端点合约调用接收方合约的指定方法
不同协议的差异主要在第 3 步:谁来验证、怎么验证、验证错了承担什么后果。
第一步:源链上发生了什么
假设你在以太坊主网上写了一个合约,想给 Arbitrum 上的某个合约发一条消息。你的代码大致是这样:
function sendCrossChain(uint256 dstChainId, address dstAddress, bytes memory payload) external payable {
Endpoint.send{value: msg.value}(
dstChainId,
abi.encodePacked(dstAddress),
payload,
payable(msg.sender),
address(0x0),
bytes("")
);
}
这里的 Endpoint 是协议方提供的合约。当你调用 Endpoint.send,几件事情会发生:
- Endpoint 计算这条消息的 nonce(防重放)和当前的应用配置
- Endpoint 触发一个
PacketSent(nonce, payload, ...)事件 - Endpoint 向当前协议的"DVN/Guardian/验证者"集合发出隐式的"请求验证"信号
- 你支付了 gas fee + 消息费(消息费由协议方收取)
关键是:消息本身没有"打包发送"这一步。它只是变成了一条以太坊主网上的 log。后面的工作由链下角色完成。
第二步:Relayer 是怎么干活的
Relayer(在 LayerZero 里叫 Executor,在 Wormhole 里叫 Spy,在 Axelar 里叫 Relayer,在 CCIP 里是 DON 节点之一)的核心工作是:
- 持续监听源链上所有 Endpoint 合约发出的
PacketSent事件 - 从事件中提取目标链、目标地址、payload、nonce
- 把这条消息包装成目标链能理解的格式
- 调用目标链的 Endpoint 合约,提交"请验证并执行这条消息"
Relayer 是无权限的——任何人都可以跑一个 Relayer,任何人都可以为任何消息提交。但要让消息真正在目标链上被接受,它必须配套提交验证证明,这就涉及到第 3 步。
| 协议 | Relayer 形态 | 是否需要抵押 |
|---|---|---|
| LayerZero V2 | Executor + DVN 分离 | DVN 可能要求抵押 |
| Wormhole | Spy 节点公开 | 不抵押 |
| Axelar | Relayer 任何人可跑 | 不抵押 |
| Chainlink CCIP | Executing DON 节点 | DON 节点抵押 LINK |
| Hyperlane | Relayer 模块化 | ISM 决定 |
Relayer 收什么钱?Relayer 的收入来自源链上用户支付的"执行 gas 费"。用户在源链上发送消息时,需要预付目标链的执行 gas(用源链原生币计价,折算)。Relayer 拿这笔钱替用户在目标链上付 gas、调用合约,差额是它的利润。
第三步:验证集做什么
这是 GMP 协议设计上分歧最大的地方。我把主流方案做了一张对照:
| 协议 | 验证机制 | 信任假设 |
|---|---|---|
| LayerZero V2 | DVN(去中心化验证网络)+ 应用可配 | 信任你选的 DVN 集合 |
| Wormhole | 19 个 Guardian 多签 | 信任 13/19 不勾结 |
| Axelar | PoS 验证者签名 | 信任 AXL 质押者大多数 |
| CCIP | Committing DON + RMN 双重验证 | 信任 DON + RMN 不同时被攻陷 |
| Hyperlane | ISM(可插拔安全模块) | 应用自定义 |
LayerZero V2 的设计是最灵活的,应用可以自己选 1 到 N 个 DVN 来验证消息。比如你可以要求"必须由 Polyhedra ZK DVN + Google Cloud DVN + 自己的 DVN 三家都签名,消息才算有效"。这种"安全栈可配"是 V2 的核心卖点。
Wormhole 19 个 Guardian 的多签门槛是 13(>2/3),从 2022 年 Wormhole 黑客事件之后没有再发生过被攻破的情况。Wormhole 2025 年新增了ZK 信标验证作为额外保障,但 Guardian 集仍然是主防线。
CCIP 是唯一一个"经济安全 + 行为安全 + 治理熔断"三层的协议,代价是中心化程度更高,RMN 决策可由人工触发。

第四步:目标链执行
消息到达目标链后,目标链的 Endpoint 合约会:
- 验证 relayer 提交的证明(签名集、ZK proof,或验证集签名)
- 检查 nonce 是否合法、是否重复
- 调用目标地址的指定方法,通常是
lzReceive/_handle/executeMessage这样的入口 - 把 payload 作为参数传入
接收合约必须实现协议规定的接口。以 LayerZero 为例:
function _lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _message,
address _executor,
bytes calldata _extraData
) internal override {
// 你的业务逻辑
require(_origin.sender == bytes32(uint256(uint160(trustedSender))), "untrusted sender");
// 解析 _message 并执行
}
最关键的一件事:接收合约必须验证 sender 是受信任的。否则任何人都能伪造来自任意源链的消息攻击你的合约。这是 GMP 应用最常见的安全漏洞——开发者忘了检查 _origin.sender。
几个常见误解
误解一:跨链消息是"同步"的。它不是。从你发起 send 到目标合约被调用,中间有几秒到几分钟的延迟,你的源链合约不能在 send 之后立刻读取目标链的状态。
误解二:消息一定会送达。不一定。如果目标链 gas 远高于你预付的金额,relayer 不会接单,消息会卡在"已发送但未执行"的状态。你需要 retry 或补 gas。
误解三:消息顺序一定是 FIFO。LayerZero V1 是 FIFO,V2 默认不是。如果你的业务依赖顺序,需要在应用层加 nonce 检查。
误解四:消息一定能反向回应。GMP 是单向的。如果你要"调用 → 等结果",你必须在目标链回发一条消息,这是两笔独立的 GMP 操作。
理解这些之后,你才知道为什么应用层架构会变得复杂。一个简单的"跨链 swap"在实现层面会变成"源链发送消息 → 目标链执行 swap → 目标链发送结果消息 → 源链确认完成"四步。
怎么开始动手写
如果你打算写一个 OApp,LayerZero V2 文档最完整、社区最大。推荐路径:
- 用 LayerZero CLI 起
oapp-evm模板 - 本地模拟器跑通 hello world
- 部署到测试网(Sepolia ↔ Arbitrum Sepolia)
- 用 LayerZeroScan 看消息状态
- 加安全检查(trusted sender、nonce、emergency pause)
- 最后再考虑主网
如果目标链含 Cosmos,选 Axelar GMP;给机构用,选 CCIP。方案选择参考 Axelar 和 Chainlink CCIP 哪个更值得用。
写代码之前,如果对桥还没建立直觉,先看 跨链桥使用指南 和 LayerZero 跨链使用教程;Wormhole 与 LayerZero 的设计差别见 对比文章。
给开发者的几句直话
跨链开发跟同链开发不是同一个心智模型。最常见的坑不是技术 bug,而是没想清楚消息可能失败、可能延迟、可能乱序、可能不可逆。
我见过一个团队把 4 万美元 USDC 卡在跨链消息里整整 11 天,只因为接收合约 require 写错一个常量,relayer 一直 revert。同链开发里不会发生,GMP 里几乎每个团队都会遇到一次。
理解这一点,你就明白为什么主流协议都把"应用层安全"放在文档第一页:协议保证消息能传到,但消息执行得对完全是你的责任。守住这条,你的第一个 GMP 应用就已经领先大多数项目。