跨链轻客户端真正贵的不是 Merkle Proof

跨链状态验证最容易被低估的成本,不是验证一条 Merkle inclusion proof,而是验证“这条状态根到底是不是源链共识认可的状态根”。如果目标链是 EVM,源链是 Tendermint、Solana、NEAR 或其他使用 Ed25519、不同哈希函数、不同状态树布局的系统,目标链合约要么直接重放源链轻客户端逻辑,要么相信一组外部验证者,要么把源链共识检查压缩成零知识证明(ZK proof)。ZK-Light Client 的价值就在第三条路径:把昂贵的异构签名、验证者权重、区块头更新和状态根检查放到链下证明,在目标链只验证一个简洁证明。

但这个方案不是“用 ZK 就免费”。真正的瓶颈是非原生字段算术(non-native field arithmetic)。Ed25519 的有限域、Solana 的哈希时间序列、Tendermint 的验证者投票权重、Ethereum 的 BLS12-381 和 BN254/小字段 STARK 体系之间并不天然同构。证明系统的底层字段不匹配时,一个源链签名验证会被拆成大量 limb、range check、carry 约束和模约减。对 [AllSwap 跨链交换](/exchange-swap) 这类路由入口而言,这决定了“信任最小化跨链证明”能否用于真实报价、退款和到账确认,而不是只停留在研究演示。

系统模型:目标链只接受压缩后的共识事实

本文讨论的是跨异构链状态验证,不讨论中心化托管、价格预言机或纯多签桥。系统包含四个状态层:

- 源链 `S`:产生区块头、验证者签名、状态根和最终性证明; - 证明者 `P`:离线同步源链头部,构造证明电路并生成 ZK proof; - 目标链验证合约 `V`:验证 proof,并更新已接受的源链共识状态; - 应用合约 `A`:基于 `V` 中的可信状态根验证跨链消息、资产释放或退款条件。

安全目标不是让目标链“理解源链的一切”,而是让目标链只接受一个可验证命题:

`VerifyZK(proof, publicInputs) = true`

其中 `publicInputs` 至少包含 `oldRoot, newRoot, sourceHeight, validatorSetHash, appStateRoot, messageCommitment`。如果 proof 正确,目标链知道:从已信任的旧共识状态出发,源链在某个高度产生了新状态根,并且该状态根包含目标应用需要的消息承诺。目标链不需要重新验证几十个 Ed25519 签名,也不需要下载所有中间区块。

这套模型仍然继承源链的安全假设。若源链验证者超过拜占庭阈值,ZK proof 只能忠实证明“源链已经接受了错误状态”;若证明者停止服务,目标链不会自动前进;若 public input 绑定错误,proof 可能证明了一个正确但对应用无意义的状态。ZK-Light Client 降低的是链上验证成本,不是消除最终性、数据可用性和同步活性问题。

非原生字段为什么会放大约束

证明系统通常在一个固定有限域 `F_p` 上工作。若要在电路里验证另一个有限域 `F_q` 的运算,且 `p != q`,就必须把 `q` 域元素表示为多个 `p` 域 limb。例如把一个 255-bit Ed25519 标量拆成若干 64-bit 或 51-bit limb,再用约束证明乘法、加法、进位和模 `q` 约减都正确。

一个普通乘法 `z = x * y mod q` 在原生域里可能只是一条乘法约束;在非原生域里会变成:

`x = sum(x_i * 2^{w*i})`

`y = sum(y_i * 2^{w*i})`

`x*y = z + k*q`

其中 `x_i, y_i, z_i, k_i` 都要做范围约束和 carry 约束。若 limb 数为 `m`,朴素乘法会出现 `O(m^2)` 个部分积,后续还要处理进位传播和模约减。Ed25519 签名验证不是一次乘法,而是椭圆曲线点加、点倍乘、哈希挑战、标量乘法和等式检查的组合。约束数量自然会膨胀。

这也是为什么 ethresear.ch 上关于 IBC 到 Ethereum 的讨论会把 Ed25519 链上验证视为灾难性成本:直接在 EVM 里验证大量 Tendermint 验证者签名,gas 会随验证者数量线性放大。zkBridge 论文的方向是把这类工作移出链上,用 succinct proof 把链上验证压到一个可接受的常数级验证过程。Electron Labs、Succinct SP1 Tendermint 示例和 ZK-IBC 的共同思路,也都是把轻客户端的签名和头部更新逻辑放入可证明程序。

一个更工程化的判断是:非原生字段成本通常不由“签名数量”单独决定,而由 `签名数量 * 每个签名的椭圆曲线操作 * 每个操作的 limb 约束` 决定。如果验证者集合有 150 个节点,但只需累计超过 2/3 权重的签名,电路仍要处理签名选择、权重累加和位图约束。若为了隐藏参与签名者集合而引入额外承诺,约束还会继续增加。因此,ZK-Light Client 的性能瓶颈经常出现在看似低级的地方:limb 宽度选错、range check 没有用 lookup、哈希函数不证明友好,都会让 proof generation 从可接受变成不可运维。

Tendermint/IBC 是最典型的压力测试

Tendermint 轻客户端的核心不是“看见一个区块头”,而是检查新头部是否被足够验证者权重签名,并且验证者集变更没有越过信任期(trusting period)和拜占庭阈值。IBC ICS-07 规范把 Tendermint light client 建模成一个可更新的 client state 和 consensus state;CometBFT light client 规范则强调,轻客户端不能信任 full node 返回的数据,必须用提交签名和验证者集关系做校验。

这套逻辑放在原生环境里已经不简单,放进 EVM 更麻烦。原因有三层:

第一,签名算法不匹配。Cosmos/Tendermint 生态常见 Ed25519 或 secp256k1 组合,而 Ethereum 执行层历史上没有 Ed25519 预编译。用 Solidity 或 EVM bytecode 直接重放 Ed25519,成本不适合大规模 validator set。

第二,验证者集合是动态的。轻客户端要证明 `validatorSet_t` 到 `validatorSet_{t+1}` 的变更合法,不能只验证一个静态多签阈值。ZK 电路需要把验证者权重、地址、投票、哈希和集合更新绑定到 public input,否则目标链可能接受一个过时或伪造的集合。

第三,状态证明有两段。先证明头部被共识接受,再证明应用消息存在于 `appHash` 或某个承诺根下。很多桥攻击不是发生在 Merkle proof 本身,而是发生在“这个 root 是否可信”的上游。ZK-Light Client 正是把这两段串起来:共识状态更新给 root 赋予信任,应用 proof 再证明消息存在。

这也是 Tendermint/IBC 成为跨链 ZK 轻客户端压力测试的原因。它不是一个静态签名验证问题,而是一个随高度滚动的状态机问题。电路要知道当前信任锚、目标高度、跳跃验证策略、验证者集合 hash、下一集合承诺和应用根之间的关系。如果只证明某个 commit 签名有效,却没有证明这个 commit 能从目标链已保存的 trusted state 合法推进,目标链得到的只是一个孤立事实,而不是可更新轻客户端。

ZK-Light Client 的状态机

一个可执行级流程可以写成:

1. `sync_header(h)`:证明者从源链获取高度 `h` 的区块头、commit、验证者集合和应用状态根。 2. `check_trusting_period()`:确认旧信任状态仍在可更新窗口内。 3. `verify_commit()`:在证明电路内检查签名集合权重超过阈值。 4. `verify_validator_update()`:证明新验证者集合 hash 与头部承诺一致。 5. `verify_message_membership()`:证明目标跨链消息存在于应用状态根。 6. `prove_transition()`:输出 proof 和 public inputs。 7. `update_client()`:目标链验证 proof,写入新高度和可信 root。

目标链合约可以只保留极少状态:

`trustedHeight`

`trustedHeaderHash`

`trustedValidatorSetHash`

`trustedAppRoot`

`consumedMessageRoot`

这比传统链上轻客户端轻得多,但它把复杂性转移给了证明生成和电路审计。电路必须正确实现签名验证、hash、权重累加、集合更新和 membership proof;任何欠约束都可能让证明者构造“合法 proof + 错误 root”。因此,ZK-Light Client 的审计重点不是 Solidity 验证器合约本身,而是整个可证明程序的约束完备性。

目标链还需要存储回滚保护。若新 proof 的 `sourceHeight` 不高于当前 `trustedHeight`,或 `trustedHeaderHash` 与当前状态不衔接,合约应拒绝更新。若允许多条源链分叉并存,应用层必须明确选择哪个 fork 的 root 可以释放资产。对跨链交换而言,最简单的安全策略通常是单调高度更新加消息一次性消费:先更新可信 root,再验证消息 membership,再把 message id 标记为 consumed,最后执行资产释放或退款。

小字段 STARK、Plonky3 与递归压缩的取舍

Plonky3、BabyBear、Goldilocks 这类小字段证明系统对 CPU 友好,适合高吞吐 STARK 或递归证明。但它们验证 Ed25519、BLS12-381 或 Keccak/Merkle Patricia Tree 时,仍会面对非原生字段和非友好哈希的成本。小字段让 prover 的内存访问和多项式运算更快,却不会自动让源链密码学变成本地运算。

工程上常见三种路线。

第一,直接在电路中实现源链签名和 hash。这最通用,但约束数量最大,证明生成延迟最高。

第二,使用递归或聚合:把多个签名、多个头部更新、多个消息 inclusion proof 先在链下聚合,再生成一个最终 proof。zkBridge 的论文就强调用 proof system 组合和分布式 proving 降低实际成本。代价是系统复杂,失败时要定位是哪一级递归或聚合出错。

第三,等待目标链原生预编译降低验证成本。Ethereum Pectra 已把 EIP-2537 纳入升级,增加 BLS12-381 曲线操作预编译,这对 BLS 系证明和签名验证是重要改善。但它不能直接解决 Ed25519、Solana PoH、Tendermint 集合更新或非 EVM 状态树在电路中的成本。预编译降低的是某些目标链验证和应用密码学成本,不是把所有异构链都变成同构链。

递归压缩也不是免费午餐。若第一层证明系统适合快速证明 Ed25519,第二层证明系统适合在目标链便宜验证,两层之间还要证明 proof verifier 的正确执行。这个“证明另一个证明系统的验证器”本身会引入新的电路、约束和 trusted setup 或 FRI 参数。工程上通常要在三件事之间取舍:单次证明延迟、链上验证成本、系统可审计性。过度复杂的递归栈可能把链上 gas 降下来,却让故障定位和安全审计变得更难。

失败模式:proof 正确也可能不安全

第一类失败是 public input 绑定错误。证明电路可能确实验证了一个有效 header,但目标链合约没有把 `sourceChainId`、`height`、`appRoot`、`messagePath` 和 `recipient` 全部绑定到同一个 public input 域。结果是 proof 正确,应用语义错误。跨链路由里这会变成错误资产释放或错误退款。

第二类失败是过时 proof。轻客户端有 trusting period 和最终性窗口。如果 relayer 或 prover 延迟提交,目标链可能接受一个理论上可验证、但已经落后于源链安全窗口的状态。CometBFT 和 IBC 规范都把信任期作为核心安全参数;ZK 版本不能把它藏到链下。

第三类失败是证明服务中心化。ZK-Light Client 降低了验证成本,却可能让证明生成集中到少数高性能 prover。若所有跨链消息依赖同一个 prover 队列,系统活性会退化为“prover 是否在线”。这不是安全性破坏,但会影响到账、退款和报价 SLA。

第四类失败是电路欠约束。签名解析、range check、carry、hash domain separation、validator weight overflow,只要有一处约束缺失,就可能出现非法状态转移被证明为合法。ZK 桥的安全边界通常不在链上验证合约,而在电路和 witness 生成程序。

第五类失败是源链最终性误读。Solana、Tendermint、Ethereum、L2 的最终性语义不同。把所有链都抽象成“等待 N 个区块”会让路由错误估计风险。ZK proof 只能证明某个共识规则下的状态,不会替产品决定应该等待多久。

第六类失败是证明参数退化。STARK 的查询次数、FRI 轮数、递归层数、SNARK 的曲线安全等级和 trusted setup 选择,都会影响最终安全边界。如果产品层只展示“ZK verified”,却不区分证明系统参数、验证器版本和升级权限,用户会得到过度简化的安全信号。对跨链证明,版本管理和可追溯性必须像资产合约地址一样被索引。

AllSwap 为什么要关心 ZK-Light Client

对用户而言,跨链交换结果是“资产是否到账”;对路由系统而言,真正的问题是“到账依据是什么”。若路径依赖多签或外部中继,系统要为外部信任假设定价;若路径依赖 ZK-Light Client,系统要为证明延迟、源链最终性、验证合约 gas、prover 可用性和退款状态定价。

AllSwap 不需要把所有路径都改造成 ZK 轻客户端。更现实的做法是把证明类型纳入路由评分:

- 多签/委员会证明:低延迟,但外部信任更强; - 原生轻客户端:信任最小化,但链上验证和维护成本高; - ZK-Light Client:链上验证便宜,但 prover、电路和同步窗口成为新风险; - optimistic 证明:成本低,但需要挑战窗口和失败恢复。

这会影响用户能看到的报价质量。大额交易、低流动性路径、重要退款路径更适合使用更强证明;小额、低风险交易可以接受更快但信任假设更强的路径。AllSwap 的 [费用说明](/fees)、[/swap/usdt-erc20](/swap/usdt-erc20) 这类资产入口,以及 [/assets/usdc](/assets/usdc) 这样的资产页,最终都应该让用户知道:手续费之外,还有证明延迟、最终性窗口和异常退款成本。

可执行的路由状态可以抽象为:

`proofType = committee | nativeLightClient | zkLightClient | optimistic`

`routeScore = priceScore - latencyPenalty - trustPenalty - refundPenalty`

其中 `trustPenalty` 不应该是主观标签,而应来自可观测变量:验证器合约版本、proof verifier 是否可升级、最近 proof 延迟、prover 队列长度、源链最终性窗口、历史失败率和退款是否有链上证据。这样做不会把普通用户推入复杂参数,但能让产品在大额交易和高风险路径上自动偏向更强证明。

对无托管交换尤其重要的是退款归因。若目标链释放失败,系统必须回答失败发生在源链 burn/lock、证明生成、目标链 proof verification、消息 membership,还是资产合约执行。ZK-Light Client 能让其中一部分路径变得可证明:如果 proof 对应的源链消息确实存在,那么退款逻辑可以围绕目标链执行失败展开;如果 proof 根本没有生成或过期,用户应看到的是同步延迟,而不是资产状态不明。

监控层也要跟上。最小指标包括 `latestProvedHeight`、`sourceFinalizedHeight`、`proofLag`、`proofFailureRate`、`verifierVersion` 和 `pendingRefundNotional`。这些指标不必全部展示给用户,但应进入路由风控;否则系统可能继续把订单送入一个已经落后数小时的证明通道。证明缓存也要按源链高度和应用根隔离,不能把旧 proof 当成新消息的通用通行证,这是基本安全底线之一。

仍未解决的问题

第一,非原生字段算术的通用优化还没有统一收敛。不同证明系统在 limb 宽度、lookup、range check、carry 约束、递归压缩上选择不同,很难给出跨系统可比的成本模型。

第二,异构链状态树的统一抽象仍然困难。Tendermint 的 AppHash、Ethereum 的 MPT/Verkle 迁移、Solana 的账户模型、Move Object 模型都不是同一个证明接口。ZK-Light Client 不能只证明 header,还要把应用状态路径规范化。

第三,prover 市场的活性还没有成熟。若证明生成需要 GPU 集群或专用优化,谁来保证证明按时生成、谁承担延迟导致的退款损失、谁审计 witness 程序,都是开放问题。

第四,升级和 emergency halt 很难处理。源链升级验证者格式、hash 函数或状态根结构时,ZK 电路也要升级。若目标链 verifier 升级慢于源链,跨链状态可能停在旧高度。

第五,用户界面还没有表达证明强度的标准。用户不需要读懂 non-native field arithmetic,但需要知道某条路径是委员会证明、原生轻客户端、ZK-Light Client 还是 optimistic proof,以及失败时退款依据在哪里。

References

[1] zkBridge: Trustless Cross-chain Bridges Made Practical, Xie et al., ACM CCS 2022, https://arxiv.org/abs/2210.00264

[2] IBC ICS-07 Tendermint Client Specification, Cosmos IBC, https://github.com/cosmos/ibc/blob/main/spec/client/ics-007-tendermint-client/README.md

[3] CometBFT Light Client Verification Specification, CometBFT, https://github.com/cometbft/cometbft-rs/blob/main/docs/spec/lightclient/verification/verification.md

[4] A Tendermint Light Client, Braithwaite et al., 2020, https://arxiv.org/abs/2010.07031

[5] Bringing IBC to Ethereum using ZK-SNARKs, Electron Labs / ethresear.ch, 2022, https://ethresear.ch/t/bringing-ibc-to-ethereum-using-zk-snarks/13634

[6] Electron Labs NEAR Prover Contracts, Electron Labs, https://github.com/Electron-Labs/near-prover-contracts

[7] SP1 Tendermint Example, Succinct Labs, https://github.com/succinctlabs/sp1-tendermint-example

[8] Prague-Electra Pectra Upgrade, ethereum.org, https://ethereum.org/roadmap/pectra/

[9] EIP-2537: Precompile for BLS12-381 curve operations, Ethereum Improvement Proposals, https://eips.ethereum.org/EIPS/eip-2537

[10] Polygon Plonky3, Polygon Labs, https://polygon.technology/blog/polygon-plonky3-the-next-generation-of-zk-proving-systems-is-production-ready

常见问题

ZK 轻客户端能完全替代跨链桥验证者吗?

它能减少对外部委员会的依赖,把源链共识验证压缩成链上 proof verification。但它仍然依赖源链最终性、证明者活性、电路正确性和目标链验证合约安全,不是无条件替代所有桥风险。

非原生字段算术为什么会让 ZK 轻客户端变贵?

当证明系统字段与源链签名或哈希字段不一致时,电路必须用 limb、range check、carry 和模约减模拟外部字段。Ed25519 签名验证会因此膨胀成大量约束。

EIP-2537 是否解决了跨链 ZK 轻客户端的主要瓶颈?

EIP-2537 改善了 Ethereum 上 BLS12-381 曲线操作,对 BLS 签名和部分证明验证有帮助。但它不能直接消除 Ed25519、Tendermint 验证者更新或非 EVM 状态树在 ZK 电路中的成本。

AllSwap 路由为什么要关注 proof type?

不同证明类型对应不同信任假设、延迟、退款边界和失败模式。大额或高风险跨链交换应优先考虑更强证明;小额低风险路径可以在成本和速度上做不同取舍。

ZK 轻客户端最常见的失败模式是什么?

常见问题包括 public input 绑定错误、过时 proof、证明服务中心化、电路欠约束、源链最终性误读和证明参数退化。这些问题都需要在路由和监控层显式建模。

参考资料