DFINITY区块链连载(三) - 技术FAQ

季宙栋季宙栋

上一篇介绍了DFINITY网络达成一致性的模型并解析了区块生成的步骤,本章将简单的对有关技术做一个FAQ,可以很好的回顾技术实现的细节和部分架构设计的考量因素。

作者:季宙栋、丛宏雷

核心关键词:验证塔、验证树、阈值接力、分片


为什么DFINITY依赖于随机性?

我们发现,到目前为止,只有通过密码学生成随机数的方法才能在一个抗攻击的网络中管理大量的客户端(矿工),从而实现一个虚拟的超级计算机。当然,中本聪也是设计让矿工去计算随机难题的方法产生随机性。DFINITY需要一个更强的、更不易被操纵,而且更高效的随机数生成算法。

我们采用的算法称为阈值接力,这个算法基于密码学,可以在一个参与者足够多的网络中生成几乎不可攻破、完全不可操纵、不可预测的随机数。基于阈值接力算法,DFINITY网络中的参与者可以生成一个可验证的随机函数VRF,基于VRF随机函数DFINITY网络完成对自己组织和运行管理。

而且,在比特币出现之前,随机数也在分布式系统中有重要的运用。比如著名的拜占庭共识协议算法,这个算法可以实现异步网络没有leader的情况下实现共识,但是他也依赖于一个common coin的概念去生成一个抛硬币随机序列,在2000年IBM通过RSA算法实现了这个随机序列。在2014年,Dominic在一个高扩展密码项目中对此类共识协议做出进一步的扩展。这就是在去中心化的网络中,通过BLS签名实现随机数生成的算法以及基于此算法的众多强大的应用的由来。基于RSA的阈值系统依赖于一个可信的代理节点,但是基于BLS的阈值系统不需要可信代理。


一个随机源对于云应用很有必要吗?

是的。随机源可以说是开放式云应用平台的根本。除了通常的应用,比如为彩票/游戏系统提供公平性外,可以为金融交易所的已提交事务进行随机排序,从而抑制矿工的非法预先交易。在基于区块链的自治系统中随机源有更重要的作用,一个很好的例子就是目前String Labs正在开发的去中心化商业银行系统PHI。

PHI是一个完全自治的系统,基于算法随机顺序挑选外部的验证者,委托它们对贷款申请进行验证,实现智能放贷。同时,当前大多数其它的自治系统也都依赖于系统外部的输入数据作出决策,由于自治系统本身无法验证输入的数据的正确性,所以自治系统通过随机选择的方式来抵抗外部系统可能的攻击。


DFINITY如何产生随机数?

DFINITY引入了‘阈值接力’这一新的机制。它可以创造出一个确定的、几乎不可攻破,同时完全不可操纵、不可预测的随机数源。


为什么不直接使用区块哈希作为一个随机数?

在DFINITY系统中的很多协议都基于随机数的不可操纵、不可预测性来实现系统的横向扩展。在传统POW系统中,虽然也可以达到基本的随机数要求,但是每个随机数的产生都伴随着一定的算力成本。在POS系统中,虽然没有算力的要求,但是矿工可以很容易地通过修改区块中的内容从而改变区块的哈希值,这样就使区块的哈希值失去随机性。综上考虑,区块的哈希值无法满足我们的需求。


为什么不用commit-reveal的方式生成随机数?

这个方法有一个‘最后披露人’漏洞。他可以选择不披露,从而影响别人已经处理的结果。 对那些拒绝披露的人来说,惩罚并不奏效,因为通过操纵随机性获得的回报可能会更高。 除了有安全漏洞之外,这个方案效率也很低、容易失败,因为他依赖所有参与者提交结果,才能进行处理。


为什么不用TPM芯片做随机源?

很难确定网络中谁会拥有这样的硬件,而且TPM可以被关掉。


DFINITY网络都有什么组成?

一个DFINITY网络有众多的矿工客户端组成,也称之为‘进程’,它们通过一个p2p广播网络连接在一起。每个矿工都有一个矿工ID,用来对其所有发送的消息进行签名,整个网络记录着每个矿工的ID。在DFINITY公有网络,矿工向去中心化的BNS管理系统支付一定的DFN代币作为准备金,BNS将为其在网络中创建一个安全帐户作为矿工ID。在DFINITY私有网络中,可以通过一个可信的代理,比如公司的系统管理员来创建矿工ID。

系统的每个矿工都需要通过USCID向系统提供一些可保证质量的计算资源,包括数据处理能力,网络带宽和存储容量(后面部分有细节解释)。随着网络规模的扩大,为防止广播通信的瓶颈,整个广播网络会被划分为若干子网。

进程间通过Kademlia DHT组成p2p网络,每个进程和很多其它进程间保持链接,通过gossip协议进行通信,基于此每个进程发出的消息很大程度上可以广播到所有其它进程,同时其它进程发出的消息这个进程也可以接收到。这种广播通信机制是去中心化网络的基础。但是攻击者仍然可以通过‘月食攻击’的方法对某一个进程进行攻击,也就是将一个正确的进程包围,使其链接的所有其它进程都是错误的进程,从而对这个正确进程发出和收到的消息进行过滤控制。在DFINITY的Tungsten版本中,我们将基于DFINITY内生的随机灯塔和基于身份的密码学算法是这种月食攻击更加的困难。通过将整个网络不停重组为各种带约束的随机形式,从而杜绝在网络中进行这种攻击的可能性。


阈值接力是如何工作的?

前面的FAQ已经描述了DFINITY网络中矿工的组织方式。阈值接力算法可以生成一个内生的随机灯塔,每一个新的值定义了矿工的随机组,这些矿工独立地尝试组建为阈值组。每个阈值组的组建都是完全随机的,不同组之间可能有交集,一个矿工可能在多种阈值组中。在DFINITY中每个组有400个成员。当其组建完成,组中的矿工将基于分布式密钥生成协议,为这个组生成一个BLS阈值签名。如果它们在给定的区块数目中完成了签名的生成,这个组就可以将它们的BLS阈值签名通过一个特定的事务请求将其公钥(ID)注册到全局区块链中。在下下个区块链的EPOCH周期,这个组将在网络中被激活,成为网络中的有效阈值组。DFINITY网络启动的时候,有若干预定义好的阈值组构成,并选定其中一个阈值组进行第一块签名。第一个签名也就是第一个随机值,此随机值用于选出下一个组,下一个组将会对前一个随机值进行签名,从而产出新的随机值,进而选出下下个组,这样不断进行接力,从而生成无限的随机数序列。

基于密码学的阈值签名,一个组中的矿工对某个消息的签名分片的数量超过阈值后就组合整个组的阈值签名,DFINITY网络中这个阈值设为51%。要完成一个消息的阈值签名,组中的矿工对这个消息分别进行独立签名,生成相互独立的组签名分片,并且将各自的组签名分片广播到组中其它矿工,在收到超过阈值数目的组签名分片后,就可以构建出整个组的阈值签名。比如,一个组的大小为400,如果他的阈值设定为201,任意一个矿工收集到201个组签名分片后,将可以构建出那个组对这个消息的唯一的阈值签名。组中每个矿工都可以验证组中其它矿工的组签名分片,整个组的阈值签名可以通过组的阈值公钥进行验证。BLS签名策略的神奇之处在于它的签名的唯一性和确定性。基于BLS的阈值签名,组中任意矿工组合,只要满足阈值数目,创造出来的阈值签名都是相同的,也就是对一个消息,这个组的阈值签名是唯一的,也是确定的。

因此,基于组的阈值签名所产生出来的随机数序列也是完全确定的,而且不可操纵的。因此各个阈值组间的阈值接力也就形成一个可验证的随机函数VRF的定义。给定一些阈值组,虽然它们所生成的随机数序列是完全确定的,每个随机数也只有在一个组中超过阈值的矿工对其进行签名后才能生成出来。也就是说,如果要攻破这个随机函数,也就需要打断这种阈值接力的过程,必须需要使某个组超过一定数量的矿工停止工作,所以在配置网络的时候需要恰当选择组大小和阈值,从而尽可能降低随机函数被攻破的概率。

比如,一个组的大小为400,阈值为201,那么需要使这个组超过200个矿工停止工作。假设整个网络中有10000个矿工,有3000个矿工是不工作的,在(400, 201)这个配置下,VRF函数被攻破的概率为10e-17。这结果是基于大数定律,即使每个矿工本身的行为是不可预测的,但是只要有足够多的矿工后,整体行为是可以进行预测的。

在保证VRF算法坚固的同时,整个系统也是非常高效的。在一个gossip广播网络中,一个400个矿工的阈值组接力一次阈值签名只需要大概20KB的数据通讯,而且DFINITY所使用的BLS签名算法,在当前主流硬件,完成一次签名只需要毫秒级以下的运算。


阈值接力的区块链是如何工作的?

阈值接力区块链,基于阈值接力所产生的随机数序列为每个高度的区块定义一系列具有优先级的备选区块,然后由当前区块高度对应的阈值组对其进行公证(签名)。比如,在区块高度h-1构建出来的随机数将对所有矿工进行优先级排序,第一的矿工位于slot 0,第二的位于slot 1。当高度为h的阈值组中的矿工发现前一个区块的签名结果选择当前阈值组为高度为h的阈值组,它们将停止区块链监测,然后等到当前区块时间结束后,开始对优先级队列中的区块进行处理。

出于效率考虑,区块时间一超时,slot0的矿工即可产生新的区块,其它slot等待slot0,若超时时间后未出块将接手产生新的区块。每个slot都有自己的权重,比如,slot0的权重为1,slot1为0.5,以此类推。当前阈值组的矿工按照如下规则对收到的区块进行阈值签名:

1. 之前没有对更高权重的链上的区块进行签名

2. 区块所指向的前一个区块被前一个组有效地签名

3. 区块本身的内容是有效的

4. 当前组尚未完成对当前高度某个区块签名

阈值组成员将持续对队列中的区块进行签名,直至发现当前阈值组已经完成当前高度某个区块的有效签名数,然后阈值组成员将停止对剩下的区块进行签名。通常情况下,在区块时间超时时,矿工已经准备好最高优先级的slot0中的区块,然后只对此区块做阈值签名就完成了整个组的阈值签名,而不需要对其它区块再做签名。不同slot中会有不同区块可以帮助阈值组选择最合适的链上的区块进行签名,但是由于新的区块的前一个区块必须被前一个阈值组签名过的,基于阈值组的公证策略可以加速区块链的最终确认。

相比,阈值接力区块链的公证流程有明显的优点。在传统的POW和POS区块链中,矿工总是可以通过超前攻击,从而从前面区块开始创造出新的分叉。在阈值接力的链上,只有在正确的时间,正确的阈值组中签名的区块才能被包含到有效的链上。这一点可以很好的的防止‘selfish mining和‘nothing at stack’攻击,同时极大地减少了区块链上区块的确认时间。而且,通常阈值接力链上只有slot0上有一个备选链,因此新的阈值组可以很快完成阈值签名,而且一旦签名完成,签名结果基本可信,因此阈值链上可以在秒级完成区块确认。

相比其它区块链,基于阈值接力的区块链有极大的优势。首先,它不需要昂贵算力的POW过程。如果需要,一个网络可以同时运行多个链而不会削弱网络的安全性。由于非常快的区块确认时间,基于它可以达到更好的用户体验。由于每个被选区块都是分配有固定的区块时间,鼓励矿工在区块中包含更多的交易事务。类似SPV的服务也可以实现,对应的客户端只需保存当前网络中阈值组的默克尔根哈希值。同时,由于备选链必须被公开公证,系统安全性也更加可控。


DFINITY如何进行横向扩展?

虽然相对于当前的其它区块链系统DFINITY有很大的性能提升,但是阈值接力本身仍然无法实现横向扩展。DFINITY网络将阈值接力应用在三层可横向扩展的系统架构中,共识、验证和存储。共识层通过阈值接力链产生随机心跳,来驱动验证层中验证塔的验证树,验证树采用Merkle树的形式实现数据验证。

随机灯塔同时将矿工组织到存储层的存储分片中,存储层在收到事务请求和对应的结果状态后,基于自己的阈值接力链实现共识,然后将其传递到验证层,然后验证层完成验证,最后传递到最上面的阈值接力共识链,将记录验证树的根节点的信息。

你可能发现整个区块中没有事务。是的,DFINITY云的目标是保存EB级别的状态,每秒钟完成亿级别的事务处理。每个矿工只能够看到全局状态的一小小部分,而网络则专注于确保只能通过合法的方式,执行正确的事务才能对系统的状态(root hash)进行修改。因此,任何数据的出处,任何事务的执行或者矿工本身所请求的任何活动,都是通过它们到当前全局Merkle根节点的路径来证明。

整个系统架构和其中的协议都是由Dominic Williams在2015早期完成设计,并且在旧金山的一次比特币开发聚会和一次伦敦的演讲中做了介绍。


验证塔是干什么的,他是如何工作的?

在传统的区块链系统中,比如比特币和以太坊,从创始块开始所有交易记录都保存在区块中,网络中的所有参与者都可以检查区块中的数据和对这些数据的更新操作是否合法。在DFINITY系统中,这一点是完全不可行的,因为DFINITY的系统设计目标是EB级的数据和亿级的TPS,网络任何一个节点不可能独自完成所有请求的处理。因此,DFINITY网络需要新的区块验证方法,只需要网络中一小部分矿工即可安全地完成各个分片的数据更新。这样,只需在其最顶层的链上保存一个Merkle根就可以锁定整个网络的状态。

要构造出Merkle根,首先需要一个验证树。和Merkle树通过一个根节点哈希就可以验证无限多的数据类似,验证树也可以在去中心网络中对无限多的事务实现公证。验证树的每一个节点对应一个验证塔,每个验证塔可以验证给定的输入信息所产生的输出信息的摘要,在塔的最底层是输入的事务请求和相应的状态转移结果。

验证塔的目的是使用网络中一小部分进程进行验证,同时可以达到网络中所有进程都进行验证类似的安全等级。初看起来这事不可能,但是实际是可行的。

一个验证塔可以有无限多层,塔中每添加一个新层将对新层下面的状态转移数据增加一些的新的证据。比如,一些事务更新了一些存储分片数据,就可以用一个验证塔来验证这些更新的有效性。验证塔的每一层都是由不同的进程组进行验证,进程组的选择则是由基于最顶层的阈值接力链的随机灯塔完成。一个进程组要完成了一个新层的构建,这个进程组需要为这个新层以及其向下d层中对应的状态转移的有效性作公证。

验证塔中向上d层都被某些进程组验证有效后,当前层中包含的事务才被整个网络认为是经过验证的,也是就是构建上面的d-1层的阈值组都为此层事务的有效性作证。也就是说,在一个进程组完成了一个新层的构建的同时,其下第d层中的事务也完成了‘完全验证’。这样就使攻击者作出的无效的事务很难实现完全验证,因为如果要一个无效事务达到完全验证的状态,需要连续控制d层的随机进程组,也许只有这个无效事务可以让他获利上百亿美元,才值得去尝试。

下面我们解释一下为什么这个是不可能的。首先考虑在一个矿工完成了对一个新层的签名后,它进入潜伏状态,直至这个新层上面有了d-1层后他才能再次启用(实际情况中,矿工在参与到网络事务处理时必须证明他的上一层已经完成验证)。攻击者在提交完错误的验证结果后,只能停止参与网络中事务的处理,但是每个矿工在创建的时候都有一个安全帐户,在其停止参与网络事务后,其帐户也无法继续操作。

攻击者可能通过概率来以小博大,那么他首先遇到的问题是每个层的构建组都是由随机灯塔选择的,随机灯塔本身是完全不可操纵、不可预测的,因此很难确保自己能被选中。如果有正确的矿工被选中,前面攻击事务所在层将会被认定为无效的。攻击者可能试图拒绝随机灯塔所选出的下一个阈值组,直至他所控制的阈值组被选中,然后继续标记攻击事务为有效。但是,验证塔协议本身禁止了这种情况:如果一个新层不是被随机灯塔的锁定阶段构建出来的,所有未完成验证的验证层都重新需要d-1层的验证。

现在你就能看出这其中的问题。攻击者每一次尝试提交一个错误的事务请求,期望他控制的阈值组能被随机选中执行接下来d-1层,有极大极大可能是他所控制的矿工将被强制退出。在这个例子中,没有一层验证失败,他将失去6个矿工进程。如果与进程关联的安全账户价值为10,000美元,则创建一个错误的层的代价是$ 5.36^e16。 很显然,在现实世界中这几乎不可能!

验证塔的好处是它使网络能够仅使用一小部分进程就可以非常安全地验证状态转换。 所有这些操作都需要一个不可破坏的、不可操纵和不可预测的由阈值接力链提供的随机源。


验证树是如何工作的?

验证树的功能是记录虚拟计算机的状态数据和网络中所发生的重要事件,基于这些数据计算出一个Merkle树。通过Merkle树的根节点的摘要,只需要20字节,实现对一个无限大输入数据的摘要。输入的数据将被组织到整个Merkle树的叶子节点,每个叶子节点的哈希向上组成一个n-ary的树。可以通过从根到叶子节点的路径上的哈希值来证明每个叶子节点数据的有效性。

通过构造一颗Merkle根,整个验证树可以锁定无限的数据。作为一个分布式的数据结构,每个验证塔作为树中的节点。这些操作都是在随机灯塔的心跳驱动之下,对输入进行验证,然后输出经过完全验证过的数据的哈希作为输出。在树的最底层是验证塔的下面是状态数据节点,通常也就是各个子网的矿工们所管理的数据分片。它们将状态转移信息输入到分配的验证塔中,然后验证塔最终将计算出它们所验证过的新的状态的哈希值。这个系统的强大之处在于,一个验证塔可以对其他验证塔的输出做验证,并且组成一个验证塔的Merkle树。

比如,当前的验证塔的最底层将为当前的某些状态数据分片的根节点哈希提供证词。在Merkle树中,它的父节点将会将其以及其兄弟节点组合在一起,一直向上直到构建出整个树的根节点。在DFINITY这样的大规模去中心网络中,存在一个问题就是,有太多的叶子节点哈希,一个单独的矿工无法将所有叶子节点构建出一个Merkle树。因此,我们希望将整个树的不同子集分配到不同的矿工,分别构建出整个树的不同部分,然后通过协议将它们组合在一起。但是这样如何验证整个树的有效性就成为了一个问题。解决方案就是通过验证塔将叶子节点组合在一起,向上形成一棵树,直至生成根节点哈希。这样,更高层的验证塔将其子节点的哈希组合到一起,生成一个新的经过完全验证过的哈希,然后继续上传到他的父节点,向上一直传递,直到生成根节点。

就这样,就有了一些可以生成正确根哈希的根验证塔,它们生成的完全验证过的根哈希被保存到整个网络最顶层的共识层中。由于验证塔之间相互独立运行,可以以各自不同处理速度运行,这样防止了某些子网的处理速度成为限制整个网络的处理速度的瓶颈。共识层中记录的最新的根节点的哈希就锁定了保存在众多分片中的某个全局状态,同样可以用于锁定一些网络中发生的重要事件。网络中参与节点可以通过,从根节点哈希到他所参与的验证塔的路径,来向网络中其他节点来证明他在网络中所作的贡献。采用这种方式,我们既可以完成对EB级别数据的锁定,也可以判断网络中的矿工的行为是否是有效的。

当然,从一个状态更新操作被执行,到共识层的系统根哈希完成对这个修改的锁定需要走相当长的一段路程(因为要经历整个树形结构中的验证塔的层层验证)。这一点是不可避免的,因为共识层中的数据必须是正确有效的,但是整个网络的处理速率并不一定需要下降。如果一个数据分配被足够多的矿工共同维护,则在这个分片广播出事务结果的时候,网络中的矿工就可以接受其作为事务的执行结果,而且,在最底层的验证塔对这个事务完成验证的时候,即使还需要一段时间才能在最顶层的共识层确认,就可以认为这个事务结果已经完全确认。对于运行在一个去中心云平台上的应用来说,额外多一些的计算费用也是完全可以接受的,毕竟它是运行在一个自治的,永不停机也不会被攻破的系统之上,相对可以极大减少人力维护的费用。


什么是USCID,他是如何工作的?

去中心化云的设计目标就是建造一个永不停机的计算平台,基于此平台可以构建永不停机的应用。这个目标要求客户端也能安全地存储数据。在比特币和以太坊平台,整个网络中只有一个链,链上所有事务都保存在这个链上,因此每个客户端都可以有完整的整个以太坊的状态数据库。DFINITY网络设计目标是保存无限量的数据,可能是EB级的数据,因此一个客户端不可能保存整个链的状态数据库。这就需要将整个链的状态数据库在所有客户端上进行分片,这就自然引出了如何进行分片来确保整个数据库的安全性。同时也就有另一个很重要的问题:我们怎么确定数据被真正的备份了?

这里面必须解决的问题就是即使有很多矿工看起来是保存了数据的备份,这些矿工可能是某个攻击者为赚到更多收益而创造出来的伪装,实际上并不做任何事情。在IPFS和其使用的激励系统FileCoin中就明显存在这个问题。IPFS是一个去中心化的文件存储,其保存的所有数据和对象都通过数据哈希进行访问。问题就在于,在一个用户上传了数据后,用户无法确定他的数据被备份了多少份,也不知道当前哪些IPFS矿工存储或缓存了他的数据,也不知道矿工会将它的数据保存多少时间。FileCoin的目标就是通过向矿工支付代币的形式来鼓励矿工更多更广泛的做数据备份。但是还有一个未解决的问题就是,IPFS协议无法确认这些客户端是不是只是一个超级计算机的代理客户端,它们的所有数据实际上都是同一个备份。

要为数据的安全性提供真实的保证,DFINITY网络必须确认网络中数据的备份因子。同时也确认网络中的备份数目也不要太多,毕竟每个文件都备份一百万份也很可笑。DFINITY的解决方案就是通过‘唯一状态拷贝ID -- USCID,要求每个矿工都采用唯一的方式对分配给它们的数据进行备份。这要求每个客户端使用一个从它们的网络ID生成的密钥对他的数据进行加密,而每个矿工的网络ID在网络中是公开的。具体加密算法是一个特殊定制的对称加密算法,整个算法加密速度相对较慢,但是解密速度很快,虽然在更新和写入数据的同时也可以进行数据加密,但是要在很短的时间内完成所有分配数据的加密是不可能的。

USCID系统要求客户端在通讯的时候对它们唯一加密的状态提供证词。比如,当一个客户端在一个阈值接力链的PSP中创造出一个备选块的时候,他必须提供这样一个证词。由于客户端要想使自己的备选块被包括到主链当中,从而可以获得一份奖励,备选块必须在一个几秒钟的时间窗口内被广播出去,这样就给攻击者制造了一个问题。证词是在客户端加密的状态数据上随机游走所产生出来的哈希链-从阈值接力网络中随机灯塔指定了随机游走的起始块-一个块加到哈希摘要,然后选择另一随机块,一直持续到指定步数。由于哈希计算是非常快的,只要之前的数据是已经加密好的,整个证词的生成也是很简单的。但是,如果之前的数据如果是明文保存的,比如在之前假设的那个超级主机上,数据就必须先进行加密然后计算哈希摘要,由于之前提到的经过定制的加密算法,要在短时间内计算出证词是不可能的。

在通常通讯中,每个客户端都需要生成这样的证词,但是这些证词通常不会被校验。然而,随机灯塔可以随机请求进行验证,或者当一个区块被接受一份奖励要被发放的时候,其他保存着相同数据的客户端可以对其证词进行校验。校验过程是,这个矿工会从当时随机游走的起点区块开始,首先对自己的数据进行解密,然后采用对应证词所有人的ID所生成出来的密钥对数据进行加密,然后计算哈希摘要,最终与证词进行比较验证。由于所使用的密码算法的特殊性,整个验证过程会需要一段时间,因此在进行验证的同时整个网络还会继续向前运行。当然,为防止网络重组造成的影响,客户端应当保存它不同历史版本的状态数据,这样当需要对历史数据进行验证的时候也没有问题。系统中也采用一个类似验证塔的数据结构来最终判决一个证词的有效性。如果发现无效证词,证词所有人的安全帐户将会被停止,分配给他的数据备份也将分配给其他的客户端。


好了,看到这里你一定眩晕了!!!恭喜你,可以返回连载一重新开始理解DFINITY网络了



下一篇 带来激动人心的挖矿简介

「这年头有搞技术的初心不容易」
还没有人赞赏,快来当第一个赞赏的人吧!
文章被以下专栏收录
2 条评论