区块链常见共识算法

3/15/2021 consensusBFTblockchain

本文主要总结区块链几种常见的共识机制(PBFT,Raft,PoW,PoS,DPoS,Ripple)。尽量使用简单易懂语言,篇幅较大,想了解的可以只读每个算法介绍中前边的原理。本篇文章主要参考《区块链技术指南》,首先表示感谢!

区块链架构是一种分布式的架构,其部署模式有公共链联盟链私有链三种,对应的是去中心化分布式系统部分去中心化分布式系统弱中心分布式系统

分布式系统的多个主机通过异步通信方式组成网络集群。在这样的一个异步系统中,主机之间需要进行状态复制,以保证每个主机达成一致的状态共识。然而,异步系统可能出现无法通信的故障主机,而主机的性能可能下降,网络可能拥塞。这些可能导致错误信息在系统内传播。因此需要为默认不可靠的异步网络定义容错协议,以确保各主机达成安全可靠的状态共识。

所谓共识,简单理解就是指大家都达成一致的意思。其实在现实生活中,有很多需要达成共识的场景,比如开会讨论,双方或多方签订一份合作协议等。区块链节点必须要做的事情就是让自己的账本跟其他节点的账本保持一致。在传统的软件结构中,这几乎就不是问题,因为有一个中心服务器存在,也就是所谓的主库,其他的从库向主库看齐就行了。在实际生活中,很多事情人们也都是按照这种思路来的,比如企业老板发布一个通知,员工照着做。但是区块链是一个分布式的对等网络结构,这个结构没有哪个节点是“老大”,一切都要商量着来。

因此,在区块链系统中,如何让每个节点通过一个规则将各自的数据保持一致是个很核心的问题。这个问题的解决方案就是制定一套共识算法,实现不同节点账本数据的一致性和正确性。这就需要借鉴分布式系统现有的状态共识算法,确定网络选择记账节点的机制,以及如何保障账本数据在全网形成正确、一致的共识。

共识算法其实就是一个规则,每个节点都按照这个规则去确认各自的数据。暂且抛开算法的原理,先来想一想生活中我们会如何解决这样一个问题:假设一群人开会,这群人没有领导或者老大,大家各抒己见,那么最后如何统一出一个决定出来呢?实际处理的时候,我们一般会在某一个时间段选出一个人,那个人负责汇总大家的内容,然后发布完整的意见,其他人投票表决,每个人都有机会来做汇总发表,最后谁的支持者多就以谁的最终意见为准。这种思路其实就是一种共识算法。然而在实际过程中,如果人数不多并且数量确定,还好处理;如果人数很多且数量不固定,通过这种方式投票决定的效率太低。我们需要通过一种机制筛选出最有代表性的人。共识算法就是筛选出具有代表性的节点。

那如何筛选呢?其实就是设置一组条件,就像筛选尖子生一样,给一组指标让大家来完成,谁能更好地完成指标,谁就能有机会被选上。区块链系统存在着多种这样的筛选方案,比如 PBFT(Practical Byzantine Fault Tolerance,实用拜占庭容错算法)、PoW(Proof of Work,工作量证明)、PoS(Proof of Stake,权益证明)、DPoS(Delegate Proof of Stake,委托权益证明)、Ripple(瑞波)等。各种不同的算法,其实就是不同的游戏玩法。

# 1. 拜占庭容错

拜占庭容错技术(Byzantine Fault Tolerance,BFT)是一类分布式计算领域的容错技术。拜占庭假设是对现实世界的模型化,由于硬件错误、网络拥塞或中断以及遭到恶意攻击等,计算机和网络可能出现不可预料的行为。拜占庭容错技术被设计用来处理这些异常行为,并满足所要解决的问题规范要求。

拜占庭容错技术来源于 拜占庭将军问题 (opens new window)

分布式系统特别是区块链网络环境,和拜占庭将军的环境类似,有运行正常的服务器(类似忠诚的拜占庭将军),有故障的服务器,还有恶意的服务器(类似叛变的拜占庭将军)。共识算法的核心是在正常的节点间形成对网络状态的共识。

通常,这些发生故障节点被称为拜占庭节点,而正常的节点即为非拜占庭节点

拜占庭容错系统是一个拥有 n 个节点的系统,整个系统对于每一个请求,满足以下条件:

  • 所有非拜占庭节点使用相同的输入信息,产生同样的结果
  • 如果输入的信息正确,那么所有非拜占庭节点必须接收这个信息,并计算相应的结果

拜占庭系统普遍采用的假设条件包括:

  • 拜占庭节点的行为可以是任意的,拜占庭节点之间可以共谋
  • 节点之间的错误是不相关的
  • 节点之间通过异步网络连接,网络消息可能丢失、乱序并延时到达,但大部分协议假设消息在有限的时间里能传达到目的地
  • 服务器之间传递的信息,第三方可以嗅探到,但是不能篡改、伪造信息的内容和验证信息的完整性。

原始的拜占庭容错系统由于需要展示其理论上的可行性而缺乏实用性,另外还依赖额外的时钟同步机制算法的复杂度也是随节点增加而指数级增加

# 2. PBFT

实用拜占庭容错系统(PBFT)降低了拜占庭协议的运行复杂度,从指数级别降低到多项式级别,使拜占庭协议在分布式系统中应用成为可能。

PBFT 是一种状态机副本复制算法,即服务作为状态机进行建模,状态机在分布式系统的不同节点进行副本复制。每个状态机的副本都保存了服务的状态,同时也实现了服务的操作。将所有副本组成的集合使用大写字母 R 表示,使用 0 到 |R|-1 的整数表示每一个副本。为了描述方便,通常假设故障节点数为 m 个,整个服务节点数为 |R|=3m+1 个,这里 m 是有最大的失效副本数。尽管可以存在多于 3m+1 个副本,但是额外的副本除了降低性能之外不能提高可靠性。

PBFT 要求共同维护一个状态,所有节点采取的行动一致。为此,需要运行三类基本协议,包括一致性协议检查点协议视图更换协议。我们主要关注支持系统日常运行的一致性协议。一致性协议至少包含若干个阶段:请求(request)、序号分配(pre-prepare)和响应(reply)。根据协议设计的不同,可能包含交互(prepare),序号确认(commit)等阶段。

PBFT 协议通信模式

上图为 PBFT 协议通信模式,每一个请求需要经过 5 个阶段,通过采用两次两两交互的方式在服务器达成一致之后再执行客户端的请求。由于客户端不能从服务器端获得任何服务器运行状态的信息,PBFT 主节点是否发生错误只能由服务器监测。如果服务器在一段时间内都不能完成客户端的请求,则会触发视图更换协议。其中 C为客户端,0~3 表示服务节点。特别地,0 为主节点,3 为故障节点。整个协议的基本过程如下:

  1. 客户端发送请求,激活主节点的服务操作。
  2. 当主节点接收请求后,启动三阶段协议以向各从节点广播请求。
    1. 序号分配阶段,主节点给请求赋值一个序列号 n,广播序号分配消息和客户端的请求消息 m,并将构造 PRE-PREPARE 消息给各从节点;
    2. 交互阶段,从节点接收 PRE-PREPARE 消息,向其他服务节点广播 PREPARE 消息;
    3. 序号确认阶段,各节点对视图内的请求和次序进行验证后,广播 COMMIT 消息,执行收到的客户端请求并响应客户端。
  3. 客户端等待来自不同节点的响应,若有 m+1 个响应相同,则该响应即为运算的结果。

PBFT 在很多场景都有应用。在区块链场景中,一般适合于对强一致性有要求的私有链和联盟链场景。例如,IBM 主导的区块链超级账本项目 hyperledger/fabric (opens new window),PBFT 是一个可选的共识协议。在 hyperledger/fabric (opens new window) 项目,共识模块被设计成可插拔的模块,支持像 PBFT、Raft 等共识算法。

# 3. Raft 协议。

对于适用 Raft 协议的分布式系统,其假设条件不需要考虑拜占庭故障,而只是处理一般的死机故障。在这种情况下,采用 Paxos 等协议会更加高效。Paxos 是 Lamport 设计的保持分布式系统一致性的协议。但由于 Paxos 非常复杂,比较难以理解,因此后来出现了各种不同的实现和变种。Raft 是由 Stanford 提出的一种更易理解的一致性算法,意在取代目前广为使用的 Paxos 算法。目前,各种主流语言都有了一些开源实现,比如本文使用的基于 JGroups 的 Raft 协议实现。关于 Raft 的原理,强烈推荐 动画版 Raft 讲解 (opens new window)

Raft 最初是一个用于管理复制日志的共识算法,它是一个为真实世界应用建立的协议,主要注重协议的落地性和可理解性。Raft 是在非拜占庭故障下达成共识的强一致协议

在区块链系统中,使用 Raft 实现记账共识的过程可以描述如下:首先选举一个 leader,接着赋予 leader 完全的权力管理记账。leader 从客户端接收记账请求,完成记账操作,生成区块,并复制到其他记账节点。leader 简化了记账操作的管理。例如,leader 能够决定是否接受新的交易记录项而无需考虑其他记账节点,leader 可能失效或与其他节点失去联系,这时系统就会选出新的 leader。

在 Raft 中,每个结点会处于下面三种状态中的一种:

  • follower:所有结点都以 follower 的状态开始。如果没收到 leader 消息则会变成 candidate 状态
  • candidate:会向其他结点“拉选票”,如果得到大部分的票则成为 leader。这个过程就叫做选主(leader election)
  • leader:所有对系统的修改都会先经过 leader。每个修改都会写一条日志。leader 收到修改请求后的过程如下,这个过程叫做日志复制(Log Replication):
    1. 复制日志到所有 follower 结点(replicate entry)
    2. 大部分结点响应时才提交日志
    3. 通知所有 follower 结点日志已提交
    4. 所有 follower 也提交日志
    5. 至此,整个系统处于一致的状态

Raft 阶段主要分为两个,首先是选主,然后在选举出来的 leader 基础上进行正常操作,比如日志复制、记账等。

# 1. 选主

follower 在选举超时时间内未收到 leader 的心跳消息时,转换为 candidate 状态。为了避免选举冲突,这个超时时间是一个 150~300ms 之间的随机数。

一般而言,在 Raft 系统中:

  1. 任何一个服务器都可以成为一个候选者 candidate,它向其他服务器 follower 发出要求选举自己的请求。
  2. 其他服务器同意了,发出 OK。注意:如果在这个过程中,有一个 follower 宕机,没有收到请求选举的要求,此时候选者可以自己选自己,只要达到 N/2+1 的大多数票,候选人还是可以成为 leader 的。
  3. 此时,这个候选者就成为了 leader 领导人,它可以向选民 follower 发出指令,比如进行记账。
  4. 以后通过心跳进行记账的通知。
  5. 一旦 leader 崩溃了,那么 follower 中有一个成为候选者,并发出邀票选举。
  6. 其他 follower 同意后,其成为 leader,继续承担记账等指导工作。

# 2. 日志复制

Raft 的记账过程按以下步骤完成:

  1. 假设 leader 已经选出,这时客户端发出增加一个日志的要求;
  2. leader 要求 follower 遵从他的指令,都将这个新的日志内容追加到他们各自日志;
  3. 大多数 follower 服务器将交易记录写入账本后,确认追加成功,发出确认成功信息;
  4. 下一个心跳中,leader 会通知所有 follower 更新确认的项目。

对于每个新的交易记录,重复上述过程。

过程若发生网络通信故障,使得 leader 不能访问大多数 follower,那么 leader 只能正常更新它能访问的那些 follower 服务器。而大多数的服务器 follower 因为没有了 leader,他们将重新选举一个候选者作为 leader,然后这个 leader 作为代表与外界打交道,如果外界要求添加新的交易记录,这个新的 leader 就按上述步骤通知大多数 follower。网络通信恢复后,原先的 leader 就变成 follower。在失联阶段,老 leader 的任何更新都不能算确认,必须全部回滚,接收新 leader 的新更新。

# 4. PoW

从去中心化账本系统的角度看,每个加入这个系统的节点都要保存一份完整的账本,但每个节点却不能同时记账,因为节点处于不同的环境,接收到不同的信息,如果同时记账的话,必然会导致账本的不一致,造成混乱。因此,需要有共识来达成哪个节点有权记账。比特币通过竞争记账的方式解决去中心化记账系统的一致性问题, 即以每个节点的计算能力即“算力”来竞争记账权的机制。

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

简单地说,PoW 就是一份确认工作端做过一定量工作的证明。PoW 系统的主要特征是计算的不对称性。工作端需要做一定难度的工作得出一个结果,验证方却很容易通过结果来检查工作端是不是付出了相应努力。

举个例子,给定字符串“blockchain”,我们给出的工作量要求是,可以在这个字符串后面连接一个称为 nonce 的整数值串,对连接后的字符串进行 SHA256 哈希运算。如果得到的哈希结果(以十六进制的形式表示)是以若干个 0 开头的,则验证通过。为了达到这个工作量证明的目标,我们需要不停地递增 nonce 值,对得到的新字符串进行 SHA256 哈希运算。假设计算过程如下,按照这个规则,需要经过 2688 次计算才能找到前 3 位均为 0 的哈希值,而要找到前 6 位均为 0 的哈希值,则需进行 620969 次计算。

1 blockchain1       -> 4bfb943cba9fb9926df93f33c17d64b378d56714e8a29c6ba8bdc9690cea8e27  
2 blockchain2       -> 01181212a283e760929f6b1628d903127c65e6fb5a9ad7fe94b790e699269221
3 blockchain515     -> 0074448bea8027bebd6333d3aa12fd11641e051911c5bab661a9b849b83958a7
4 blockchain2688    -> 0009b257eb8cf9eba179ab2be74d446fa1c59f0adfa8814260f52ae0016dd50f
5 blockchain48851   -> 00000b3d96b4db1a976d3a69829aabef8bafa35ab5871e084211a16d3a4f385c
6 blockchain6200969 -> 000000db7fa334aef754b51792cff6c880cd286c5f490d5cf73f658d9576d424
1
2
3
4
5
6

上面计算特定 SHA256 运算结果的示例应该能够让我们对 PoW 机制有个初步的理解。对于特定字符串后接随机 nonce 值所构成的串,要找到这样的 nonce 值,满足前 n 位均为 0 的 SHA256 值,需要多次进行哈希值的计算。一般来说,n 值越大,需要完成的哈希计算量也越大。由于哈希值的伪随机特性,要寻找 4 个前导 0 的哈希值,预期大概要进行 216 次尝试,这个数学期望的计算次数,就是所要求的“工作量”。

任何一个比特币节点,如果想生成一个新区块并写入区块链,必须解出比特币网络的 PoW 问题。这道题关键的 3 个要素是工作量证明函数区块难度值。工作量证明函数是这道题的计算方法,区块决定了这道题的输入数据,难度值决定了这道题所需要的计算量。

# 1. 工作量证明函数及区块数据计算过程

比特币使用的工作量证明函数就是 SHA256 (opens new window)

比特币区块结构如下图所示:

以下是比特币高度为 674664 (opens new window) 的区块结构

版本 2 000 400 016 10
父区块哈希 0000000000000000000ac7801203daa18a1dbbc1b44ddf4bd47ef6ae9229e49c
默克尔树根哈希 5b2f5a204c024eea3b37e2f2d5ec3cce92af8fc226b4ed872e9570a65634c003
时间戳 1615740994
比特 386736012
随机数 1922272427
交易计数 2,329
铸币交易
交易
...

所得区块链哈希为 00000000000000000004cf3cd7bef5f00c3f6aee50ff547eaed81e380db9d0f5

比特币的区块由区块头及该区块所包含的交易列表组成。区块头的大小为 80 字节,由 4 字节的版本号、32 字节的父区块哈希、32 字节的默克尔树根哈希值、4 字节的时间戳(当前时间)、4 字节的当前难度值、4 字节的随机数组成。区块包含的交易列表则附加在区块头后面,其中的第一笔交易是铸币(coinbase)交易--为了让矿工获得奖励及手续费的特殊交易。

拥有 80 字节固定长度的区块头是用于比特币工作量证明的输入字符串。因此,为了使区块头能体现区块所包含的所有交易,在区块的构造过程中,需要将该区块要包含的交易列表,通过 默克尔树算法 (opens new window) 生成默克尔根哈希值,并以此作为交易列表的哈希值存到区块头。其中默克尔树的算法图解如下图所示。

                         +------------------------+
                         |     MerkleRoot         |
                  +----->+                        +<-----+
                  |      | H_ABCD=Hash(H_AB|H_CD) |      |
                  |      +------------------------+      |
                  |                                      |
                  |                                      |
                  |                                      |
        +---------+----------+                 +---------+----------+
        | H_AB=Hash(H_A|H_B) |                 | H_AB=Hash(H_A|H_B) |
        +---+----------------+                 +---+----------------+
            ^          ^                           ^          ^
            |          |                           |          |
            |          |                           |          |
+-----------+---+   +--+------------+   +----------+----+   +-+-------------+
| H_A=Hash(TxA) |   | H_B=Hash(TxB) |   | H_C=Hash(TxC) |   | H_D=Hash(TxD) |
+---------------+   +---------------+   +---------------+   +---------------+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

上图展示了一个具有 4 个交易记录的默克尔树根哈希值的计算过程。首先以这 4 个交易作为叶子结点构造一棵完全二叉树,然后通过哈希值的计算,将这棵二叉树转化为默克尔树。

首先对 4 个交易记录:TxA~TxD,分别计算各自的哈希值 H_A~H_C,然后计算两个中间节点的哈希值 H_AB=Hash(H_A|H_B)H_CD=Hash(H_C|H_D),最后计算出根节点的哈希值 H_ABCD=Hash(H_AB|H_CD)

                     +---------+           +---------+          +---------+
                     | PoW 信息 |           | PoW 信息 |          | PoW 信息 |
                     | 父区块哈希+---------->+ 父区块哈希+--------->+ 父区块哈希|
                  +---+默克尔树根|           | 默克尔树根|          | 默克尔树根|
                  |  +---------+           +----+----+          +----+----+
                  |                             |                    |
                  |                             v                    v
+-----------------|------------------+      +---+--+              +--+---+
|               +-v---+        数据块 |      | 数据块|              | 数据块|
|         +---->+ H() +<---+         |      +------+              +------+
|         |     +-----+    |         |
|         |                |         |
|      +--+--+          +--+--+      |
|    +>+ H() +<+      +-> H() +<+    |
|    | +-----+ |      | +-----+ |    |
|    |         |      |         |    |
| +--+--+  +---+-+  +-+---+  +--+--+ |
| | Txn |  | Txn |  | Txn |  | Txn | |
| +-----+  +-----+  +-----+  +-----+ |
+------------------------------------+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

而构造出来的简化区块链结构如上图所示。可以看到:所有在给定时间范围需要记录的交易信息被构造成一棵默克尔树,区块包含了指向这个默克尔树的哈希指针,关联了与该区块相关的交易数据,同时区块也包含了指向前一区块的哈希指针,使得记录了不同交易的单个区块被关联起来,形成区块链。

# 2. 挖矿难度

难度值是比特币节点打包区块时的重要参考指标,它决定了节点大约需要经过多少次哈希运算才能产生一个合法区块。比特币区块大约每 10 分钟生成一个。如果要在不同的全网算力条件下,新区块的产生都基本保持这个速率,难度值必须根据全网算力的变化进行调整。简单地说,难度值被设定在无论节点计算能力如何,新区块产生速率都保持在每 10 分钟一个。

难度的调整是在每个完整节点独立自动发生的。每 2016 个区块,所有节点都会按统一的公式自动调整难度。这个公式是由最新 2016 个区块的花费时长与期望时长(期望时长为 20160 分钟即两周,是按每 10 分钟一个区块的产生速率计算出的总时长)比较得出的。根据实际时长与期望时长的比值,进行相应调整(或变难或变易)。也就是说,如果区块产生的速率比 10 分钟快则增加难度,比 10 分钟慢则降低难度。

这个公式可以总结为:新难度值 = 旧难度值 ×(过去 2016 个区块花费时长/20160 分钟)

工作量证明需要有一个目标值。比特币工作量证明的目标值计算公式:目标值=最大目标值/难度值,其中最大目标值为一个恒定值:0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

目标值的大小与难度值成反比。比特币工作量证明的达成就是矿工计算出来的区块哈希值必须小于目标值。

# 3. PoW 过程

比特币 PoW 的过程可以简单理解成就是将不同的 nonce 值作为输入,尝试进行 SHA256 哈希运算,找出满足给定数量前导 0 的哈希值的过程。而所要求前导 0 的个数越多,代表难度越大。比特币节点求解工作量证明问题的步骤大致归纳如下:

  1. 生成铸币交易,并与其他所有准备打包进区块的交易组成交易列表,通过默克尔树算法计算默克尔树根哈希
  2. 把默克尔树根哈希及其他相关字段组装成区块头,将区块头的 80 字节数据作为工作量证明的输入;
  3. 不停地变更区块头的随机数,即 nonce 的数值,并对每次变更后的区块头做双重 SHA256 运算(即 SHA256(SHA256(Block_Header))),将结果值与当前网络的目标值做对比,如果小于目标值,则解题成功,工作量证明完成。

比特币的工作量证明,就是俗称“挖矿”所做的主要工作。

# 4. PoW 能否解决拜占庭将军问题

关于比特币 PoW 共识机制能否解决拜占庭将军问题一直在业界有争议。2015 年,Juan Garay 对比特币的 PoW 共识算法进行了正式的分析,得出的结论是比特币的 PoW 共识算法是一种概率性的拜占庭协议(Probabilistic BA)。Garay 对比特币共识协议的两个重要属性分析如下。

  1. 一致性(Agreement)
    • 在不诚实节点总算力小于 50% 的情况下,同时每轮同步区块生成的几率很少的情况下,诚实的节点具有相同区块的概率很高。用数学的严格语言说应该是:当任意两个诚实节点的本地链条截取 K 个节点,两条剩下的链条头区块不相同的概率随着 K 的增加呈指数型递减。
  2. 正确性(Validity)
    • 大多数的区块必须由诚实节点提供。严格来说,当不诚实算力非常小的时候,才能使大多数区块由诚实节点提供。

由此可见,不诚实的算力小于网络总算力的 50% 时,同时挖矿难度比较高。在大约 10 分钟出一个区块情况下,比特币网络达到一致性的概率会随确认区块的数目增多而呈指数型增加。但当不诚实算力具备一定规模,甚至不用接近 50% 的时候,比特币的共识算法并不能保证正确性。也就是,不能保证大多数的区块由诚实节点提供。

因此,比特币的共识算法不适合于私有链和联盟链,原因有二

  • 它是一个最终一致性共识算法,不是一个强一致性共识算法
  • 共识效率低。

提高共识效率又会牺牲共识协议的安全性。另外,比特币通过巧妙的矿工奖励机制来提升网络安全性。矿工挖矿获得比特币奖励以及记账所得的交易费用使得矿工更希望维护网络的正常运行,而任何破坏网络的非诚信行为都会损害矿工自身的利益。因此,即使有些比特币矿池具备强大的算力,它们都没有作恶的动机,反而有动力维护比特币的正常运行,因为这和它们的切实利益相关。

PoW 机制存在明显的弊端。一方面,PoW 的前提是节点和算力是均匀分布的。因为通过 CPU 的计算能力来进行投票,拥有钱包(节点)数和算力值应该是大致匹配的,然而随着人们将 CPU 挖矿逐渐升级到 GPU、FPGA,直至 ASIC 矿机挖矿,节点数和算力值也渐渐失配。另一方面,PoW 太浪费了。比特币网络每秒可完成数百万亿次 SHA256 计算,但这些计算除了使恶意攻击者不能轻易地伪装成几百万个节点和打垮比特币网络,并没有更多实际的科学价值。当然,相对于允许世界上任何一个人在瞬间就能通过去中心化和半匿名的全球货币网络给其他人几乎没有手续费地转账所带来的巨大好处,它的浪费也许只算是很小的代价。

有鉴于此,人们提出了权益证明 PoS。

# 5. PoS

PoS 类似于财产储存在银行。这种模式会根据你持有数字货币的量和时间,分配给你相应的利息。

简单来说,就是一个根据你持有货币的量和时间,给你发利息的一个制度。股权证明 PoS 模式有一个名词叫币龄,每个币每天产生 1 币龄。比如你持有 100 个币,总共持有了 30 天。那么此时你的币龄就为 3000。如果你发现了一个 PoS 区块,币龄就会被清空为 0。每被清空 365 币龄,你将会从区块获得 0.05 个币的利息(假定利息可理解为年利率 5%)。在这个案例中,利息 = 3000 * 5% / 365 = 0.41 个币。这下就很有意思了,持币有利息。

点点币(Peercoin)是首先采用权益证明的货币。它在 SHA256 哈希运算的难度方面引入了币龄的概念,使得难度与交易输入的币龄成反比。在点点币中,币龄被定义为币的数量与币所拥有天数的乘积,这使得币龄能够反映交易时刻用户所拥有的货币数量。实际上点点币的权益证明机制结合了随机化与币龄的概念,至少未使用 30 天的币可以参与竞争下一区块,越久和越大的币集有更大的可能去签名下一区块。

然而,一旦币的权益被用于签名区块,其币龄将清为零,这样必须等待至少 30 天才能签署另一区块。同时为防止非常老或非常大的权益控制区块链,寻找下一区块的最大概率在 90 天后达到最大值,这一过程保护了网络,并随着时间逐渐生成新币而无需消耗大量的计算能力。点点币的开发者声称这将使得恶意攻击变得困难,因为没有中心化的挖矿池需求,而且购买半数以上的币的开销似乎超过获得 51% 工作量证明的哈希计算能力。

权益证明必须采用某种方法定义任意区块链的下一合法区块,依据账户结余来选择会导致中心化。例如单个首富成员可能会拥有长久的优势。为此,人们还设计了其他不同的方法来选择下一合法区块。

PoS 机制虽然考虑到了 PoW 的不足,但依据权益结余来选择,会导致首富账户的权力更大,有可能支配记账权。股份授权证明机制(Delegated Proof of Stake,DPoS)的出现正是基于解决 PoW 机制和 PoS 机制的这类不足。

# 6. DPoS

比特股(Bitshare)是一类采用 DPoS 机制的密码货币,它期望通过引入一个技术民主层来减少中心化的负面影响。

比特股的 DPoS 机制,中文名叫做委托权益证明机制。其原理是让每一个持有比特股的人进行投票,由此产生 101 位代表,我们可以将其理解为 101 个超级节点或者矿池,而这 101 个超级节点的权利是完全相等的。从某种角度来看,DPOS 有点像是议会制度或人民代表大会制度。如果代表不能履行他们的职责(轮到他们时,没能生成区块),他们会被除名,网络会选出新的超级节点来取代他们。DPOS 的出现最主要还是因为矿机的产生,大量的算力在不了解也不关心比特币的人身上,类似演唱会的黄牛,大量囤票而丝毫不关心演唱会的内容。

比特股引入了见证人这个概念。见证人可以生成区块,每一个持有比特股的人都可以投票选举见证人。得到总同意票数中的前 N 个(N 通常定义为 101)候选者可以当选为见证人,当选见证人的个数(N)需满足:至少一半的参与投票者相信 N 已经充分地去中心化。

见证人的候选名单每个维护周期(1 天)更新一次。见证人然后随机排列,每个见证人按序有 2 秒的权限时间生成区块。若见证人在给定的时间片不能生成区块,区块生成权限交给下一个时间片对应的见证人。DPoS 的这种设计使得区块的生成更为快速,也更加节能。

DPoS 充分利用了持股人的投票,以公平民主的方式达成共识。他们投票选出的 N 个见证人,可以视为 N 个矿池,而这 N 个矿池彼此的权利是完全相等的。持股人可以随时通过投票更换这些见证人(矿池),只要他们提供的算力不稳定,计算机宕机,或者试图利用手中的权力作恶。

比特股还设计了另外一类竞选--代表竞选。选出的代表拥有提出改变网络参数的特权,包括交易费用、区块大小、见证人费用和区块区间。若大多数代表同意所提出的改变,持股人有两周的审查期,这期间可以罢免代表并废止所提出的改变。这一设计确保代表技术上没有直接修改参数的权利以及所有网络参数的改变最终需得到持股人的同意。

# 7. Ripple 共识算法。

Ripple(瑞波)是一种基于互联网的开源支付协议,可以实现去中心化的货币兑换、支付与清算功能。在 Ripple 的网络中,交易由客户端(应用)发起,经过追踪节点(tracking node)或验证节点(validating node)把交易广播到整个网络中。追踪节点的主要功能是分发交易信息以及响应客户端的账本请求。验证节点除包含追踪节点的所有功能外,还能够通过共识协议,在账本中增加新的账本实例数据。

Ripple 的共识达成发生在验证节点之间。每个验证节点都预先配置了一份可信任节点名单,称为 UNL(Unique Node List)。名单上的节点可对交易达成进行投票。每隔几秒,Ripple 网络将进行如下共识过程:

  1. 每个验证节点会不断收到从网络发送过来的交易,通过与本地账本数据验证后,不合法的交易直接丢弃,合法的交易将汇总成交易候选集(candidate set)。交易候选集里面还包括之前共识过程无法确认而遗留下来的交易。
  2. 每个验证节点把自己的交易候选集作为提案发送给其他验证节点。
  3. 验证节点在收到其他节点发来的提案后,如果不是来自 UNL 的节点,则忽略该提案;如果是来自 UNL 的节点,就会对比提案中的交易和本地的交易候选集,如果有相同的交易,该交易就获得一票。在一定时间内,当交易获得超过 50% 的票数时,则该交易进入下一轮。没有超过 50% 的交易,将留待下一次共识过程去确认。
  4. 验证节点把超过 50% 票数的交易作为提案发给其他节点,同时提高所需票数的阈值到 60%,重复步骤3 和 4,直到阈值达到 80%。
  5. 验证节点把经过 80% UNL 节点确认的交易正式写入本地的账本,称为最后关闭账本(Last Closed Ledger),即账本最后(最新)的状态。

Ripple 共识过程节点交互示意图

+-----+             +-----------------+          +-----------------+         +-----------------+
| App +------------>+  Tracking Node  +<-------->+ Validating Node +<------->+ Validating Node |
+--+--+             +-------+---------+          +------+----------+         +--------+--------+
   |                        ^                           ^                             ^
   |                        |                           |                             |
   |                        |                           |                             v
   |                        |                           |                     +-------+---------+
   |                        |     +---------------------+-------------------->+ Validating Node |
   |                        |     |                                           +-------+---------+
   |                        |     |                                                   ^
   |                        |     |                                                   |
   |                        v     v                                                   v
   |                +-------+-----+---+          +-----------------+          +-------+---------+         +-----+
   +--------------->+ Validating Node +<-------->+ Validating Node +<-------->+  Tracking Node  |-------->| App |
                    +-----------------+          +-----------------+          +-----------------+         +-----+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Ripple共识算法流程

Ripple 的共识算法中,参与投票节点的身份是事先知道的,因此,算法的效率比 PoW 等匿名共识算法要高效,交易的确认时间只需几秒钟。当然,这点也决定了该共识算法只适合于权限链(Permissioned chain)的场景。Ripple 共识算法的拜占庭容错(BFT)能力为 (n-1)/5,即可以容忍整个网络中 20% 的节点出现拜占庭错误而不影响正确的共识。

# 小结

以上主要是目前主流的共识算法。但说起哪种共识机制更好或更具替代作用? 我认为 DPOS 来单独替代 POW,POS 或者 POW+POS 不太可能,毕竟存在即合理。每种算法都在特定的时间段、场景下具有各自的意义,无论是技术上,还是业务上。如果跳出技术者的角度,更多结合政治与经济的思考方式在里面,或许还会不断出现更多的共识机制。

对于算法的选择,一句话总结如下:

“在区块链网络中,由于应用场景的不同,所设计的目标各异,不同的区块链系统采用了不同的共识算法。一般来说,在私有链和联盟链情况下,对一致性、正确性有很强的要求。一般来说要采用强一致性的共识算法。而在公有链情况下,对一致性和正确性通常没法做到百分之百,通常采用最终一致性(Eventual Consistency)的共识算法。”

通俗点就是:共识算法的选择与应用场景高度相关,可信环境使用 Paxos 或者 Raft,带许可的联盟可使用 PBFT,非许可链可以是 PoW,PoS,Ripple 共识等,根据对手方信任度分级,自由选择共识机制。

# 参考文献