零知识证明实践教程,第二部分

本文是零知识证明简单实践教程的第二部分,
第一部分见:零知识证明第一部分
第三部分见:零知识证明第三部分

现在一个问题是,prover(证明者)可能撒谎,比如原本它应该向verifier(验证者)揭露p[1]的值,但prover出于某种原因或者纯粹恶作剧,把这个值替换成其他的数值。因此,我们要解决的问题是,如何做到如果prover撒谎,verifier能够知道呢?于是,“承诺commitment”被提出来了。

承诺,单单从字面来理解,指的是当前对未来某一刻的保证,当前一方拿到它,到了未来能够验证这个承诺是成立还是失败。本文中的承诺的含义跟这个基本一致。

prover的承诺 commitment

在密码学中,承诺的表示有很多种,这里使用Merkle树来说明。

零知识证明实践教程,第二部分_第1张图片

上图是一个merkle树,叶子节点是明文信息,其他节点是两个子节点的hash的值。merkle树的一个特点是只要子节点有一点点改动,会导致该节点的父节点以及以上节点都变动。换一种表述是只要merkle树的根节点不变,那么任何一个节点都没变动。下面的图片表示,如果我把叶子节点Boogie改动一点,导致其他节点的变动。
零知识证明实践教程,第二部分_第2张图片
Merkle树的认证路径

一个验证过程:
假如上图所示的merkle树,您早先拿到了根节点的值,现在,除了第二个叶子节点的信息“Sir”,不让你知道其他叶子节点的情况下,如何向你证明Sir是这棵树的一个叶子节点呢?

做法:
我只需要让你知道4号节点的hash值和3号节点的hash值,以及Sir节点,就可以了。

原因:
你可以使用上面这几个值计算出merkle树的根节点的值,跟原来的对比,如果相等,表面验证通过。否则验证失败。上面这几个值组成了一个完整的从子节点到根节点的验证路径,下文我们称它们为验证路径

现在回到我们的例子,如果prover将数组p中的每一个元素作为一颗merkle树的叶子节点,并将根节点作为承诺发给verifier。然后,verifier发送一个随机数 i 给prover,prover向verifier发送p[i]和p[i+1]的值和merkle树的验证路径。verifier通过这些信息能够判断这两个元素p[i]和p[i+1]是不是在数组p中。这样解决了verifier撒谎的问题。

下面的代码是上面这一段描述的实现,注意的是下面这个代码只是为了演示功能的作用,不是优化的。

import hashlib
from math import log2, ceil

def hash_string(s):
    return hashlib.sha256(s.encode()).hexdigest()

class MerkleTree:
    """
    A naive Merkle tree implementation using SHA256
    """
    def __init__(self, data):
        self.data = data
        next_pow_of_2 = int(2**ceil(log2(len(data))))
        self.data.extend([0] * (next_pow_of_2 - len(data)))
        self.tree = ["" for x in self.data] + \
                    [hash_string(str(x)) for x in self.data]
        for i in range(len(self.data) - 1, 0, -1):
            self.tree[i] = hash_string(self.tree[i * 2] + self.tree[i * 2 + 1])

    def get_root(self):
        return self.tree[1]

    def get_val_and_path(self, id):
        val = self.data[id]
        auth_path = []
        id = id + len(self.data)
        while id > 1:
            auth_path += [self.tree[id ^ 1]]
            id = id // 2
        return val, auth_path

def verify_merkle_path(root, data_size, value_id, value, path):
    cur = hash_string(str(value))
    tree_node_id = value_id + int(2**ceil(log2(data_size)))
    for sibling in path:
        assert tree_node_id > 1
        if tree_node_id % 2 == 0:
            cur = hash_string(cur + sibling)
        else:
            cur = hash_string(sibling + cur)
        tree_node_id = tree_node_id // 2
    assert tree_node_id == 1
    return root == cur

上面的merkle树存在的缺陷是,verifier可能通过暴力的方式找到其他叶子节点的明文信息。如果verifier知道数组p的所有值,很可能以此来推测出数组m的值,这违背了“零知识”原则。在下一部分内容中,我们将讲解如何通过一个简单的方法来混淆merkle树,让这种暴力破解的方式变得不可能或者基本不可能。
在下一部分内容中,我们还将继续进一步说明零知识证明的多次验证过程等内容。

第一部分见:零知识证明第一部分
第三部分见:零知识证明第三部分

你可能感兴趣的:(科技前沿,区块链,数学)