这篇文章主要介绍了零知识证明能做什么,为什么zkRollup可以通过零知识证明来将区块链的执行可信的外包,零知识证明的主要流程以及零知识证明电路和普通程序的一些区别。

本文不包含具体的零知识证明算法原理,但有助于理解它们(希望如此)。

什么是零知识证明?为什么要零知识证明?

我们首先需要了解一下什么是零知识证明,以及零知识证明有什么基本的性质。 零知识证明这一密码学原语指一类可以使证明者能够在不向验证者提供任何额外的信息的情况下,使验证者相信某个论断是正确的协议。 零知识证明具有三个核心性质:

通过零知识证明完备性和可靠性的特点,我们可以通过零知识证明实现对计算过程的证明与验证,从而在去信任的场景下将交易的执行过程放在链下进行而不损害系统的安全性。

我们可以通过下面的例子来说明为什么我们需要零知识证明来实现对计算的验证。

假设我们需要对一个输入数据进行一万次哈希运算,并将这一运算过程外包给另一个执行者来进行。我们怎样才能确定执行者提供的执行结果是正确的?

$$ mid_1 = Hash(input)\\mid_2=Hash(mid1)\\mid_3=Hash(mid_2)\\...\\Result=Hash(mid_{9999}) $$

首先很容易想到,我们可以通过某种方式对计算结果进行验算。对于某些计算十分困难,但验证较为简单的问题,这一方法是可行的。但对于一万次哈希运算这样的任务来说,我们只能通过进行一遍同样的运算来对结果进行检验,从而使计算外包失去了意义。因此这种方法只能用在一些非常有限的场景中,并不是一种通用的解决方案。

其次,我们可以考虑引入多个执行者同时计算结果,并对计算结果进行共识。只要大部分执行者是诚实的,那么就可以相信执行结果的正确性。然而这一方法并不适用于zkRollup扩容这类场景。因为这一过程的可靠性依赖于对执行者集群的共识结果的信任。这一过程在Layer2中相当于构建一个独立的区块链,失去了Layer 2方案原有的优势。

我们还可以考虑对计算的过程进行随机抽查。以一万次哈希运算为例,验证者可以要求计算者对一万次哈希过程中的所有中间结果 $(mid_1, mid_2, ..., mid_{9999})$ 生成一个向量承诺,并随机抽取一万次哈希中的某些计算步骤进行检验。但由于计算过程中数据的关联性,任何一个计算步骤的错误都有可能导致最终结果的错误。计算执行者在一万次哈希运算中,只要有一次计算是错误的,都会导致最终结果错误。对计算过程抽样检查很难发现这样的错误。

由此我们可以看出,对一个计算过程进行验证是一个较为困难的问题。这一问题同样也是 Rollup 扩容方案在向主链更新状态时所需要面对的关键问题。即主链应该如何在不进行完整运算的情况下验证Layer 2执行者正确的进行了Layer 2状态的更新。所幸,我们可以通过零知识证明技术来实现这一目标。

还是以前面提到的一万次哈希计算为例子。根据零知识证明的完备性,当证明者完成对输入数据 的一万次哈希运算,得到输出数据后,他可以生成一个证明使验证者相信,输入数据经过一万次哈希的结果是执行者提供的结果。而零知识证明的可靠性则保证了当执行者提供错误的执行结果时,无法生成有效的证明。因此,通过零知识证明的完备性和可靠性,我们可以放心的将这些复杂的计算过程交给其他人计算,并通过相对较为简单的验证过程来验证计算是否正确,而无需对计算外包者的信任。

零知识证明可以实现对十分复杂的计算过程的证明,且其验证过程相对简单。以目前广泛应用的zk-SNARK类共识算法为例。zk-SNARK是Zero-Knowledge Succinct Non-Interactive Argument of Knowledge的缩写。其主要特点是Succinct,即简洁的。指零知识证明的验证过程不需要大量的数据传输,且较为简单。这一特性意味着对于任意大小的电路进行零知识证明,生成的证明大小及证明验证时间均为常数复杂度。这使得zk-Rollup将状态更新计算移至链下进行,链上只对运算正确性进行验证的扩容路线成为了可能。

零知识证明的过程

从一个程序到零知识证明,需要经历什么 我们准备下面这个简单的运算来作为例子说明零知识证明的过程。