零知识证明入门手册1

124 阅读5分钟

翻译自:www.shirpeled.com/2018/09/a-h…

零知识证明

零知识是如下类型的故事:A陈述了一个声明,并向B证明该声明,在他们之间经过了一些信息交互后:

  1. B可以确信声明有99.99999%的概率是真的。
  2. B在该过程中没有学习到任何知识,除了声明是真的。

本节首先讲述 "零知识的知识论据(ZK arguments of knowledge)",这与零知识证明并不完全一样,但足够接近。概况来说:一个零知识证明的证据是可以被完全信任的,即使试图证明声明的人(通常被称为证明者)有着无限的算力。信任“零知识的知识论据”需要满足下面的假设:如果证明者确实想作假,这会是多项式界限的。

在零知识证明的世界中,交换信息的另一方被称为“验证者”。下面将沿用该术语。

分割问题

给定一个数字的序列 a0,a1,...,an1a_0,a_1,...,a_{n-1}, 能否将该序列分割为两个子集合,使得两个子集合的和相同?

如果问题中的序列是1,9,0,8,2,21,9,0,8,2,2,那答案明显是可以,因为有2+9=8+1+2+02+9=8+1+2+0

然而,如果序列是 2,3,4,5,6,72,3,4,5,6,7,那答案是否,因为和是一个奇数,不可能两个子集合的和正好是奇数的一半(这些数字都是整数)。

尽管这是些简单的实例,通常这个问题是NP完全的(即使它有个伪多项式时间的算法)。

开始证明

假设我们有个数字的python列表ll,定义了我们的分割问题实例。我们认为另一个列表mm是一个满足的分配,如果

  1. len(m)==len(l)len(m) == len(l)

  2. mm中所有的元素都是111-1

  3. llmm的点积是0。

这等价于分割问题的陈述。如果我们认为mm中的‘1’,是将它对应的ll中的数字放在等式左边,‘-1’是放在等式右边。

让我们重写llmm点积的部分和的列表。用数学表达为pi=0k<il[k]m[k]p_i=\sum_{0\leq k<i} l[k]\cdot m[k]

因此,如果l=[4,11,8,1]l=[4,11,8,1], 而且m=[1,1,1,1]m = [1,-1,1,-1],那么 pp将是更长的元素p=[0,4,7,1,0]p=[0,4,-7,1,0]

注意到pp 现在有两个有趣的属性,如果mm确实是满足的分配:

(p的属性1)它以0开始和结尾

(p的属性2)对每个0i<n0 \leq i< n, 都有 l[i]=p[i+1]p[i]|l[i]| = |p[i+1]-p[i]|

所以有了第一个简单的零知识协议的草案:

验证者选择随机的0in0 \leq i \leq n, 如果 i=ni=n, 验证者要求证明者提供p[0]p[0]p[n]p[n]并检查他们都为0.

否则,验证者要求证明者提供p[i]p[i]p[i+1]p[i+1]并检查确实有l[i]=p[i+1]p[i]|l[i]| = |p[i+1]-p[i]|ll是验证者已知的,作为证明者发出的声明的一部分)。

证明者撒谎怎么办???

上面的示例包含了一个隐含的假设:验证者要求证明者提供数据时,证明者会诚实的提供。我们并不认为会这样、这个问题将在下一篇文章中讨论,现在我们假设所有参与者都是诚实的。

这没有证明任何事!

细心的读者会指出,询问一个单独的元素并不意味着什么。这是正确的,我们将进行多次查询,并在足够多的查询后,我们确信声明是正确的。我们将在第三篇文章进行更精确的量化。

这并不是零知识!

每一次查询都揭示了关于mm的信息,因此它并不是零知识的。因此,在足够多的查询之后,mm可以被完全揭示。

这是糟糕的,让我们修复这个问题

制造零知识

从数学的角度来讲,我们通常会说一些信息没有提供新知识,如果这些信息是随机的,更确切地说---是指信息是均匀地分布在合理选择的域上。在没有了解“提取(exact)”定义前,要想使某些信息变成零知识的,需要将这些信息和随机混合在一起。这是我们的做法:

  1. 并不需要把mm揭示给我们,我们需要掷一枚硬币。如果是正面,我们将不对mm作处理,如果是反面,我们将mm的所有元素乘以1-1。注意到元素被初始化为111-1,而且与ll的点积被初始化为0,这并没有改变它与ll的点积。
  2. 我们选择一个随机数rr,将它与pp中所有的元素相加,这并没有影响到pp的第二个属性,但改变了第一个属性,现在pp的第一个元素和最后一个元素也许不是0。然而,他们必须与另一个保持一致。

现在假设在每次查询前,我们重新生成随机数(抛硬币并改变mm,选择随机数rr,并将其添加在pp元素中)。

如果我们仔细选择rr,那么pp中每两个连续元素将会不同,由于ll中对应的元素看起来更随机。

下面是我们所需要的代码的第一部分,这段代码输入问题(比如ll)和一个满足的分配(比如mm)并构建一个证据(比如pp),该证据可以证明问题实例的可满足性。

imort random

def get_witness(problem, assignment):
    """
    Given an instance of a partition problem via a list of numbers (the problem) and a list of
    (-1, 1), we say that the assignment satisfies the problem if their dot product is 0.
    """
    sum = 0
    mx = 0    
    side_obfuscator = 1 - 2 * random.randint(0, 1)
    witness = [sum]
    assert len(problem) == len(assignment)
    for num, side in zip(problem, assignment):
        assert side == 1 or side == -1
        sum += side * num * side_obfuscator
        witness += [sum]
        mx = max(mx, num)
    # make sure that it is a satisfying assignment
    assert sum == 0
    shift = random.randint(0, mx)
    witness = [x + shift for x in witness]
    return witness