[区块链学习专栏]--共识算法

zhangtuo2018 2022-01-14 15:49:38 阅读数:103

学习 算法 区块 共识 专栏

共识算法

1 共识算法简介

共识算法是指在分布式场景中,多个节点为了达成相同的数据状态而运行的一种分布式算法。 在分布式场景中,可能出现网络丢包、时钟漂移、节点宕机、节点作恶等等故障情况,共识算法需要能够容忍这些错误,保证多个节点取得相同的数据状态。

根据可容忍的故障类型的不同,可以将共识算法分为两类:

  • 容忍宕机错误类算法(crash fault tolerant consensus algorithm),可以容忍网络丢包、时钟漂移、部分节点宕机这种节点为良性的错误。常见算法有 Paxos、Raft。
  • 容忍拜占庭错误类算法(byzantine fault tolerant consensus algorithm),可以容忍部分节点任意类型错误,包括节点作恶的情况。常见算法有 PBFT、PoW、PoS等。

根据使用场景的不同,又可将共识算法分为公链共识、联盟链共识两类。

1.1. 公链共识

公链的特点是节点数量多且节点分布分散,主要使用的共识算法有PoW和PoS,这两种共识的优点是可以支持的节点数量多,缺点是TPS较低和交易确认时间长。

1.2. 联盟链共识

联盟链的特点是节点之间网络较为稳定且节点有准入要求,根据需要容忍的错误类型可以选择Raft和PBFT类算法,这类算法的优点是TPS较高且交易可以在毫秒级确认,缺点是支持的节点数量有限,通常不多于100个节点。

2 联盟链共识算法

2.1 Raft

参考文献:

2.1.1. 算法简介

Raft算法是目前使用最广泛的非拜占庭容错类共识算法。 Raft算法主要依靠 投票机制日志复制机制来实现节点间的共识。节点通过投票选出一个leader,由leader负责处理所有请求,再将请求以日志的方式复制到其他节点。

Raft保证了在一个由N个节点构成的系统中有(N+1)/2(向上取整)个节点正常工作的情况下的系统的一致性,比如在一个5个节点的系统中允许2个节点出现非拜占庭错误,如节点宕机网络分区消息延时。Raft相比于Paxos更容易理解,且被证明可以提供与Paxos相同的容错性以及性能,其详细介绍可见官网动态演示

2.1.2. 算法用途

  1. 不考虑恶意节点的多节点环境;
  2. 需要支持高TPS的环境。

2.1.3. 名词解释

2.1.3.1 节点类型

在Raft算法中,每个网络节点只能如下三种身份之一:Leader、Follower以及Candidate,其中:

  • Leader:主要负责与外界交互,由Follower节点选举而来,在每一次共识过程中有且仅有一个Leader节点,由Leader全权负责从交易池中取出交易、打包交易组成区块并将区块上链;
  • Follower:以Leader节点为准进行同步,并在Leader节点失效时举行选举以选出新的Leader节点;
  • Candidate:Follower节点在竞选Leader时拥有的临时身份。
2.1.3.2 任期

Raft算法将时间划分为不定长度的任期Terms,Terms为连续的数字。每个Term以选举开始,如果选举成功,则由当前leader负责出块,如果选举失败,并没有选举出新的单一Leader,则会开启新的Term,重新开始选举。

2.1.3.3 任期

2.1.4. 核心流程

Raft算法的核心流程由两部分组织,Leader选举和日志复制。

2.1.4.1 leader 选举

在这里插入图片描述

Raft共识模块中使用心跳机制来触发Leader选举。当节点启动时,节点自动成为Follower且将Term置0。只要Follower从Leader或者Candidate收到有效的Heartbeat或RequestVote消息,其就会保持在Follower状态,如果Follower在一段时间内(这段时间称为 Election Timeout)没收到上述消息,则它会假设系统当前的Leader已经失活,然后增加自己的Term并转换为Candidiate,开启新一轮的Leader选举流程,流程如下:

  1. Follower增加当前的Term,转换为Candidate;
  2. Candidate将票投给自己,并广播RequestVote到其他节点请求投票;
  3. Candidate节点保持在Candidate状态,直到下面三种情况中的一种发生:
    1. 该节点赢得选举,,即自己获得了超过半数以上的服务器投票,成为 Leader;
    2. 在等待选举期间,Candidate收到了其他节点的Heartbeat,有其他节点获得了半数以上的投票;
    3. 经过Election Timeout后,没有Leader被选出。

Raft算法采用随机定时器的方法来避免节点选票出现平均瓜分的情况以保证大多数时候只会有一个节点超时进入Candidate状态并获得大部分节点的投票成为Leader。

  • 投票流程

节点在收到VoteReq消息后,会根据消息的内容选择不同的响应策略:

  1. VoteReq的Term小于或等于自己的Term

    • 如果节点是Leader,则拒绝该投票请求,Candidate收到此响应后会放弃选举转变为Follower,并增加投票超时;
    • 如果节点不是Leader:
      • 如果VoteReq的Term小于自己的Term,则拒绝该投票请求,如果Candidate收到超过半数的该种响应则表明其已经过时,此时Candidate会放弃选举转变为Follower,并增加投票超时;
      • 如果VoteReq的Term等于自己的Term,则拒绝该投票请求,对于该投票请求不作任何处理。对于每个节点而言,只能按照先到先得的原则投票给一个Candidate,从而保证每轮选举中至多只有一个Candidate被选为Leader。
  2. VoteReq的lastLeaderTerm小于自己的lastLeaderTerm

    每个节点中会有一个lastLeaderTerm字段表示该节点见过的最后一个Leader的Term,lastLeaderTerm仅能由Heartbeat进行更新。如果VoteReq中的lastLeaderTerm小于自己的lastLeaderTerm,表明Leader访问这个Candidate存在问题,如果此时Candidate处于网络孤岛的环境中,会不断向外提起投票请求,因此需要打断它的投票请求,所以此时节点会拒绝该投票请求。

  3. VoteReq的lastBlockNumber小于自己的lastBlockNumber

    每个节点中会有一个lastBlockNumber字段表示节点见到过的最新块的块高。在出块过程中,节点间会进行区块复制(详见3.2节),在区块复制的过程中,可能有部分节点收到了较新的区块数据而部分没有,从而导致不同节点的lastBlockNumber不一致。为了使系统能够达成一致,需要要求节点必须把票投给拥有较新数据的节点,因此在这种情况下节点会拒绝该投票请求。

  4. 节点是第一次投票

    为了避免出现Follower因为网络抖动导致重新发起选举,规定如果节点是第一次投票,直接拒绝该投票请求,同时会将自己的firstVote字段置为该Candidate的节点索引。

  5. 1~4步骤中都没有拒绝投票请求

    同意该投票请求。

  • 心跳超时

    在Leader成为网络孤岛时,Leader可以发出心跳、Follower可以收到心跳但是Leader收不到心跳回应,这种情况下Leader此时已经出现网络异常,但是由于一直可以向外发送心跳包会导致Follower无法切换状态进行选取,系统陷入停滞。为了避免第二种情况发生,模块中设置了心跳超时机制,Leader每次收到心跳回应时会进行相应记录,一旦一段时间后记录没有更新则Leader放弃Leader身份并转换为Follower节点。

2.1.4.2 日志复制

在这里插入图片描述

Raft 协议强依赖 Leader 节点的可用性来确保集群数据的一致性。数据的流向只能从 Leader 节点向 Follower 节点转移。

当 Client 向集群 Leader 节点提交数据后,Leader 节点接收到的数据处于未提交状态(Uncommitted),

接着 Leader 节点会并发向所有 Follower 节点复制数据并等待接收响应,确保至少集群中超过半数节点已接收到数据后再向 Client 确认数据已接收。

一旦向 Client 发出数据接收 Ack 响应后,表明此时数据状态进入已提交(Committed),Leader 节点再向 Follower 节点发通知告知该数据状态已提交。

2.2 PBFT

2.2.1 简介

  • 结论:

    时间复杂度是O(n^2),可解决拜占庭问题。PBFT算法可以容忍小于1/3个无效或者恶意节点。如果系统内有n台机子,那么系统最多能容忍的作恶/故障节点为(n-1)/3个。系统的总节点数为:|R| = 3f + 1。

  • 证明过程

    因为我们知道有f个作恶节点,所以我们必须在n-f个状态复制机的沟通内,就要做出决定。而且我们无法预测这f个作恶节点做了什么(错误消息/不发送),所以我们并不知道,这n-f个里面有几个是作恶节点,我们必须保证正常的节点大于作恶节点数。所以有 n-f-f > f,从而得出了n > 3f。

  • 主节点确定机制:

    主节点通过视图编号以及节点数集合来确定,即:主节点 p = v mod |R|。v:视图编号,|R|节点个数,p:主节点编号。

2.2.2 共识流程

角色划分

  • Client: 客户端节点,负责发送交易请求。
  • Primary: 主节点,负责将交易打包成区块和区块共识,每轮共识过程中有且仅有一个Primary节点。
  • Replica: 副本节点,负责区块共识,每轮共识过程中有多个Replica节点,每个Replica节点的处理过程类似。

其中,Primary和Replica节点都属于共识节点。

基本流程

PBFT 算法的基本流程主要有以下四步:

  1. 客户端发送请求给主节点
  2. 主节点广播请求给其它节点,节点执行PBFT算法的三阶段共识流程。
  3. 节点处理完三阶段流程后,返回消息给客户端。
  4. 客户端收到来自 f+1 个节点的相同消息后,代表共识已经正确完成。
    在这里插入图片描述

核心流程

算法的核心三个阶段分别是 pre-prepare 阶段(预准备阶段),prepare 阶段(准备阶段), commit 阶段(提交阶段)。图中的C代表客户端,0,1,2,3 代表节点的编号,其中0 是主节点primary,打×的3代表可能是故障节点或者是作恶节点,这里表现的行为就是对其它节点的请求无响应。整个过程大致是如下:

首先,客户端向主节点0发起请求<<REQUEST,o,t,c>>其中t是时间戳,o表示操作,c是这个client,主节点收到客户端请求,会向其它节点发送 pre-prepare 消息,其它节点就收到了pre-prepare 消息,就开始了这个核心三阶段共识过程了。

  • Pre-prepare 阶段:副本节点replica收到 pre-prepare 消息<<PRE_PREPARE,v,n,d>,m>后,判断是否接收该消息。其中,v 代表视图编号,n代表消息序号(主节点收到客户端的每个请求都以一个编号来标记)d代表消息摘要m代表原始消息数据。消息里的 v 和 n 在之前收到里的消息是曾经出现过的,但是 d 和 m 却和之前的消息不一致,或者请求编号n不在高低水位之间,这时候就会拒绝请求。拒绝的逻辑就是主节点不会发送两条具有相同的 v 和 n ,但 d 和 m 却不同的消息。

    Replia节点接收到pre-prepare消息,进行以下消息验证:

    • 消息m的签名合法性,并且消息摘要d和消息m相匹配:d=hash(m)
    • 节点当前处于视图v中
    • 节点当前在同一个(view v ,sequence n)上没有其它pre-prepare消息,即不存在另外一个m’和对应的d’ ,d’=hash(m’)
    • h<=n<=H,H和h代表序号n的高低水位。
  • Prepare 阶段:当前节点同意请求后会向其它节点发送 prepare 消息<PREPARE,v,n,d,i>同时将消息记录到log中,其中i用于表示当前节点的身份。同一时刻不是只有一个节点在进行这个过程,可能有 n 个节点也在进行这个过程。因此节点是有可能收到其它节点发送的 prepare 消息的,当前节点i验证这些prepare消息和自己发出的prepare消息的v,n,d三个数据是否都是一致的。验证通过之后,当前节点i将prepared(m,v,n) 设置为true,prepared(m,v,n) 代表共识节点认为在(v,n)中针对消息m的Prepare阶段是否已经完成。在一定时间范围内,如果收到超过 2f 个其他节点的prepare 消息,就代表 prepare 阶段已经完成。最后共识节点i发送commit消息并进入Commit阶段。

  • Commit 阶段:当前节点i接收到2f个来自其他共识节点的commit消息<COMMIT,v,n,d,i>同时将该消息插入log中(算上自己的共有2f+1个),验证这些commit消息和自己发的commit消息的v,n,d三个数据都是一致后,共识节点将committed-local(m,v,n)设置为true,committed-local(m,v,n)代表共识节点确定消息m已经在整个系统中得到至少2f+1个节点的共识,而这保证了至少有f+1个non-faulty节点已经对消息m达成共识。于是节点就会执行请求,写入数据。

处理完毕后,节点会返回消息<<REPLY,v,t,c,i,r>>给客户端,当客户端收集到f+1个消息后,共识完成,这就是PBFT算法的全部流程。

prepare和commit阶段为何都要2f+1个节点反馈确认?(这2f+1并不一定是相同的)
考虑最坏的情况:我们假设收到的有f个是正常节点发过来的,也有f个是恶意节点发过来的,那么,第2f+1个只可能是正常节点发过来的。由此可知,“大多数”正常的节点还是可以让系统工作下去的。所以2f+1这个参数和n>3f+1的要求是逻辑自洽的。

client为何只需要f+1个相同的回复就可确认?

​ 之前我们说,prepare和commit阶段为何都要2f+1个节点反馈,才能确认。client只需要f+1个相同的reply就可以了呢?我们还是来考虑最坏的情况,我们假设这f+1个相同的reply中,有f个都是恶意节点。

所以至少有一个rely是正常节点发出来的,因为在prepare阶段,这个正常的节点已经可以保证prepared(m,v,n,i)为真,所以已经能代表大多数的意见,所以,client只需要f+1个相同的reply就能保证他拿到的是整个系统内“大多数正常节点“的意见,从而达到一致性。

总结: 如果顺利的话,一个节点收到1个pre-prepare消息和2f个和prepare消息进入commit阶段,2f+1个commit消息后可以reply给client,client收到f+1个回复就可以确认提交成功。

2.2.3 垃圾回收

根据前面的算法部分可以发现,我们需要不断地往log中插入消息,在view change时恢复需要用到。于是log很快就会变得很占内存,这时候需要有一种方式清理掉无用的log。当某一request已经被f+1个正常节点执行完毕后,并当view change可以向其他节点证明当前状态的正确性,与该request相关的message就可以删除了。

当request的序号n % C ( 某 一 定 值 ) =0时,产生一个checkpoint,节点i多播消息<<CHECKPOINT,n,d,i>>给其他节点,当节点接收2f+1个消息时,该checkpoint变为stable checkpoint,也就是这2f+1个节点可以证明该状态的正确性,同时可以删除序号≤n的消息相关的log信息和checkpoint信息。

  • checkpoint:当前节点处理的最新请求序号
  • stable checkpoint (稳定检查点):stable checkpoint 就是大部分节点 (2f+1个) 已经共识完成的最大请求序号。
  • 高低水位:低水位就是stable checkpoint的序号n,高水位是stable checkpoint的序号n + K,其中K是定值,一般是C(上面提及到的某一定值)的整数倍。

2.2.4 视图更换(view change)

主节点出错或成为恶意节点时,就需要进行视图更换(view change),也就是选择(轮换法)下一个replica节点作为主节点,视图编号v进行+1操作,共识过程进入下一个view。

在这里插入图片描述

如图所示, view change 会有三个阶段,分别是 view-changeview-change-acknew-view阶段。

  • replica节点认为主节点primary有问题时,会向其它节点发送 view-change 消息<<VIEW−CHANGE,v+1,n,C,P,i>>
    • v:上一个视图编号
    • n:节点i的stable checkpoint的编号
    • C:2f+1个节点的有效checkpoint信息的集合
    • P:节点i中的上一个视图中编号大于n并且达到prepared状态的请求消息的集合
    • i:节点的编号
  • 当前存活的节点编号最小的节点将成为新的主节点。当新的主节点收到 2f 个其它节点的 view-change 消息,则证明有足够多人的节点认为主节点有问题,于是就会向其它节点广播 new-view 消息<<NEW-VIEW,v+1,V,O>>
    • v:上一个视图编号
    • V:新的主节点接收到的有效的视图编号为v+1的view-change消息集合
    • O:pre-prepare消息的集合。假设 O 集合里消息的编号范围:(min~max),则 Min 为 V 集合最小的 stable checkpoint , Max 为 V 集合中最大序号的 prepare 消息。最后一步执行 O 集合里的 pre-preapare 消息,每条消息会有两种情况: 如果 max-min>0,则产生消息 <<pre-prepare,v+1,n,d>> ;如果 max-min=0,则产生消息 <<pre-prepare,v+1,n,d(null)>>

注意:replica节点不会发起 new-view 事件。对于主节点,发送 new-view 消息后会继续执行上个视图未处理完的请求,从 pre-prepare 阶段开始。其它节点验证 new-view 消息通过后,就会处理主节点发来的 pre-prepare 消息,这时执行的过程就是前面描述的PBFT过程。到这时,正式进入 v+1 (视图编号加1)的时代了。

  • raft和pbft对比
    在这里插入图片描述

3 公链共识算法

3.1 POW

3.1.1 名称解释

  • 区块头结构

    区块头的大小为80字节,由4字节的版本号、32字节的上一个区块的哈希值、32字节的Merkle根哈希值、4字节的时间戳(当前时间)、4字节的目标值、4字节的随机数组成。

在这里插入图片描述

  • 难度值

    比特币的区块大约每10分钟生成一个,如果要在不同的全网算力条件下,新区块的产生都基本保持这个速率,难度值必须根据全网算力的变化进行调整。简单地说,难度值被设定在无论节点计算能力如何,新区块产生速率都保持在每10分钟一个。

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

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

  • 目标值

    比特币工作量证明的目标值(Target)的计算公式:目标值=最大目标值/难度值

    其中最大目标值为一个恒定值,难度为1时的目标值,即2^224:0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

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

3.1.2 共识过程

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

  • 节点收集上一个区块产生后全网待确认的交易,将符合条件的交易记入交易内存池,然后更新并计算内存池中交易的Merkle根的值,并将其写入区块头部;
  • 随机数nonce在0到232之间取值,对区块头部信息进行哈希计算,当哈希值小于或等于目标值时,打包并广播该区块,待其他节点验证后完成记账;
  • 一定时间内如果无法计算出符合要求的哈希值,则重复步骤2。如果计算过程中有其他节点完成了计算,则从步骤1重新开始。

在这里插入图片描述

3.2 POS

PoS(PowofStake)即股权证明,是为解决 PoW 算法 大量浪费资源问题而提出的一种替代算法,该算法与 PoW 算 法的最大不同点在于区块的记账权由权益最高的节点获得, 而不是算力最高的节点.

共识过程中节点通过消耗的币龄来获取记账权,节点消耗的币龄越多,获得记账权的机会越 大.算法设定的主链原则为:消耗币龄最多的链为系统中正确有效的链.点点币中 PoS算法合格区块的判定公式为:

ProofHash<Target×CoinAge

其中,ProofHash对应一组数据的哈希,此处省略;CoinAge 为币龄(coin*age);Target为当前目标值,同 PoW 一样,与难度成反比.

Target=PrevTarget×(1007×10×60+2×Tgap )/1009× 10×60

其中,PrevTarget为上一个区块的目标值;2×Tgap 为前两个 区块的间隔时间.

当前两个区块时间间隔大于10 min时,当前目标值相比 于上一个区块目标值会提高,即当前区块难度会降低;

反之, 当前两个区块时间间隔小于10 min,当前目标值相比于上一 个区块目标值会降低,即当前区块难度会提高.

PoS 将算力竞争转化为权益竞争, 不仅节约算 力, 权益的引入也能够防止节点发动恶意攻击; 同 时促使所有节点有责任维护区块链的安全稳定运行 以保障自身权益; PoS 虽然降低了算力资源的消耗, 但是没有解决中心化程度增强的问题, 新区块的生 成趋向于权益高的节点。PoS 中需要拥有超全网一半 的权益发动 51%攻击, 但其成本高于拥有超全网一 半的算力[70], 另外创建区块需要消耗权益, 使得 PoS 持续进行 51%攻击的难度增加, 一定程度上降低了 安全风险[31]

3.3 DPOS

DPoS(Delegated ProofofStake)即 股 权 授 权 证 明 机 制,它是 PoS的一种衍生算法,算法的思想是系统中持有权益的节点投票选举出一部分代表,再由这些代表轮流获取区块记账权,某种程度上类似于股份制公司的“董事会”。

DPoS算法将节点分为两部分:参与投票的选民被选举出的代表.

每个节点都可将自己持有的权益转化为选票投给 自己信任的节点,所持的权益越多,选票所占的权重也就越 高,获得票数最多的n 个节点当选为见证人(Witness)即 代 表.见证人的名单每经过一个固定周期都将重新选举更换一 次.见证人直接参与区块的共识和验证过程,在一个规定的 时间内它们随机排列并轮流对交易进行打包,生成新区块连 接到最长链上.每生成一个区块,见证人会获得 m%的交易 手续费,m 值由见证人们提议设定并由选民们投票决议.当 然,参与见证人的竞选也需缴纳大量的保证金,这样,见证人在系统中投入的资金最大,为保证自身的利益积极地维护系 统安全.

在这里插入图片描述

参考文献

  1. 万字长文解读区块链七类共识算法
  2. Bcos Raft
    当 然,参与见证人的竞选也需缴纳大量的保证金,这样,见证人在系统中投入的资金最大,为保证自身的利益积极地维护系 统安全.

参考文献

  1. 万字长文解读区块链七类共识算法
  2. Bcos Raft
  3. 共识算法系列之一:raft和pbft算法
版权声明:本文为[zhangtuo2018]所创,转载请带上原文链接,感谢。 https://blog.csdn.net/zhangtuo93/article/details/122338476