Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
网络与协议指南阅读约 11 分钟

ZK Rollup 架构:有效性证明如何结算批次以及如何验证

一份技术指南,涵盖 zk-rollup 批次执行、证明验证、数据可用性,以及通过 RPC 检查 rollup 状态转换的可复现流程。

TL;DR

zk-rollup(零知识 rollup)在基础链之外执行交易,压缩产生的状态变化,并发布一个加密有效性证明,基础链合约在验证该证明后才接受新的状态根。本文端到端解释其机制:排序器批处理、状态承诺、证明系统、数据可用性和最终性。然后提供一套可运行的基于 RPC 的流程,用于观察批次提交并确认 rollup 的链上状态根与证明验证的转换一致。重点在于可复现的验证方法,而非推荐特定的 rollup 或端点提供商。

范围与核心心智模型

zk-rollup 是一种二层构造,其中交易执行发生在基础链之外,但正确性由链上的简洁证明来强制保证。基础链上的 rollup 合约存储一个状态根。要推进该根,运营者提交批次承诺加上有效性证明;合约验证证明,如果有效,则更新根。由于证明由基础链检查,用户无需信任运营者的诚实性来保证状态正确性,只需信任其活性和抗审查行为。

本文将 zk rollup 视为一种架构而非单一产品。不同的 zk-rollup 在证明系统(SNARK 或 STARK)、数据可用性策略(calldata、blob 或独立的 DA 层)以及虚拟机(EVM 等效或自定义)方面有所不同。下面的验证工作流编写得可以适配任何暴露标准 JSON-RPC 和基础链合约地址的 rollup。

本指南回答的实际问题是:给定一个 rollup RPC 端点和一个基础链 RPC 端点,如何观察一个批次、确认证明已被接受,并核对 rollup 报告的状态与基础链合约?

  • 执行:链下,由排序器或证明者网络执行。
  • 结算:在基础链上,通过验证者合约。
  • 数据可用性:发布交易数据以便重建状态。
  • 最终性:当有效性证明在链上验证通过时达成。

先决条件与假设

你需要两个 RPC 端点:一个用于 rollup 网络,一个用于其基础链(例如以太坊主网)。你还需要 rollup 的结算合约地址,最好还有其批次提交事件的 ABI。这些在每个 rollup 的文档中发布;本指南不假设特定的 rollup。

这些流程使用 curl 和 Python,仅使用标准库加上 HTTP 调用。将 RPC_URL、BASE_RPC_URL 和 ROLLUP_CONTRACT 替换为你正在检查的网络中的值。切勿将私钥粘贴到 shell 历史记录中;对于只读验证,根本不需要密钥。

如果你是在选择端点而非验证端点,具体的提供商决策属于 RPC Assistant;本页专注于方法论。有关更广泛的端点选择框架,请参阅 RPC 端点指南和多链 RPC 端点概述。

  • Rollup RPC URL(只读即可)。
  • 基础链 RPC URL。
  • 结算合约地址和批次事件主题。
  • 包含至少一个已知批次提交的区块范围。

zk-rollup 批次如何从执行走向最终性

生命周期有五个阶段。首先,用户向 rollup 提交交易,排序器对其进行排序。其次,排序器针对当前状态执行批次,产生新的状态根和状态差异列表。第三,证明者生成有效性证明,证明在先前状态上执行批次得到新根。第四,运营者将压缩的交易数据和证明发布到基础链。第五,验证者合约检查证明,成功后写入新根。

zk-rollup 和 optimistic rollup 的区别在于第五阶段。Optimistic rollup 立即接受根,并依赖欺诈证明窗口,在此期间任何人都可以挑战。Zk-rollup 仅在证明验证后接受根,因此一旦证明落地,提款不需要挑战期。这就是 zk-rollup 有时被称为有效性 rollup 的原因。

数据可用性是与有效性分开的轴。证明可以是有效的,而底层交易数据被扣留,这将阻止用户重建状态或独立退出。成熟的 zk-rollup 在基础链上发布足够的数据(calldata 或 blob)以重建状态。评估 zk-rollup 时,请同时检查证明系统和 DA 承诺。

通过基础链 RPC 观察批次提交

最直接的验证是读取结算合约的日志。批次提交通常发出一个事件,包含先前状态根、新状态根以及对批次数据的承诺。确切的事件签名因 rollup 而异,因此从项目仓库获取 ABI 并用 keccak256 计算主题。

以下 Python 脚本在区块范围内查询给定合约和主题的日志。它打印每个匹配的日志,以便你检查状态根字段。它不解码有效载荷,因为解码取决于 rollup 的 ABI;那是下一步。

import json, urllib.request

BASE_RPC_URL = "BASE_RPC_URL"
ROLLUP_CONTRACT = "ROLLUP_CONTRACT"
TOPIC0 = "BATCH_EVENT_TOPIC"
FROM_BLOCK = "0x0"
TO_BLOCK = "latest"

def rpc(method, params):
    body = json.dumps({"jsonrpc":"2.0","id":1,"method":method,"params":params}).encode()
    req = urllib.request.Request(BASE_RPC_URL, data=body, headers={"Content-Type":"application/json"})
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.loads(r.read())["result"]

logs = rpc("eth_getLogs", [{
    "address": ROLLUP_CONTRACT,
    "fromBlock": FROM_BLOCK,
    "toBlock": TO_BLOCK,
    "topics": [TOPIC0]
}])

for log in logs:
    print(log["transactionHash"], log["blockNumber"], log["data"][:130])

将 rollup 状态根与基础链核对

获得批次交易哈希后,在基础链上获取交易收据并确认状态为 0x1。然后从 rollup RPC 读取 rollup 的当前状态根(许多暴露诸如状态根 getter 或区块标签的方法),并与结算合约记录的最新根进行比较。不匹配意味着你要么读取了不同的区块高度,要么 rollup RPC 滞后。

以下 curl 调用说明了读取模式。第一个获取收据;第二个调用 rollup RPC 方法。将方法名替换为你的 rollup 文档中记录的方法名。

curl -s -X POST "$BASE_RPC_URL" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt","params":["BATCH_TX_HASH"]}'

curl -s -X POST "$RPC_URL" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"ROLLUP_STATE_ROOT_METHOD","params":["latest"]}'

用于记录验证运行的结果表

将每次验证运行记录在表格中,以便结果在不同 rollup 和时间上具有可比性。不要填写你未测量的数字;在运行完成前将单元格留空。下面是一个建议的模式,随后是填充它的步骤。

步骤:(1) 从日志查询中选择一个批次交易哈希;(2) 记录基础链区块号和收据状态;(3) 同时记录 rollup RPC 区块高度;(4) 从两个来源读取状态根;(5) 标记匹配或不匹配;(6) 记录批次包含与你读取之间的实际时间。在一天中的三个不同时间重复,以暴露 RPC 滞后模式。

  • | 运行 | 批次交易 | 基础区块 | 收据状态 | Rollup 高度 | 根匹配 | 观察到的滞后 |
  • |-----|----------|------------|----------------|---------------|------------|--------------|
  • | 1 | | | | | | |
  • | 2 | | | | | | |
  • | 3 | | | | | | |

预期验证结果及其含义

健康的运行显示基础链收据成功,rollup RPC 高度等于或高于批次的 rollup 高度,并且在同一高度上 rollup RPC 和结算合约的状态根相同。如果根匹配,则证明验证的转换被两个数据源一致地反映。

如果收据成功但根不同,首先确认你比较的是相同的区块高度。Rollup RPC 和基础链 RPC 独立推进,滞后的 rollup 节点可能报告较旧的根。在得出结论有问题之前,在固定高度重新读取两者。在固定高度持续不匹配是一个严重信号,需要检查 rollup 的状态页面和合约的事件历史。

如果收据失败,证明被拒绝或交易回滚;批次未最终确定。检查回滚原因和证明者的公开状态(如果可用)。

排查常见的验证失败

大多数失败是观察性的而非协议层面的。下表将症状映射到可能的原因和下一步检查。

对于端点级别的可靠性问题,例如超时或响应不一致,故障排除方法与任何 JSON-RPC 工作负载相同:比较两个独立的端点,固定区块高度,并记录请求 ID。如果你需要具体的端点推荐而非方法,RPC Assistant 单独涵盖这一点。

  • 日志结果为空:合约地址错误、主题错误或区块范围在批次窗口之外。
  • 未找到收据:交易仍在待处理或提交到了不同的链 ID。
  • 根不匹配:高度不匹配、rollup RPC 滞后或基础链重组。
  • 解码错误:ABI 与部署的合约版本不匹配。
  • 超时:端点速率限制或过载;使用退避和第二个端点重试。

zk-rollup 的局限性与权衡

有效性证明提供了强大的正确性保证,但并非没有代价。证明计算量大,证明成本随批次复杂性扩展。对于某些工作负载,每笔交易的摊销成本很低;对于其他工作负载,尤其是小批次,它可能占主导地位。这是设计权衡,不是缺陷,这就是批次大小和证明递归重要的原因。

EVM 兼容性是另一个权衡。EVM 等效的 zk-rollup 旨在无需修改地运行现有合约,但面临更高的证明成本;自定义 VM 可以更高效,但需要移植。数据可用性选择也很重要:发布完整的 calldata 最大化透明度,但成本高于 blob 或外部 DA 层。

最后,zk-rollup 仍然依赖基础链进行结算,并依赖排序器实现活性。有效的证明不能防止审查或停机;这些是操作属性,你应该单独评估,通常通过监控多个 RPC 端点。

下一步:从验证到生产监控

一旦你能复现单次验证运行,就将其自动化。以固定间隔调度日志查询和根比较,将结果存储在上面的表格模式中,并在持续不匹配或收据失败时发出警报。这将一次性检查转变为轻量级结算监控器。

对于频繁读取 rollup 状态的生产工作负载,考虑使用专用节点,这样你的读取不受共享速率限制的影响。OnFinality 的专用节点产品和 API 服务是基础设施入口点;定价详情在 RPC 定价页面上,支持的网络列在网络目录中。

如果你的下一个问题是针对特定 rollup 或基础链使用哪个端点,请将该决策交给 RPC Assistant,而不是从本方法页面猜测。

永远不用担心基础设施

OnFinality 消除了 DevOps 的繁重工作,让您能够更聪明、更快地构建。

开始