Starknet 跨链消息为什么两个方向不一样:自动落地的充值与要自己领的提现 图 1
Starknet 跨链消息为什么两个方向不一样:自动落地的充值与要自己领的提现 · 图 1

使用 Starknet 桥接资产的人常会遇到一个奇怪现象:从以太坊充值到 Starknet,几分钟内资产就到了;而从 Starknet 提现回以太坊,到了链上却还要再做一笔交易才能拿到钱。这不是桥的 bug,而是 Starknet 官方文档明确写下的设计:它的跨链消息机制是异步的,也是不对称的。理解这两句话,充值延迟、提现卡住的排查思路就都有了。

异步:两边的合约都等不到对方

异步的意思是,无论是 Solidity 合约还是 Cairo 合约,在执行过程中都无法等待另一层消息的结果返回。以太坊合约调用发消息函数后就结束了,它不会停下来等 Starknet 执行完毕;反过来也一样。所以 Starknet 的消息不是函数调用,更像挂号信:寄出、登记、由对方(或收件人)在稍后某个时间处理。任何依赖”消息一定几秒内到”的合约逻辑,在 Starknet 的模型里都不成立。

不对称之一:L1 到 L2 是全自动的

示意(AI 生成,非数据图)

从以太坊向 Starknet 发消息,要调用 Starknet 核心合约的 sendMessageToL2 函数。核心合约会把消息参数哈希后写进自己的存储,标记这条消息确实被发送过,并记录发送者随调用附上、用于支付 L2 手续费的费用。消息参数包括五样东西:L1 发送者地址、L2 接收合约地址、函数选择器、Calldata 数组和一个消息 nonce。

之后一切自动进行:排序器监听到这条 L1 日志并等到足够的 L1 确认数后,会自动构造并执行一笔 L1 Handler 交易,去调用目标 L2 合约上带 l1_handler 装饰器的函数。这笔交易不由任何账户发起,所以它有两个特殊之处:其一,Calldata 的第一个元素永远是 L1 发送者的以太坊地址,合约要靠它识别”谁寄的信”;其二,在 L2 合约里查询调用者地址会返回 0x0,因为根本没有账户签名。这条 L1 Handler 交易随后被写进状态更新,核心合约在收到更新后清除存储里的消息记录,整个方向就此闭环。

不对称之二:L2 到 L1 只登记哈希,领钱要自己动手

反方向完全不同。L2 合约执行时调用 send_message_to_L1 系统调用,参数(L2 发送合约地址、L1 目标地址、payload)被排序器附加到包含这笔调用的区块上,随证明提交到 L1。核心合约做的只是把这条消息哈希对应的计数器加一,并发出事件。注意:排序器只负责把哈希登记上链,不会替你在 L1 上执行任何东西。

要真正”收到”这条消息,必须由 L1 上的接收合约调用核心合约的 consumeMessageFromL2 函数,并把消息参数原样带上。核心合约核对两边哈希一致、调用者确实是登记的目标地址后,才把计数器减一,消息至此才算消费完毕。这就是提现到了 Starknet 侧却要在以太坊再做一笔交易的原因——通常由桥的 relayer 代做,但如果 relayer 不活跃,路径就得用户手动走。

哈希与 nonce:同一条消息发两次也不混淆

两个方向的消息都在 L1 上用 keccak256 计算哈希。L2 发往 L1 的哈希拼接了发送方地址、目标地址、payload 长度与内容;L1 发往 L2 的哈希则多拼了一个 nonce。这个 nonce 由 L1 核心合约维护,每发一条消息递增一次,它的作用是防止同一条消息被重复发送两次时产生哈希碰撞、导致两笔 L2 执行互相混淆。L2 侧对应的 L1 Handler 交易哈希用 Pedersen 哈希计算,其中包含链 ID,防止两条网络的交易哈希互串。

卡住了怎么办:消费状态与五天取消窗口

如果 L1 到 L2 的消息迟迟没有被消费——比如 L2 目标合约有 bug 导致执行失败——协议留了一条退出路径:发起方可调用 startL1ToL2MessageCancellation 声明取消意图,等待五天后调用 cancelL1ToL2Message 完成取消并拿回资产。这五天延迟是为了防止有人反复发送又取消消息、干扰排序器的活跃性。自 Starknet 0.14.0 版本起,执行失败的 L1 Handler 会在区块里以 REVERTED 状态可见,而不再只记录在排序器的数据服务里,这让按 L1 交易哈希追踪消息状态(例如用 getMessageStatus 类接口)比以前容易。

排查顺序也因此清晰:先查 L1 上消息哈希是否登记(发送是否成功),再查是否已出现在 L2 的 L1 Handler 交易里(排序器是否投递),最后查该交易的执行状态是成功还是 REVERTED(合约侧是否吞了消息)。三个问题各对应一段责任边界:发送方、排序器、目标合约。

费用上也有固定的不对称:L2 向 L1 发消息因为要在 L1 存储写哈希,固定消耗约 20000 L1 gas;消息被消费时的存储清除另计约 5000 L1 gas;L1 到 L2 的 L2 手续费则在调用 sendMessageToL2 时随 ETH 一并在 L1 上付清,排序器在投递时收取。

风险提示:本文内容为区块链协议机制的科普性介绍,不构成任何投资建议、收益承诺或买卖时机判断。链上操作涉及资产安全,跨链与提现流程受协议版本、网络状态与合约升级影响,操作前请以官方文档和链上实际状态为准,并通过小额测试验证路径。