传统分片技术存在双方需要知道对方历史区块的包袱,DFINITY 采用 BLS 文件签名算法或可解决这个问题。

原文标题:《DFINITY | Paul Liu:一条没有历史包袱的链》
撰文:Paul Liu,DFINITY 核心工程师
整理:BeWater

2021 年 9 月 4 日,在 BeWater DevCon 2021 全球开发者大会上,DFINITY 核心工程师 Paul Liu,曾任英特尔实验室研究科学家,为 X86 架构构建了 Haskell 编译器,他就密码学如何改变共识这一主题,进行了分享。

传统分片技术存在分片双方需要知道对方历史区块的包袱。DFINITY 采用了 BLS 文件签名,引入对 DKG 的算法进行一些改进,加入证明,解决最长链验证问题 ,子网之间的交换便无需历史包袱,区块链变成了一台「互联网计算机」,甚至可以在上面跑网站。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

Hello,大家好,我是 DFINITY 的工程师 Paul,很感谢大会的邀请,在这里能有机会和大家分享一下技术心得。我今天带来的题目主要是围绕着怎么用一些加密技术来改进共识协议,能够为共识协议去掉一些历史包袱。

比特币诞生以来提出了一个口号:「Don’t trust. Verify」。就是说,「不要去相信,要去验证」。但具体怎么验证呢,里面的细节其实还比较多。

历史区块包袱

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

我们这里看一个例子,后面的区块需要对前一个区块进行哈希,还包括区块本身的内容,比如用户的交易这些。

假如给你一个区块要验证它的正确性,这个怎么做呢?通常,比特币的做法需要知道这个区块是属于最长链,经过多轮区块以后我们基本上可以确信这个区块不能够再更改了,所以它是安全的。

要知道这个区块属于最长链,它的从属关系,实际上是要知道这个区块它之前的节点是正确的,需要一直回溯到创世区块,我们假设认定创世区块是正确的,这样算出来的最长链。最长实际上也是难度的概括,我就不细讲了。

算出来最长链才能验证这个区块链,这势必就牵扯到你需要知道所有的历史区块,这样的负担对于用户来说基本上是不可接受的。对于节点来说,可能也就是第一次进行同步的时候,一个新节点加入进来进行同步的时候,它可能需要知道所有历史记录,这才能验证之后区块的正确性。

怎么样解决这个问题,因为这对于容量的扩充是一个挑战,始终是需要保持所有的历史区块的记录。

传统分片技术

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

扩容方面我们通常的方案是做分片,我们把整个区块链的状态分成好多个片,然后每一个片实际上通过自己的区块链在运行。如果一个交易需要跨片的话,从一个区块链发起的交易到另外一个区块链上去接收,发起方和接收方势必要验证对方区块链上面的区块,每个分片就需要知道所有其它分片过去的历史区块,这样相对于单链来说其实负担更大了。可能有一些删除优化算法,但这些优化算法实际上也就是提高了一些效率,并没有达到质的飞跃。

我们看看以太坊 2.0 是怎么解决分片的问题?

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

它采取分层的结构,在最上面一层有一个叫 Beacon Chain,它是对其它的 Chain 做一个快照,其它的 Chain 只需要知道 Beacon Chain 是经过确认的,然后它就可以验证另外的 Chain。这个实际上还是隐含着一个条件,你要能够验证 Beacon Chain,就要知道 Beacon Chain 所有的历史区块。虽然对不分层的结构而言它有一定的进步,但实际上跟单链来说它也没有特别大的差异。所需要的难度,就是保持历史区块所需要的挑战基本上是一样的。

所以,我们可以看到历史区块实际上是有包袱的。有没有一种办法能够把这个包袱完全抛弃掉,这是今天所要探讨的一个问题。

BLS 门限签名

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

我们考虑一个简单的例子,跟刚才我们提到的 Beacon Chain 目的有点相关,它主要是作为随机数的序列,公众能够验证的公平的随机数。它必须满足几个条件:

  • 不可以被预测:如果能够被预测的话,实际上没有安全性可言了。
  • 公平:公平性是说参与计算的节点,它不可以影响下一个随机数数值的公平性,它可以影响的话实际上带来了安全性上的问题。
  • 公共可验证:即前面两点要能够被公众所验证。

所以要满足这三点才能够做一个安全的随机数的序列区块。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

有一个算法叫做「门限签名」,它的基本原理如上图所示。

我们可以看到这个例子里面有四个节点,每个节点有各自的私钥,然后每个节点单独对数据进行签名,对同一个数据进行签名。如果我们能够收集到三个签名,将这三个签名合在一起,就能够合成为一个签名,叫做「集体签名」。集体签名有一个有意思的地方,无论是哪三个你收集起来的单独签名,你把它们合成起来合成一个集体,都可以成为一个集体签名。

这个集体签名可以被验证,验证集体签名是通过一个公钥,这个是集体公钥,而不是某一个节点的公钥。集体公钥是最初开始生成的时候就确定的,所以这个集体公钥可以拿来验证集体签名的消息。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

我们怎么样能够信任集体签名呢?

首先,我们必须要能够信任最开始的生成过程,假定这个过程是可以信任的,稍后还会重新看内部的具体过程怎么实现。假如说,生成的节点私钥公钥和集体的公钥都是可以被信任的,除了我们这个节点能参加运算,它还需要知道其它的节点,所以我们需要有些公共信息,就是包括节点的 ID,包括它们的公钥。

公共信息放在创世区块里面,同时对创世区块进行哈希,来到下一个区块,我们再对下一个区块进行哈希,并且做一轮门限签名,就是阈值签名,我们收集到足够多的签名,把它们合在一起,得到一个集体签名,这样就得到下一个区块。如此反复,我们就可以把区块链进行下去。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

但是,造成的结果就是,我们不光有随机数序列的 Chain,还有 Block Chain,这个 Block Chain 记录了 DKG 所需要的一些消息,以及 DKG 运算的一些结果。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

我们假如说在一定的区间运行一整套 DKG,然后新加入的节点在这时候可以报名之类的,运行完出来一个结果,这个结果包括了下一个区间所需要的节点列表,它们各自的公钥,还有集体公钥,这个结果我们把它交给 Summary Block。

实际上我们可以看到 Summary Block 会经过一个门限签名,这个签名所需要用的公钥是上一个区间用的公钥。我们这个区间 Summary Block 会有之后区间运行所需要的公钥,意味着运行一次 DKG 加入了新节点,这个公钥可能就发生了改变。私钥显然是发生了改变,因为有新的节点加入,公钥也发生了改变,我们就说下一个区间是用新的公钥来进行验证签名的。

如此反复我们就得到了每一个区间的 DKG,每一个都有 Summary Block。需要验证 Summary Block,我们必然需要知道上一个区间的公钥是正确的,我们就需要验证上一个区间的 Summary Block,这样它们之间形成了一个链条,这个链条也是追溯到最开始的创世区块,所以我们并没有解决我们想要解决的问题,虽然说做了一些强化,但是没有从根本性上解决,我们还是需要保持历史节点,至少是 Summary Block 的历史节点。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

这个怎么办呢?通过研究,我们发现有一种新的算法能够在运行 DKG 的时候不改变它集体的公钥。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

这个算法大概的意思:假如说我们这边有一个节点发生故障导致它丢失了,我们加入一个新的节点,已有的这三个节点还是保留它各自的密钥,有它各自的秘密,可以把这些秘密通过零知识证明,不暴露它的秘密。可以做一些 reshare,可以分成四份,这四份再组合在一起。这三个节点都把自己的秘密分四份,四份重新组合一下,又得到了四个节点的私钥和节点的公钥,它们就可以用节点的私钥来做签名。

把这些签名合在一起,需要验证集体签名所需要的集体公钥,它跟以前是一模一样的,也就是说集体公钥是没有发生变化的。虽然我们又运行了一次 DKG,虽然我们加入了新的节点,但是我们保证了公钥的不变性。这就解决了刚才谈到的问题,即 Summary Block 需要保持它的历史区块的问题。即只需要一个公钥,就不需要保存历史记录了。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

具体到实现上还是有一些新的问题。从节点的角度来说,新节点的加入,它要产生下一个区块的话,就需要知道前一个区块的哈希,所以就需要和其他节点交互,得到前一个区块,比如在 203 的区块高度,我们发现做今后的计算我们不需要高度 200 以前的数据,我们只需要高度 200 以后的数据,假设这中间数据的依赖关系已经可以做到这点,就是说 200 以前我们可以完全丢掉,这时候在 200 这个高度我们就可以对 200 高度的这些区块,这些状态和它一起打个包,把这个包做一个签名,得到了集体签名之后,这个包就可以用来给新加入的节点。

它只需要验证一下这个包是可信的,再跟其它的节点同步一下,我们把从这个包到目前最新的区块重新同步到新的节点上来,这个新的节点就可以加入接下来的计算了。

通过这样一个方式,我们能够对 DKG 的算法进行一些改进,加入零知识证明和 resharing 的方法,我们就可以丢弃历史包袱。

我们来看一下这个技术在 Internet Computer Blockchain 架构里面的具体应用。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

首先我们讲公钥。我们只需要一个公钥,这是 48 个字节,就可以验证所有的交易。对照一下以太坊,以太坊 Open Ethereum 的客户端要下载 400 GB 的数据才能够开始进行计算,这完全不是一个维度的事情了。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

Internet Computer 的节点我们也是采取了分片的做法,这里为了区分一下我们把它叫做子网,也就是我们没有母网,每个子网相互之间可以交换信息,但是它不需要知道对方子网的历史区块。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

这里面有一个子网有一点特殊,我们把它叫做 NNS,它主要的功能就是创建新的子网。它可以为另外的子网运行 DKG,它通过运行 DKG 产生一个新子网所需要的公钥。绿色的公钥是绿色子网所需要的,它最初创世纪的区块是在 NNS 这个子网上面进行计算得到,所以 NNS 就可以创建其它子网所需要的公钥,它在这个基础上再做一个证书,这个证书就是证明子网公钥的正确性。

我们在跟用户进行交互的时候,返回的信息,然后用户需要验证,它就只需要知道 NNS 的公钥,加上子网的证书,加上子网的签名,一整套验证就可以验证信息的安全性和正确性。这个验证是非常简单的,我们在浏览器里面通过 JavaScript 就可以做到。所以用户这边是完全没有新的要求,只需要通过浏览器。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

在节点方面,每个子网节点参与运算,有时候它会发生故障,有新的节点,或者我们知道有的节点被攻破了,我们需要用新机器来替换它,这些过程都是由子网自己完成。只有子网的创世区块是由 NNS 完成,子网本身运行自己的 DKG。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

我们刚才提到了它的作用,一方面是替换节点,甚至当子网有非常严重破坏的时候,比如说丢失了大部分的节点。

这时候我们怎么办呢?通常区块链就没有办法修复,但是我们可以通过 NNS 再运行一次 DKG,然后加入新的节点进来,然后通过 Catch-up packages,从之前停止的状态继续进行下去,虽然这时候因为 NNS 运行 DKG 得到的子网公钥发生了变化,但是这个不影响跟用户之间的交互,因为子网的签名最终是通过它的证书来保证正确性,只要证书被传到了用户,用户就可以通过 NNS 的公钥来验证。我们还可以做一些升级它的协议这样一些管理,自动化的管理应用过程。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

所以抛开历史包袱之后我们发现是一个新世界,很多之前做不到的事情现在都变得非常简单,只需要一个公钥来验证交易,我们就可以直接在区块链上跑网站,这个网站是直接从区块链上,把网页送到用户侧,因为验证数据的正确性只需要一个公钥。

然后我们还可以很容易的扩容,扩容我们只需要添加新的子网,就可以处理新的交易,处理更多的用户,保存更多的状态,因为子网跟子网之间是没有保存历史记录的这样一个负担的,我们可以修复子网,可以对协议进行升级,这些事情都变得非常简易。

DFINITY 核心开发者:如何构建没有历史包袱的区块链?

今天我的分享就到这里,想要知道更多的技术细节,欢迎大家到这个网址看一下,因为这个网址上面提供了我们团队对一些技术细节讲解的视频,还是非常有帮助。

如果对今天这个分享有什么疑问的话欢迎联系我,我的 github 的账号和新浪微博的账号都发在这里,谢谢大家今天的参与,我们下一次再见。

LEAVE A REPLY

Please enter your comment!
Please enter your name here

1 × 5 =