本篇详细讲述了区块链中非常重要的“共识”算法及其安全性,由于文章内根据不同应用环境讲述了不同的共识算法及其安全,所以涉及到的内容相对比较丰富。
文章内容为原创,并且从基础部分开始讲述,所以更适合初学者。文章分为多个系列,此文为系列一。

前言部分

区块链作为一种去中心化的分布式公共数据库,其并没有中央管理机构进行管理工作,而是通过分布式节点共同利用密码学协议共同维护。而各个节点在维护整个系统的的时候要通过底层的共识协议来保证账本的一致性。而在区块链中我们又可以分为公有链和许可链,而由于实际的用途不同,这些共识算法也有所不同。而不同的共识算法所涉及的安全性又有所区别。

一、共识安全模型分析

我们知道,区块链的本质是一个分布式系统。而对于分布式系统来说,不同的共识模型在不同的环境下能够容忍的错误类型与数理也是不同的。所以我们可以由分布式系统共识的评判标准引申出对区块链的安全性定义。

1 同步模型

顾名思义,同步模型要求发送消息方与接收方直接类似于面对面应答。即其延迟时间 Δt 是确定的,并且算法的执行时间也是确定的。然而同步模型在现实世界中并不存在,在分布式系统中延时是肯定存在的,所以延迟时间就并不确定。所以这种模型也就只是一种假设,如果此假设被应用于实际,那么我们依据此模型而进行的进一步研究的正确性将无法被保证。从另一个角度想,如果在同步模型的假设下,依然没有算法可以解 决某个问题,那么在异步模型下,失去了可以预测的时 间,这个问题更加没办法解决。

2 异步模型

异步模型中,消息的延时时间 Δt 并不是确定的,可以是任意长的时间。在异步模型中,程序执行到某个语句可能会因为消息的等待而进入阻塞状态,所以其执行时间是非常不确定的。所以使用此算法也有一定的弊端,例如我们无法判断节点消息是由于延时还是由于节点已经宕机。然而这种算法可以保证程序的安全性,但是对系统的灵活性并不友好。

3 半同步/半异步模型

为了将上述两种模型的优点集成起来,我们基于时间模型上设计了半同步 / 半异步模型。在这个模型的架构中,我们会设计一个最长超时限制 tmax。倘若时间超过此tmax而消息接收方依然没有收到消息,那么我们就可以大胆的假设这个节点已经宕机,并不在等待这个节点消息。

二、Pow共识算法

目前的数字货币大多使用工作量证明机制来进行 维护账本。工作量证明机制是由 Nakamoto 匿名提出, 将其用来作为比特币系统的共识机制,其主要思想是 使用计算能力寻找特定的数字来使区块满足要求。

因为区块链系统是以去中心化为其特性,那么我们的每个节点都需要一份本地的完整账本来对整个区块链网络的信息进行把控。并且在更新账本的时候要以来其中某个节点的帮助,但是每个节点却不能同时记账,因为节点处于不同的环境,接收到不同的信息,如果同时记账的话,必然会导致账本的不一致,造成混乱。因此,需要有共识来达成哪个节点有权记账。比特币区块链通过竞争记账的方式解决去中心化的记账系统的一致性问题, 即以每个节点的计算能力即“算力”来竞争记账权的机制。 

在比特币系统中,大约每10分钟进行一轮算力竞赛,竞赛的胜利者,就获得一次记账的权力,并向其他节点同步新增账本信息。然而,在一个去中心化的系统中,谁有权判定竞争的结果呢?比特币系统是通过一个称为“工作量证明”(Proof of Work,PoW)的机制完成的。

简单来说,Pow就是一种所有节点均遵循的共识,而这个共识要求每个节点进行一定难度的哈希计算工作,正如我们的工作制度一样。工作端需要做一定难度的工作得出一个结果,验证方却很容易通过结果来检查工作端是不是做了相应的工作。

举个例子,假设我们给定字符串“blockchain”,我们在此字符串后面连接nonce随机数(32位)并对连接后的字符串进行SHA256哈希运算,如果得到的哈希结果(以十六进制的形式表示)是以若干个0开头的,则验证通过。为了达到这个工作量证明的目标,我们需要不停地递增nonce值,对得到的新字符串进行SHA256哈希运算。

blockchain1 → 4bfb943cba9fb9926df93f33c17d64b378d56714e8a29c6ba8bdc9690cea8e27  
2 blockchain2 → 01181212a283e760929f6b1628d903127c65e6fb5a9ad7fe94b790e699269221 ……
3 blockchain515 → 0074448bea8027bebd6333d3aa12fd11641e051911c5bab661a9b849b83958a7……
4 blockchain2688 → 0009b257eb8cf9eba179ab2be74d446fa1c59f0adfa8814260f52ae0016dd50f……
5 blockchain48851: 00000b3d96b4db1a976d3a69829aabef8bafa35ab5871e084211a16d3a4f385c……
6 blockchain6200969: 000000db7fa334aef754b51792cff6c880cd286c5f490d5cf73f658d9576d424

按照这个规则,需要经过2688次计算才能找到前3位均为0的哈希值,而要找到前6位均为0的哈希值,则需进行620969次计算。

所以简单来说,系统通过激励措施鼓励用户通过维护区块链系统来获取利益。参与共识过程的用户收集新产生的交易记录构造区块,尝试修改区块中 Nonce 的值,直到该区 块的哈希值小于特定难度的哈希值,便可以对外广播 区块。该区块得到其他用户验证和认可,成功添加到主链之后,用户就可以获得相应的奖励。

在这里我们将一个区块表示为包含三元组的数据 包 B = (h',tx,nonce),其中h' 是前一个区块的哈希, tx 是区块中所包含的交易记录,nonce 是一个 32 比特的整数。为了达到某个共识,区块链系统会针对当前系统中用户量或者整体计算量的情况进行难度值D的设定。而D 定义了当前整个区块哈希值需要有多少位前导 0,前导 0 数量越多,难度越大。由于 nonce 改变任意一个比特都会使整个区块的哈希 H(B)完全改变,所以没有方法可以 预测哪种形式的 nonce 可以符合要求。因此为了达到 区块的要求,节点需要用其计算资源尝试大量可能的 nonce 值使得 H( B) < D。

系统输入:tx、nonce、D

Pow过程:
1:nonce = 1(初值)
2:while(H(nonce,tx,h') > = D):
3:          nonce++
4:Broadcast( < nonce,tx,h' > )//广播计算的值
5:end

既然Pow可以解决这么多问题,那么其存在的安全隐患是什么呢?我们看下面的内容。

三、Pow攻击详解

1 双花攻击

简单来说,双花攻击就是指同一个货币被花费了多次。在区块链网络中,每个用户的每一次交易都可以对应一个网络请求。而区块链整个系统会进行对此请求的验证。其中包括检查其资产的有效性、是否已经使用已花费的资产来进行交易。经过全网节点的检验后,广播这个成功验证的账本。

举个例子来说,小明拿着编号为123456的一百元去购买《区块链安全》这本书,之后又拿这个123456编号的钱同样购买了《人工智能》这本书。此时小明就使用同样的一份钱购买了两次商品。

那传统的方面是如何解决这种情况的呢?防止双花从交易和货币本身来控制,首先从交易上,例如我们都是以刷卡交易,卡上的资产都是在银行作为第三方参与者手中,我们将100元从小明的手里转到了商家手里,假如这个过程中存在延时,那么小明几乎是同时在两个地方消费了100元,银行也就同时收到了关于这笔钱的两次消费。然而银行会按顺序一笔一笔处理,第一笔处理完之后,小明已经没钱了,第二笔自然就失败。

那区块链系统是如何解决这个问题的呢?

1 首先,假设我们的比特币拥有者A想要从B处购买一本书,他会像全网广播:A向B支付一个bitcoin去购买。

在广播这个信息的时候,A会将这个信息使用哈希函数加密并使用A自己的私有进行签名。

接收到这条信息的B和其他用户先用同样的Hash函数对明文信息生成摘要,再用A的公钥对加密信息进行解密,如果解密得到的摘要与明文生成的摘要相同,便认为信息确实是A发出的,且没有经过篡改。

A的公钥和Hash是公开的,私钥则无法算出,只有A知道,这样就既保证了交易的达成,又保证了A的信息无法被窃取。

2 假设黑客想对这笔交易进行攻击,那么他需要获得区块记账权。然而是否拥有记账权取决于黑客是否能算出符合要求的哈希值。而这一步是困难的,代价也是巨大的,所以基于Pow的共识机制也就保证了大概率的数据安全。

3 由于区块链分布式系统的特性,其在交易的时候存在延时是不可避免的,所以交易并不是立刻执行。所以交易确认的时间要长很多,使得这种诈骗有可能实现,这就是比特币的double spending双重花费问题,简称“双花”。

对于双花问题,说区块链网络是如何应对的呢?

2 51%攻击

然而在Pow体系下,系统同时允许存在多条分叉链,而每一条链均可以声明自己的正确的。但是在区块链的设计理念中有一个最长有效原理:“不论在什么时候,最长的链会被认为是拥有最多工作的主链。”比特币目前需要12个区块的确认时间才可以保证交易的不可篡改。

下面我们模拟一下具体的过程:

如果攻击者者把第一笔交易向一半网络进行广播,把第二笔交易向另一半网络广播,然后两边正好有两个矿工几乎同时取得记账权,把各自记的区块发布给大家的话,网络是不是会混乱呢?

即在原来的账本中出现了分叉的情况。

根据我们上面提到的,假设下一个矿工选择在A基础上继续记账的话,A分支就会比B分支更长,根据区块链的规则,最长的分支会被认可,短的分支会被放弃,账本还是会回归为一个,交易也只有一笔有效,而B链分支会被舍弃:

加入作恶人在A分支确认并拿到商品后自己立刻变为矿工,并争取到连续两次记账的权利,在B分支上添加两个区块呢?:

于是B分支成为认可的分支,A被舍弃,A分支中的交易不再成立,但他已经拿到商品,诈骗成功。

我们发现这其实就是一种攻击方法。但是我们有没有想过上述攻击成立的条件是B拿到了连续两次记账的权利。我们可以大致算一下:

假设诈骗者掌握了全网1%的计算能力,那么他争取到记账权的概率就是1%,两次就是10的负4次方。那还是存在一定概率进行攻击的。

既然连续两次的概率比较大,那么我们就将次数增加。也就是等6个入链并被确认后再把交易对应的商品交付。

这样概率也就几乎为零了(在百分之1的算力情况下)。

下面我们就算个数,倘若我掌握了全网的51%的算力,那么我成功记录六个区块的概率是多少呢?

即0.018,也就是说,几乎100笔交易中我就有可能进行一次攻击。那这样的危害也就很大了。

3 自私挖矿

在Pow机制中,除了上述通过“算力”这种大吃小的安全隐患外,还有一种通过小技巧进行的安全危害——“自私挖矿”。

诚实挖矿的策略如下:其挖到区块链后就对其进行全网广播。而在自私挖矿的策略中,矿工维护两个区块链,其中一个链是公开的一个是私密的。一开始私密区块链等于公开区块链。在矿工挖到区块之后,它并不将新挖的区块加入公开链中,而是添加到本地的私密区块链中,并不广播给其他人。而我们可以想象到,加入我已经知道上一个区块的值,我就可以先人一步进入下一个区块的计算中。(如同我们考试的时候提前将第一天做出来,就可以快人一步做第二道题)。即使当前区块被被人提前发了出来,我们也可以将自己挖出的区块公布到全网中。

而诚实矿工可能会在不知情的情况下选择 “支持” 攻击者所在分叉:即在攻击者发布的区块后面继续挖矿。这是如何出现的呢?当攻击者的长链分叉信息率先传递到某个诚实矿工那里,这个诚实矿工会根据比特币的共识机制,认可该分叉内区块的合法性,并且选择在该长链尾部继续挖矿。这令攻击者处在更为有利的位置上。事实上,出现这种情况,恰恰是由于比特币网络信息传播存在时延所致。对于网络上的其他节点来说,这两个区块的高度是相同的,所以被其他节点承认的概率还有1/2,这样自私矿工就有了相对于其他人的优势。

然而自私挖矿策略仅对使用Pow共识有效果,对Pos等不适用。

四、Pos、DPos共识算法

Pos算法:由于区块链的分布式结构,所以区块链的共识过程也就是我们所说的选择一个leader的过程,并且有这个领导人来进行新的区块的发布工作,但是在设计共识的过程中我们也要避免单个用户或者财团对账本的长期控制。而根据我们上述的Pow机制,我们发现这种共识机制会浪费巨大的电力资源,并且存在了许多安全隐患,例如自私挖矿与双花攻击。

正式由于Pow中存在的不足情况,Pos孕育而生。Pow中文名为股权证明机制,它希望使用股权来代替或者部分代替计算资源来完成这个领 导人选举的过程。在股权证明机制的过程中,共识算法根据参与共识过程每个人所持有股权比例来选择下一个出块的人。

这一种思想也是参考了我们生活中的经济社会。即一个人拥有的股份数量越多,其获得的股份以及红利越多。这也使得区块链在币圈中更加贴近金融,有着通胀的情况产生。我们来看实际的协议是如何运行的。

例如 Peercoin,其协议在运行的过程中采用币龄作为一个参考变量来影响下一个区块的挖矿难度。在共识过程中,节点需要提交一份交易记录来证明对区块链资产的拥有权,并且拥有的区块链资产越多,持有时间越长,挖矿就会越容易。算法希望用户对自己所拥有的资产进行证明,而这些资产可以影响矿工的挖矿难度。拥有的资产越多,就越有机会计算出符合条件的 nonce。因此其计算公式为:success = <coins * age * target(难度值)>

而当proofHash < success的值时,挖矿成功。

系统输入:tx、nonce、D

Pos过程:
1:nonce = 1(初值)
2:coins←accountBalance
3:age←currentTime-lashTransactionTime //币龄
4:while(H(nonce,tx,h') > = coins * age * D):
5:          nonce++
6:Broadcast( < nonce,tx,h' > )//广播计算的值
7:end

虽然Pos算法比起Pow有其独特的优点用以避免计算资源的竞争,但是它仍会存在某些问题。比如用户长时间拥有了全网的大量资产,那么根据计算公式,他的计算难度就比其他节点小很多,这样就会造成严重的“贫富不均”情况。不进如此,Pos仍需要进行哈希计算,所以仍然存在浪费计算资源的情况。

所以我们设计了第三种公式机制——DPos。

DPOS 是去中心化交易所 Bitshares 提出并使用的
共识算法。在Bitshares中存在三种类型的用户:见证人(witnesses),代表(delegates)和工人(work- ers)。见证人如同矿工,通过处理交易和维护区块链来获得报酬。而代表可以发起更新请求,但是他们并没有报酬。工人可以提出自己下一步的项目想法,如果此项目获得了大部分人的投票支持,那么他们会从中获得收益。

DPos在共识期间分为“见证人选举”和“见证人出块”两个过程。见证人对各个交易进行验证签名与时间。在网络中每个账户都可以为下一个见证人投票,而票数要根据区块链的资产情况来分配。

见证人选举过程如下:

只有拥有被选举权的永久节点才能够被选举,最终只有前N名见证人可以被选举出来。这N个人都要获得50%以上的票数才能够顺利当选,除此之外,这个名单会按照固定的时间间隔进行重新选举。

见证人出块过程如下:

见证人每生产一个块,都会获得报酬,他们的薪酬水平由其获得的投票决定。如果见证人没有生产区块,他们便没有收入,并且还有可能被 投票失去见证人的身份。

见证人生产区块时,每 2 s 生产一个区块,如果见 证人没有在规定的时间生产块,那么这个见证人将会

每2s需要产生一个区块,如果超过了规定的时间,那么当前见证人就会失去生产权利而转交给下一个人。

网络中所有的用户均有责任监控区块链生成的过程,并且同意需要选择分叉最长的那个链进行追加。

五、确定性共识算法攻击

1 确定性算法重放攻击

传统计算机术语中,重放攻击(Replay Attacks)又称重播攻击、回放攻击,是指攻击者发送一个目的主机已接收过的数据包,来达到欺骗系统的目的。重放攻击在任何网络通过程中都可能发生,是计算机世界黑客常用的攻击方式之一。

在区块链技术中,重放攻击是指“一条链上的交易在另一条链上也往往是合法的”。简单来说,就是攻击者“重放”他们在网络上窃听到的消息。

由于硬分叉的两条链,它们的地址和私钥生产的算法相同,交易格式也完全相同,因此导致在其中一条链上的交易在另一条链上很可能是完全合法的。所以你在其中一条链上发起的交易,就可以到另一条链上去重新广播,可能也会得到确认。这就是“重放攻击”。而这种攻击一旦发生,就会产生类似于双花攻击那样的效果:同一笔钱转给了同一个人两次,就会导致在不需要付款人参与的情况下多一次支付。

那我们如何防止重放攻击呢?

下面我们讲解两种方法去防止:

而在传统的密码学协议中,我们首先可以添加时间戳来防止重放攻击:

DateTime current = DateTime.Now;     // 每次访问的时间
if (current >= _dt.Add(RefreshTime)) // 如果访问的时间大于一个特定的时间戳,则需要更新保存的时间
{
    _dt = current;
    // 生成新的nonce值
    // ...
}

在client每次访问的时候,需要查看这个时间是否过期了,如果过期了,需要重新记录下当前的访问的时间。

除此之外,我们可以使用挑战相应机制,每次发送挑战并使用公钥密码去验证对方身份,以杜绝中间人的破坏行为。

2 权利压迫攻击

这种攻击方法简单来说,就是攻击者在获得记账权的时候利用手中的部分权利实施一些操作让系统的随机数产生偏移用以增加自己下一次获得记账权力的可能性。

方式一:验证者通过一些参数的组合找到一些特殊参数用以增加自己被选择的可能性。

方式二:利用对当前区块的控制力来影响下一个区块。

例如:假设N+1个区块的随机性依赖于区块N的签名,那么如果攻击者在当前区块中始终指向自己,那么他将永远获得记账权。

在解决这类问题时,我们可以采取如下手段:让验证者抵押自己的资产以避免作恶,并且避免使用那些容易被操控的信息来产生随机数。

六、参考资料

本稿为原创稿件,转载请标明出处。谢谢。

源链接

Hacking more

...