新奇隐私币项目 Horizen 团队曾经声称:“如果中本聪要给 Bitcoin 设计侧链,那么他设计出来就应该是像 Zendoo 这样的”。这里的 Zendoo,就是 Horizen 团队设计的侧链协议,我们今天来研读 Zendoo 协议白皮书。 Sin7Y 团队全面分析——Zendoo 协议白皮书
Zendoo 白皮书由 Horizen 团队和 IOHK 团队共同撰写完成的,该协议支持任意类型的侧链,包含 UTXO 模型的侧链、智能合约的侧链、甚至是一个普通的中心化应用。

Zendoo 协议仅支持资金跨链,这点很好理解,因为 Horizen 是个 UTXO 模型的隐私币公链,并不支持智能合约,所以的跨链协议也只是资金跨链。Zendoo 结合零知识证明,支持去中心化的方式达到跨链的目的。

跨链方式

共包含四种的跨链方式:
Sin7Y 团队全面分析——Zendoo 协议白皮书
我们来展开研读:

1. FT (Forward Transfer)

FT 是在主链上进行操作,将主链上的资产,转移到侧链上。协议也颇为简单,主链将 FT 作为一种特殊的交易,在主链上销毁币,在侧链上提供收款相关的 metadata。

侧链需要实时监控主链,如果主链的最新区块没有包含 FT 交易,则侧链仅仅将主链该区块的 hash 作为引用,加入侧链中;若主链的最新区块包含 FT 交易,则整个区块头、所有 FT 交易、以及所有 FT 交易的 merkle 路径都会被引入侧链中作为引用。这样,侧链上的任何人都可以验证交易是否合法,而不用去主链上进行查询验证了。
Sin7Y 团队全面分析——Zendoo 协议白皮书

2. BT (Backward Transfer)

BT 是在侧链上操作的,是将侧链上的资产,转移到主链上。前面讲过,IOHK 也是 Zendoo 白皮书的撰写者之一,所以我们在 BT 的设计中,能看到 Ouroboros 协议的影子。
Sin7Y 团队全面分析——Zendoo 协议白皮书
像 Ouroboros 协议一样,BT 的协议设计也涉及到 epoch 和 slot 的概念,如上图所示。一个 witdrawl epoch 由 len 个 slot 组成,len 对不同的侧链参数不同,如果一个提款证书在这段时间内没有提交到主链上,则侧链会被认为是停止了( ceased ,比如在 epoch i 里的一个提款证书,在 epoch i+1 里的前 len 个块中没有提交到主链上 ),以后的提款证书则不会被主链所接受,但是侧链的资金,还是可以取走的。

上图的一个 B,就是一个 slot,侧链 forger (出块者)会打包签名包含 BT 的 slot,并且在规定的 epoch 时间内,将提款申请证书提交到主链上去,关于提款证书,在后面会讲到。

提交提款证书的角色是什么,主链并不关心,并且提款证书该如何打包,也是侧链考虑的事情,主链同样不关心。

BT = (receiverAddr, amount)

其中:

  • receiverAddr - 主链上收款人的地址
  • amount - 转账金额

提款证书(WCert,Withdrawl Certificate)是个允许侧链与主链进行沟通的标准化帖子(posting)。 这个需要至少 51% 的出块节点签名,防止造假,再将其发送至主链。主要功能是:

  1. 发送 BT 给主链
  2. 作为一个心跳信息,使主链能够识别侧链的状态

其定义如下:

WCert = (ledgerId, epochId, quality, BTList, proofdata, proof)

其中:

  • ledgerId - 创建 WCert 的侧链 id
  • epochId - 一个提款 epoch 的 id,一般来说都是依次递增的
  • quality - 表明这次提款证书的质量(其实就是最后一次状态更新的区块高度)
  • BTList - 在一个提款期间内的多笔提款交易数组
  • proofdata - SNARK 验证者用到的公开输入参数
  • proof - SNARK 验证者用到的 proof

主链包含 vk (侧链注册时写入的),侧链的证书生成者(可以是任何人)包含 pk。

true/false <- verify(vk, public_input, proof)public_input = (wcert_sysdata, MH(proofdata))wcert_sysdata = (quality, MH(BTList), H(Bi-1), H(Bi))proofdata = (H(SB_last), H(state_SB_last[MST]), mst_delta)

其中:

  • Bi-1 - 主链中,i-1 提款 epoch 的最后一个区块 hash
  • Bi - 主链中,i 提款 epoch 的最后一个区块 hash
  • H(SB_last) - i 提款 epoch 侧链的最后一个区块 hash
  • H(state_SB_last[MST]) - 更新状态 SB_last 之后的 merkle hash
  • mst_delta - MST 修改的 bit vector,因为 MST 是个固定大小的树,所以 mst_delta 也是个固定大小的 bit vector,每个 bit 代表树的一个叶子。这个用于数据可见性,在 UTXO 模型中,可以在主链上就可以还原提款数据,而不需要侧链的数据。

bit vector 算法

Sin7Y 团队全面分析——Zendoo 协议白皮书
如上图所示,一个深度为 3 叶子节点为 2^3 的 MST,灰色的叶子节点表示有更新的,白色的叶子节点表示没有更新,用 bit vector 算法来表示 MST 的变化,就是:

mst_delta = (10111001)

这个算法在 Data Availability 特色的扩容方案中,可以 有效节约每次的上链数据 ,即仅仅传输 变化的 delta 值 MST 变化前的 root hash MST 变化后的 root hash ,就可以验证数据的合法性。对于 L1 的验证者来说,不必验证所有的数据,仅仅验证变化的状态即可。

当然,如果数据量非常大时,MST 的叶子很多,那么这个 bit vector 也会很大,会消耗大量 gas,这个是后续需要优化的方向。

3. BTR (Backward Transfer Request)

有可能用户没办法在侧链上进行 BT 操作,比如侧链的 RPC 地址拒绝访问,这个时候,想要取回侧链上的资金到主链上,就只能在主链上发起 BTR 操作了。BTR 并不会直接在主链上进行资金的转账,而是将此 BTR 同步到侧链上,在侧链再走一遍类似 BT (除去用户主动的部分)的流程。

BTR = (ledgerId, receiver, amount, nullifier, proofdata, proof)

其中:

  • ledgerId - 侧链 id
  • receiver - 主链上收款人
  • amount - 转账金额
  • nullifier - 本次 claimed 币的唯一标识符(类似以太坊中的 nonce,防双花)
  • proofdata - 公开输入的参数,后面会详细讲到
  • proof - 零知识证明的 proof

一个 ZKSnark 协议中的 vk、pk 都是公开的,所以任何人都以使用 vk 进行验证 proof 的正确性。

true/false <- verify(vk, public_input, proof)public_input = (btr_sysdata, MH(proofdata))btr_sysdata = (H(Bw), nullifier, receiver, amount)

其中:其他的都和上述 WCert 的一样,H(Bw) 是这个侧链提交最后一个提款证书时主链的区块 hash。

4. CSW (Cessed Sidechain Withdrawl)

侧链无响应时,比如侧链被攻击或者没有在规定时间内发送心跳信息到主链,这个时候,需要在主链上提走侧链上的资金。其定义如下:

CSW = (ledgerId, receiver, amount, nullifier, proofdata, proof)

参数的意义,和 BTR 的一样。和 BTR 不同的是,不会去侧链走 BT 流程(因为此时侧链无响应),而是直接在主链上完成转账。

详细设计

提款保障( Withdrawal Safeguard)

在主链上设置一个全局变量,FT 时增加相应的转账金额,BT 时减少相应的金额,提款时,金额不能大于这个金额。(在智能合约中更容易实现此功能)

主链引用侧链

为了同步主链和侧链,在主链的区块头里包含了一个额外的字段,用来承诺(commit)主链区块中所有与侧链相关的操作(除了 CSW,侧链停止时使用)。

引入 scTxsCommitment (Sidechain Transactions Commitment) ,就是包含了侧链所有交易或输出相关的 merkle 树的根 hash。
Sin7Y 团队全面分析——Zendoo 协议白皮书
比如:

type MCBlockHeader {    prevBlock: BlockId,    height: int,    ...    scTxsCommitment: Hash,}

侧链 Full Reference 主链

Sin7Y 团队全面分析——Zendoo 协议白皮书
侧链(SC)的区块包含主链(MC)的高度引用(或 hash),这样有两个好处:

  • 侧链可以快速同步
  • 主链分叉,侧链可以根据主链的 final 高度引用作为回滚点

Sin7Y 团队全面分析——Zendoo 协议白皮书
由上图可知,主链区块中,可能包含侧链相关的交易,该交易同时也会被包含在侧链的区块中,该区块也会引用主链区块。
其中, 侧链包含主链的引用:

type MCBlockRefrence {    header: MCBlockHeader,    mproof: Option[MerkleProof],    proofOfNoData: Option[MerkleProof],    forwardTransfers: Option[FTTx],    btRequests: Option[BTRTx],    wcert: Option[WCert],}

header - 引用了某个 MC 区块的 header
mproof - 可选,若 MC 区块中包含至少一个跟此 SC 有关的交易,则此字段用于此 SC 的 merkle root,否则,为空
proofOfNoData - 可选
forwardTransfers - MC 跨链到 SC
btRequests - MC 上请求从 SC 赎回资金
wcert - 提款证书

组合 Proof

递归组合 SNARK,可以将多个状态转移,最终生成一个 proof。
Sin7Y 团队全面分析——Zendoo 协议白皮书
单个代币的转账(BT/BTR)形式是固定的,所以基础的电路是通用的,例如对于 ERC20 来说,是 transfer。然后,可以将 base_proof 进行聚合,最终生成一个 proof。这种聚合的形式,其实就是 MMR (Merkle Mountain Range),下面详细说说 MMR。

Merkle Mountain Range(MMR)

Sin7Y 团队全面分析——Zendoo 协议白皮书
对于普通的 Merkle tree 结构的聚合证明来说,想要将 10 个交易的证明聚合成一个证明,需要将叶子节点两两聚合,若其中某些叶子节点的证明还未就绪(比如我们在 ZK-Rollups 中设定满 10 笔交易生成一次 base_proof,这个时候,可能 8 笔交易已经就绪,但剩下两笔交易还未发生),整个 prove 过程就会等待,直到叶子节点的证明准备就绪,再两两聚合,最后依次聚合,生成一个 merge_proof。

而 MMR 对 Merkle tree 结构进行了改进,可以先将已经生成的 base_proof 两两聚合,生成 merge_proof,再将 merge_proof 两两聚合,生成 merge_proof。如上图所示,在交易 Tx9 和 Tx10 还未就绪时,可以先将前面 8 个交易的 base_proof 聚合,生成 Tx1->Tx8 的 merge_proof(1->8),等 Tx9 和 Tx10 完成,将这两个 base_proof 生成一个 merge_proof(9->10),再和已经就绪的 merge_proof(1->8) 聚合,生成最终的 merg_proof (1->10)。
和 Merkle tree 的聚合证明相比,MMR 有以下特点:

  • 生成聚合证明的效率高,prove 时间大大减少
  • 结构更加灵活,可以将 base_proof 和 merge_proof 进行聚合

证书提交

提交时,只有节点维护者 (Forger/Slot leader) 才有提交之后得到奖励的资格,并且,是在特定的提交时期内。 如果提交失败或者逾期未提交,则顺延到下一个竞争者。(slot leader 选举,使用主链上个 epoch 的最小区块 hash 来产生随机数。)
Sin7Y 团队全面分析——Zendoo 协议白皮书
Latus 中,会对出块者和 prover 都进行奖励,出块者打包,prover 生成证明。
对基础的状态转换来说,prove 和 verify 是:
Sin7Y 团队全面分析——Zendoo 协议白皮书
其中:

  • a ∈ {pay, FT, BT, BTR}
  • s_i,s_{i+1} 是公开输入,si = Hash(State_i),即公开输入是状态转移前后的 hash,对 Latus 来说,是 merkle tree root hash

对合并的 SNARK 来说,prove 和 verify 是:
Sin7Y 团队全面分析——Zendoo 协议白皮书
其中:

  • b,c ∈ {Base, Merge}
  • proof^b_1 证明存在这样的交易,tx_1\, …\, tx_j\, 使得 State_{i+j} = update([tx_i\, …\, tx_j]\, State_i)
  • proof^c_2 证明存在这样的交易,tx_{j+1}\, …\, tx_k\, 使得 State_{i+k} = update([tx_{j+1}\, …\, tx_k]\, State_{i+j} )

总结

从白皮书来看,Zendoo 和 ZK-Rollups 颇为相似,对于 Data Availality 来说,前者涉及到跨链的会生成 proof,侧链上的转账不会,后者所有 L2 的交易都会生成 Proof,提交到 L1 验证。

对于提款来说,Zendoo 有 BT/BTR/CSW,而 ZK-Rollups 类似的操作有 Withdraw/Exit/Force_exit。对于 MST 的数据维护来说,现有的 ZK-Rollups 实现基本是用单节点或者中心化节点,而 Zendoo 的节点是去中心化存储的。

和目前大热的 Polygon 相比,跨链形式,Zendoo 是去中心化的,任何人都可以提交 ZK Proof,而 Polygon 是用有权限的多签桥控制合约两边记账来达到跨链。

对于提款时间来说,Zendoo 需要生成 Proof,提交到链上,比较耗时,而 Polygon 比较中心化,时间可控。对于实现难度来说,Zendoo 除开节点,还需要用于生成 Proof 的 Prover,这就需要实现零知识证明电路,开发难度较复杂,而对于 Polygon 来说,只需实现多签的合约控制即可。

引用

https://heap.horizen.io/
https://www.horizen.io/assets/files/Horizen-Sidechain-Zendoo-A_zk-SNARK-Verifiable-Cross-Chain-Transfer-Protocol.pdf
https://www.horizen.io/assets/files/whitepaper-snarks.pdf
https://academy.horizen.io/horizen/expert/sidechains/

来源链接: zks.org