# Rollup 原理（ ZK / OP ）

By [Ethan - if(DAO)](https://paragraph.com/@ethan-if-dao) · 2021-12-31

---

导读
--

我们可以简单的将区块链公链理解为一套分布式存储的账本，每个节点在各自本地存储一份账本。这些节点为了保障全网的账本一致性，就需要进行共识。共识的过程需要网络的不同节点之间交换信息，所以导致执行计算和记账的效率不高。

对于软件系统而言，描述其系统吞吐量一般用 TPS（ 每秒事务处理量 TransactionPerSecond ） 性能指标。具体来说，比特币系统每秒仅支持 7 笔交易，以太坊也仅支持每秒 15 笔左右的交易。这样的 TPS 显然无法满足大型商业应用的需求。

在这种历史背景下，为区块链系统扩容被提上了日程。目前业内主要的扩容方案有两类 ：

*   链上扩容 / Layer1 一层扩容
    
    即对区块链本身进行改造，从而直接提升链上交易处理速度 ：直接增加链上区块内可以包含的交易数量（ 如隔离见证 、扩块）、直接改变区块链的链体结构（ 如分片 Sharding 、有向无环图 DAG ）、直接改变区块链的共识机制（ 如 EOS 的 DPoS 、Solana 的 PoH ）等方案。
    
*   链下扩容 / Layer2 二层扩容（ 以太坊实现扩容时将源代码文件夹命名为 Layer2 ）
    
    即将链上的相当一部分工作量转移到链下来完成，从而间接提升链上交易处理速度 ：State Channel（ 状态通道 ）、Plasma 、Rollup。具体如下图所示 ：
    

![](https://storage.googleapis.com/papyrus_images/1937ca5ee7d0237b5b636ed8147f9eeea825aae8c73fe61cb6b3f2e0fc2d759a.png)

if(DAO) 希望通过本篇内容解释上图中 “ Optimistic Rollup ” 和 “ zkRollup ” 的基本工作原理。

zk是零知识证明的缩写（ZKP ：Zero-Knowledge Proof），Rollup 字面意思是 “ 一卷 ” ，通俗讲就是将链下一大堆转账交易记录压缩为一个批量，然后将这个批量交易打包、压缩后上链验证并存储。

> 上图中的 Groth 16 、PLONK 都属于 zk-SNARKs 协议族，他们的数学理论基础都是基于椭圆曲线的 ；STARK 是零知识证明的另外一种实现算法，数学理论基础是离散函数并且无需可信设置。
> 
> 关于 ZKP 和 zk-SNARK 协议的演进过程和底层原理，可以详见 if(DAO) 之前的文章 ：

[https://mirror.xyz/0xd05cFA28Eaf8B4eaFD8Cd86d33c6CeD1a1875417/X3qSOjObTknXQ\_iGhDBFYETibD0TVW0twz5QDIthjGI](https://mirror.xyz/0xd05cFA28Eaf8B4eaFD8Cd86d33c6CeD1a1875417/X3qSOjObTknXQ_iGhDBFYETibD0TVW0twz5QDIthjGI)

Ethereum 的 “ 状态 ” 和 “ 历史 ”
--------------------------

在了解二层之前，我们有必要首先了解一下 Ethereum 一层的基本结构 ：

![](https://storage.googleapis.com/papyrus_images/337a2b660c0c4eef4e1001dc18a72806c21b3b890fe2ace05efb1df0922f9229.png)

1、Ethereum 的状态

状态 = 指的是 Ethereum 全量账本当前的样子 ：由所有以太坊账户构成的一棵**状态树**（ Merkle Patricia Tree 即上图中ABCD向上收敛形成的树 ），每个账户（树的叶子）的最主要结构是 ：

（1）key = 账户的名字（状态树的叶子A、B、C、D）

（2）balance = 每个账户的余额（10、20、30、0）

2、Ethereum 的历史

历史 = 由当前区块包含所有交易构成的一颗**交易树**（上图中未画出交易树），每笔交易最主要结构是：

（1）to = 交易将要发送到的账户地址（在 Block XXX 时，to = D）

（2）value = 交易的转账金额（在 Block XXX+1 时，value = 10）

（3）data = 交易输入的变量（比如将A和B的余额分别扣除10%的佣金给E）

3、交易驱动状态的变化

（1）根据交易的转账金额（value）计算目标账户（to）的新余额（balance），比如C给某人转账10 ETH后更新C的新余额为20 ETH。

（2）如果交易是发送给一个智能合约，则将交易的 data 作为参数传递给目标账户（to）的智能合约，运行智能合约的业务逻辑，在运行中可能会修改任意账户的状态从而生成新的状态（比如E的账户余额更新为 = 原余额 + 10\*10% + 20\*10%）。

（3）构造新的叶子存放新状态（D：20 / C：20），更新状态树，最终计算形成新的stateRoot（即 Merkle Tree Root ）并储存至新区块的 Header 中。

4、二层照猫画虎

既然是要对区块链扩容，那么二层的基本思路就是将一层需要处理的工作挪到二层处理，这样自然就减轻了一层的交易并发压力，从而提高了交易处理能力。二层需要的处理的工作包括：

（1）首先要确保 transaction 的合法性：转出账户是否有足够的余额支付转账金额和 gas 手续费；转出账户的 nonce 是否正确（防止重放攻击）；转账 transaction 的签名是否正确。

（2）二层的 Relayer（ 暂时理解成节点 ）执行该 transaction，修改 Merkle Tree 中的转出账户和转入账户的叶子节点余额，然后重新计算新的状态树的根 Merkle Tree Root。

（3）重复（2），Relayer 按照先后顺序一次性处理多个 transaction，然后将最后计算得到的状态树的根 Merkle Tree Root 作为新的状态提交到链上合约中。

5、照猫画虎的问题

按照 4 的方案，如果仅提交状态树的根到链上合约，那么 L1 节点如何相信新的状态根是如实地根据上述逻辑计算出来的（ L1 除了一个状态树根之外对于 L2 发生的交易一无所知 ），万一二层的 Relayer 作恶将 transaction 的 gas 调大呢。

解决这个问题的一个方法是要求 Relayer 提交状态树根到合约时，同时也将所有 transaction 一并提交到合约，这样 L1 节点可以根据这些 transaction 来验证 Relayer 在计算新的状态树时有没有作恶。但这等于是将所有链下的数据又搬回了链上，没有实现 Layer 2 扩容的目的。 此时 “ zkRollup ” 中的 zk（零知识）就派上了用途。

6、zk-SNARK 的用武之地

（1）从过程 3 不难看出，以太坊的状态变化过程就是：当前全量账户余额状态随着状态转换函数（转账交易或智能合约）不断转换为下一个区块时的全量账户余额新状态。

（2）回想我们之前文章介绍的 zk-SNARK 的做法：将函数的输入、计算过程和输出结果构建成多项式（零知识），通过多项式可以生成 Proof（零知识证明），验证者可以通过 Proof 验证函数的输入、计算过程和输出结果的正确性。

（3）以太坊的状态变化过程和零知识证明的过程非常类似，所以我们将以太坊的状态转换函数的输入、计算过程和输出结果构建成多项式并进行零知识证明，验证者（网络上的其他节点或链上智能合约）就可以在不知道状态转换具体过程和转换结果的情况下，通过 Proof 验证状态转换的是否正确。

zkRollup 的基本原理
--------------

使用 zkRollup 的二层项目一般会包含 2 个基本角色：

*   Transactor（终端用户即以太坊外部账户）：用户构建转账交易并用私钥签名，然后将交易发送给 relayer。
    
*   Relayer（中继者）：负责收集并验证用户的交易，之后将交易批量打包压缩，并生成 zk-SNARK 的 Proof。最后relayer将用户交易中的核心数据 + Proof + 全量用户新状态的 Merkle Root 提交到链上的 Layer2 智能合约。 Layer2 智能合约验证 Proof 通过后将新状态的 Merkle Root 更新至新区块的 stateRoot。
    

在 Layer2 上用户的各种交易会被发送给二层的 Relayer 运营者 / 中继者。Relayer 会将一批交易压缩成为一笔 Batch 批量交易，调用链上的合约进行处理。这笔调用交易就会像其他的 Layer1 的交易一样进入交易内存池。在 Layer1 上矿工会接收一层网络中所有的交易（ 包含由二层打包上来的交易 ）并打包成区块发布。

zkRollup 的本质是将原本在链上的用户状态变更，转移到链下进行，同时通过 zk-SNARK 的Proof 来保证链下用户状态变更过程和结果的正确性。

这样做的原因显而易见：在链上直接处理账户状态的变更成本是比较高的（全网每个节点本地计算后进行共识，然后更新新的用户状态），但是仅仅利用链上的智能合约来验证一个账户状态改变的 Proof 是否正确，成本是相对低很多的。

zkRollup 的执行过程
--------------

![](https://storage.googleapis.com/papyrus_images/e3c9ce3a9735c8d840563dbf037712361bf589466a5bb0b29b5c6ce52d0ef18b.png)

1、zkRollup 项目方在 L1 发布一个智能合约，合约内存储着当前以太坊全量账户的 Merkle Tree Root（状态根）。

2、Relayer 在 L2 收集 Transactor 用私钥 signature 签名过的若干 transaction（ T\[1\] … T\[n\] ），然后用预先定义好的 ZK circuits（零知识电路）生成一个基于 zk-SNARK 的 Proof 证明。

3、该 Proof 可以证明：

（1）状态变化过程没有问题 ：即状态变化函数 STF ( PRE\_STATE ，T\[1\] … T\[n\] ) = POST\_STATE，即老状态 PRE\_STATE 经过 T\[1\] … T\[n\] 转换为新状态 POST\_STATE。

（2）每个交易 T\[1\] … T\[n\] 中的 value（ nonce ，fee ）等值都没有问题且 signature 正确。

（3）root ( PRE\_STATE ) = r1。

（4）root ( POST\_STATE ) = r2。

4、Relayer 将 t\[1\] … t\[n\] 、r1 、r2 、Proof 一起提交到 L1 链上智能合约。其中 t\[1\] … t\[n\] 是 transaction 的核心信息，不包含 nonce 和 signature。因此 t\[i\] 比 T\[i\] 更小，起到了压缩交易数据的作用，使得单个区块可以容纳更多的 transaction ，从而提升了区块链系统的 TPS。

5、L1 的智能合约验证

（1）Proof 是否正确 ：如果 Proof 正确则进行（2）。如果 Proof 不正确则 L1 不接受 Relayer 提交的这批交易。

（2）智能合约中保存的全量账户状态根 Merkle Root 是否与 r1 相等。如果相等则意味着链下发生 T\[1\] … T\[n\] 之前的状态和链上发生 T\[1\] … T\[n\] 之前的状态是一致的，之后的状态转换过程也是正确的（ Proof正确 ），所以将智能合约中保存的全量账户状态根 Merkle Root 更新为新状态 r2。

6、Relayer 作恶

（1）如果 Relayer 修改用户的 transaction 的任何信息，则 Proof 将无法被合约验证通过，即作恶失败。

（2）如果 Relayer 恶意拒绝某个用户的转账（不收集该笔转账），这时 Proof 是可以被合约验证通过的，因为 Proof 只能证明老状态经过 T\[1\] … T\[n-1\] 转换至新状态的过程和结果是正确的，却不知道是否少了一笔 T\[n\] 的转账。因此为了防止这种行为，链上智能合约必须支持 On-Chain Withdraw，即任何用户都可以从 L2 将自己的 token 提到 L1。

Optimistic Rollup 基本原理
----------------------

zkRollup 和 OP Rollup 的共同点是 ：为了对 L1 扩容，交易的实际执行都不是在以太坊上完成的，而是转移 L2，但安全性仍然依赖于 L1。这意味着我们需要以太坊以某种方式保证交易执行的正确性，即使交易发生在 L2 上。 那么以太坊如何才能对 Rollup 的状态给予批准呢，答案是：证明。 Rollup 使用专门的证明向以太坊证明其交易执行的正确性（ 即使 L1 不执行交易，以太坊也可以验证正确性 ）。

zkRollup 和 OP Rollup 的核心不同点是 ： 在zkRollup 看来所有 L2 交易的执行都是不可信的，除非 L2 可以提供交易执行正确性的证明（ 即zk-SNARK Proof ）；而在 OP Rollup 看来所有 L2 交易的执行都首先认为是可信的（ Optimistic 面对 L2 是乐观的 ），除非有人质疑 L2 的交易并提供交易执行有问题的证明 （ 即欺诈证明 Fraud Proof ）。

实际上对于 OP Rollup 发布到 L1 的更新后的新状态发布到以太坊时，OP Rollup 根本没有发布任何证明。任何人都可以发布一个包含关于执行某些交易的正确结果的声明的汇总块。其他节点执行相同的交易，如果他们不同意第一个节点的主张，可以提出 Challenge 挑战。有效的 Dispute Protocol 争议协议可以解决任何分歧，保证正确的一方将赢得挑战。

Optimistic Rollup 执行过程
----------------------

![](https://storage.googleapis.com/papyrus_images/73ec170e18f3ae7e6d5f4ba9c486a89485fc4e079c6e7bd76a71928d13f5b36d.png)

1、Optimistic Rollup 项目方在 L1 发布一个 2 个智能合约（ 其他合约未列出 ） ：

（1）CTC（ 权威交易链 ）：用于存储 L2 roll up上来的交易。

（2）SCC（ 状态承诺链 ）：用于存储 L2 每笔交易执行后的 “ 中间状态根 ”。

2、Sequencer 收集 L2 发生的交易。

3、Sequencer 执行本地操作

（1）Sequencer 压缩交易 T \[ 1 \] 、T \[ 2 \] … T \[ n \] 并提交 t \[ 1 \] 、t \[ 2 \] … t \[ n \] 至 L1 的 CTC。假如 Sequencer 作恶偷偷修改了 t \[ 2 \]（图中用红色标出）。

（2）Sequencer 根据 T \[ 1 \] 、T \[ 2 \] … T \[ n \] 在本地执行状态转换，并将每经历一笔转账的中间状态分别记录为 s \[ 1 \] 、s \[ 2 \] … s \[ n \]。由于 Sequencer 作恶偷偷修改了 t \[ 2 \] 的转账金额，所以从 s \[ 2 \] … s \[ n \] 都是不正确的 Hash 值。

4、Sequencer 的舞弊行为被 Proposer（ L2 进行交易的本人或其他第三方 ）或 Verifier 发现，Verifier 从 SCC 下载 Sequencer 上传的红色 s \[ 2 \]，并根据 T \[ 2 \] 执行正确的过程转换生成正确的 s \[ 2 \] ，二者进行比较。

（1）如果二者相等，则说明欺诈证明为假。保持 L1 合约中的若干中间状态根不变。

（2）如果二者不相等，则说明欺诈证明为真，即 Sequencer 确实有舞弊行为。

5、对于欺诈证明为真的情况，Verifier 从错误的交易状态根（ s \[ 2 \] ）开始更新 SCC 的这个批次的所有后续状态根（ s \[ 2 \] … s \[ n \] ）。

6、激励和惩罚

由于 Optimistic Rollup 是无许可的，SCC 旨在允许任何人成为 Sequencer 并发布状态根。为避免 SCC 被垃圾数据淹没， 我们引入了1个限制：想成为 Proposer 或 Sequencer 必须先存入固定金额的抵押品至 Optimistic Rollup 的债券合约中，且在7天后才可以提取该金额。

DYOR
----

\================================

\================================

欢迎大佬们的探讨指正～

您可以在这里找到作者 twitter：@ethan\_ifdao

---

*Originally published on [Ethan - if(DAO)](https://paragraph.com/@ethan-if-dao/rollup-zk-op)*
