本文介绍了Relayer.sol,一个用于在Forge测试环境中进行多链端到端测试的工具。通过使用Relayer.sol,开发者可以在Forge测试中模拟跨链消息传递,无需外部relayer或脚本,简化了跨链应用的测试流程。文章还介绍了如何配置Supersim,编写跨链测试,以及一些高级用法和常见问题。
Relayer.sol
为你的 Forge 测试设置带来一流的多链端到端测试。通过继承抽象的 Relayer
测试助手,你的测试可以启动多个网络的 fork,发出真实的 L2ToL2CrossDomainMessenger
事件,并在同一个 forge test
进程中传递它们 —— 无需外部 relayer 或脚本粘合代码。在这篇文章中,你将学习如何:
从运行的 Supersim 端点连接 forked RPC URLs
在多个链上部署和执行合约
通过一次调用传递所有消息(或仅传递一个子集)
像其他单元测试一样,断言目标链上的状态变化
Superchain Interop 承诺改变去中心化应用程序开发的范例。这项令人兴奋的改进将允许在 Superchain interop 集中的链之间进行低延迟、无缝的消息传递和资产桥接。当你寻求为这个未来构建时,你需要提升你的跨链测试工作流程。
Superchain 的 interop 工作流程是事件驱动的:合约在 L2ToL2CrossDomainMessenger
上调用 sendMessage()
,messenger 发出 SentMessage
,一个链下 relayer 拾取该事件并向目标链提交一个执行交易 - 只有这样,payload 才会运行。
Relayer.sol
将该 relayer 逻辑直接嵌入到 Forge 中,消除了你在本地 CI 运行中尝试的不可靠的休眠或手动 cast
命令。
Relayer.sol
将跨链测试从“运行两个不相交的测试套件并希望桥能工作”转变为“在 Forge 内部证明消息确实在链 B 上执行”。它消除了外部依赖,减少了样板代码,并使你的测试环境更接近主网的现实。
Relayer.sol 显著简化了跨链测试流程
forge install ethereum-optimism/interop-lib
Relayer.sol
位于 repo https://github.com/ethereum-optimism/interop-lib 中的 src/test/Relayer.sol
下。
安装 Supersim
brew install ethereum-optimism/tap/supersim # macOS/Linux
启动一个原生的 Superchain
supersim
Supersim 启动三个本地 anvil 节点,预先部署 Superchain interop 合约,并暴露 JSON-RPC 端点:
链 | ID | RPC URL |
---|---|---|
L1(主网) | 900 | http://127.0.0.1:8545 |
L2-A | 901 | http://127.0.0.1:9545 |
L2-B | 902 | http://127.0.0.1:9546 |
有关 Supersim 以及你可以自定义本地开发工作流程的各种方法的更多信息,请查看 Supersim 文档。
在 foundry.toml
中告诉 Foundry 这些 RPC
[rpc_endpoints]
l2a = "<http://127.0.0.1:9545>"
l2b = "<http://127.0.0.1:9546>"
需要 devnet 吗? 如果你想试用 interop devnet,或者通过更改你的 foundry.toml
以指向这些 devnet 端点,将你的项目从本地开发升级到 devnet,请跳过 Supersim(有关最新的端点,请参阅文档):
[rpc_endpoints]
devnet0 = "<https://interop-alpha-0.optimism.io>"
devnet1 = "<https://interop-alpha-1.optimism.io>"
下面是参考 CrossChainIncrementer.t.sol
测试的简化版本:
contract IncrementerTest is Relayer {
/**
* 0. Constructor – 将 Supersim RPC URLs 传递给 Relayer,以便它可以映射
* chainIds ↔ forkIds 在幕后。
*/
constructor() Relayer(_rpcUrls()) {}
function _rpcUrls() internal view returns (string[] memory urls) {
urls = new string[](2);
urls[0] = vm.rpcUrl("l2a"); // source
urls[1] = vm.rpcUrl("l2b"); // destination
}
// 1. Fork 标识符
uint256 l2aFork;
uint256 l2bFork;
// ──────────────── 2. 合约Handle ────────────────
Counter src; // lives on l2a fork
Counter dst; // lives on l2b fork
function setUp() public {
// Foundry forks
l2aFork = vm.createFork(vm.rpcUrl("l2a"));
l2bFork = vm.createFork(vm.rpcUrl("l2b"));
// 2. 在每个链上部署合约
vm.selectFork(l2aFork);
src = new Counter();
vm.selectFork(l2bFork);
dst = new Counter();
}
function testIncrementAcrossChains() public {
// 3. 在源链上构建消息
vm.selectFork(l2aFork);
L2ToL2CrossDomainMessenger(payable(CDM_ADDR)).sendMessage(
address(dst),
abi.encodeCall(dst.increment, ()),
100_000
);
// 4. 传递刚刚记录的所有内容
relayAllMessages();
// 5. 断言目标链
vm.selectFork(l2bFork);
assertEq(dst.count(), 1);
}
}
让我们来解读一下这里发生了什么。
vm.createFork
克隆远程状态;vm.selectFork
切换活动 fork。保留返回的 IDs。每当你需要在链之间跳转时,都需要它们。
Relayer
构造函数调用 vm.recordLogs()
意味着捕获每个发出的事件。你不需要自己添加这个;只需继承这个助手!
L2ToL2CrossDomainMessenger.sendMessage
为你提供开箱即用的重放保护和域绑定。像在链上一样使用它。
relayAllMessages()
通过 vm.getRecordedLogs()
拉取缓冲区,筛选 SentMessage
,并在正确的目的地 fork 上重新执行每个消息。
需要更多控制? 将 vm.log[]
的切片传递给 relayMessages()
,并决定哪些事件被传递。
一旦传递完成,切换到目的地 fork(再次 vm.selectFork("l2a")
)并像任何其他单元测试一样进行断言。因为一切都在进程中同步运行,所以没有竞争条件或轮询循环。
💡要查看此测试模式的实际应用,请查看这里!
有时你只想传递事件的子集。因为 SentMessage
日志不嵌入源链,所以你 必须 传递产生这些日志的 sourceChainId
。
Vm.Log[] memory logs = vm.getRecordedLogs();
relayMessages(slice(logs, 1, 3), 901); // only the second and third messages
当你需要缓存记录的日志以用于传递以外的其他用途时,此功能特别有用。因为 vm.getRecordedLogs()
消耗缓冲区,你可以获取一次,存储它,在原始事件上运行自定义断言/解码/模糊测试,然后将相同的切片(或过滤的子切片)馈送到 relayMessages()
中。这使你可以传递你关心的项目,重新传递同一消息以测试重放保护路径,或保留日志以用于覆盖率指标 - 所有这些都无需丢失数据或需要额外的链上发送。
Interop 库还公开了一个 Promise
原语来保证传递语义;早期的测试套件位于 Promise.t.sol
中。期待该助手很快获得一流的 Promise 实用程序!
缺少预部署: 在没有 messenger 的本地节点上运行将会回滚。坚持使用 Supersim 和 devnets!
日志缓冲区消耗: 每次调用 vm.getRecordedLogs()
都会 消耗 缓冲区。如果你需要多次传递,请缓存它。
只需不到 40 行样板代码,你现在就可以进行实际的、确定性的多链测试,这些测试可以在几秒钟内运行:
supersim &. # 一次性启动
forge test -vvvv. # 一切都在本地传递
在幕后,Forge:
在 supersim 上 fork 两个 L2
执行你的源链交易
在目标 fork 上重放日志
断言后置条件
……所有这些都不需要离开 EVM 或依赖外部基础设施。这就是 Relayer.sol
的力量。试一试,破坏一些消息,并在你的跨链逻辑进入生产环境之前使其加强。祝你测试愉快!
Interop 消息传递概述 Optimism Docs
手动 cast
传递教程 Optimism Docs
pyk 的多链 Forge 指南 Pyk.sh
完整的 recordLogs
/ getRecordedLogs
作弊码 F oundry Book
Superchain devnet 工具文档 Optimism Docs
Supersim 文档 & CLI 参考 Optimism Docs
Supersim GitHub README GitHub
在加密货币领域以及在 OP Labs,我们拥有一项与众不同的工作。风险巨大,我们试图做的事情的复杂性也是如此。但是,有时,简单的流程可以发挥重要作用,并使我们能够进一步发展。
在 OP Labs,我们正在招聘 准备在技术和安全的最前沿工作的人员。如果你想在一个世界级的团队中从事世界级的项目,请联系我们!
- 原文链接: optimism.io/blog/end-to-...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!