Service Properties
- 算法的适用范围
Our algorithm can be used to implement any deterministic replicated service with a state and some operations.
- deterministic :确定性服务,即相同的参数执行相同操作,每次得到的结果都是一致的,不存在随机性(the execution of an operation in a given state and with a given set of arguments must always produce the same result)
- replicated : 多副本
- a state and some operations : 服务有相同的初始状态及相同的操作,保证当按相同顺序执行完相同的操作,各个副本的即时状态是完全一致的 (must start in the same state)
- 在异步系统中要容忍个恶意节点,则至少需要个节点才能提供safety and liveness(Asynchronous Consensus and Broadcast Protocols这篇论文中证明)。
- safety : 副本服务满足线性一致性,像在中心化服务中同时原子性的执行操作。不受恶意client数量影响,恶意client的操作可以通过被正常client感知,利用access control来限制。
- liveness : 利用同步提供liveness(论文证明)。只要满足个节点,可以保证client持续重传,并且到达destination的传输时间没有无限的增长情况下,client最终会收到回复。
- 算法不提供隐私容错性,即隐私信息会泄漏给恶意节点
The algorithm does not address the problem of fault- tolerant privacy: a faulty replica may leak information to an attacker.
The Algorithm
0. 术语
- view :类似raft的term,view number在变更时+1
- primary :在一个view中只有单独一个节点成为primary,选择的方式为 (为节点的id,是当前view number,)
- backup : 一个view内除primary外的其他所有节点,与primary都称为replica
算法的粗略过程:
- A client sends a request to invoke a service operation to the primary
- The primary multicasts the request to the backups
- Replicas execute the request and send a reply to the client
- The client waits for replies from different replicas with the same result; this is the result of the operation.
1. The Client
client 点对点发送到primary,然后由primary广播到所有backup。是操作,是时间(如本地机器时间),用来保证exactly-once语义,表示对消息签名(下同)。replica 相应的回复。是当前view number(存在view-change情况),是执行结果,然后各自对消息签名。client可以根据回复的view number跟踪当前view,相应的得知当前primary。client接收到个相同结果的有效回复(校验签名),则结果有效,完成。
如果client在有效时间内没法收到个有效结果,则广播request到所有的replica。如果该request已经被处理过,replica就简单的重新发送reply到client。如果没有,backup会将改request重定向到primary。如果primary没有再次将该request广播,则会被认为是异常节点,最终触犯view change。
2. Normal-Case Operation
replica记录的状态
The state of each replica includes the state of the service, a message log containing messages the replica has accepted, and an integer denoting the replica’s current view.
整个流程分为三阶段,pre-prepare, prepare, commit (pre-prepare, prepare用于保持同一个view内request的有序性,prepare, commit用于保证跨view的reqeust的有序性)。
-
pre-prepare 阶段
primary广播到所有的backup。是primary分配给该消息的序号,全局递增,是client的请求消息,是消息的digest摘要。然后签名。如果满足以下条件,则backup会接受该pre-prepare请求,并且进入prepare阶段(The last condition prevents a faulty primary from exhausting the space of sequence numbers by selecting a very large one.):
- the signatures in the request and the pre-prepare message are correct and is the digest for ;
- it is in view ;
- it has not accepted a pre-prepare message for view and sequence number containing a different digest;
- the sequence number in the pre-prepare message is between a low water mark, , and a high water mark, .
-
prepare 阶段
backup广播消息到所有replica(包含primary和backup),为该节点id。并且将和消息保存到本地log。replica接收到prepare消息后,校验签名正确,是当前view,在水位和之间,摘要和本地保存的消息里的相等。当replica 收到个这样的消息后,就达到了 状态,进入commit阶段。 -
commit 阶段
replica 广播消息到所有replica。replica收到后校验通过后,写入log。如果replica 达到了 状态,并且收到个(包括自身的commit消息)校验通过的commit(,,相同且与log里记录的prepare吻合),则达到状态,并且其当前状态是消息序号小于的之前所有消息按序执行后的最新最全结果,则该replica执行里指定的操作。操作的结果返回到client。The commit phase ensures the following invariant: if is true for some non-faulty then is true. This invariant and the view-change protocol described in Section 4.4 ensure that non-faulty replicas agree on the sequence numbers of requests that commit locally even if they commit in different views at each replica. Furthermore, it ensures that any request that commits locally at a non-faulty replica will commit at or more non-faulty replicas eventually.
这里指的是,达到只能保证至少个正常节点被最终commit,而并不是所有正常节点都执行。这就造成正常节点间都状态不一致,需要最终同步状态。或者使用上文提到Section 4.4里提到都view-change场景下重走commit流程最终达成状态一致。[引用2:Fabric0.6里的最终状态同步方案,3:介绍实时全正常节点同步的方案]
3. Garbage Collection
replica周期性对其本地状态生成快照,称为checkpoint。当replica 生成checkpoint同时,广播消息到其他所有replica,其中,是生成该状态执行的最后的请求消息的序号,是该状态的摘要digest。replica接收checkpoint消息,并且记录到log里,当收到个相同的checkpoint消息(,)相同,则该checkpoint变成stable checkpoint。此时,小于等于的消息都会被丢弃,同时,之前的stable checkpoint也被丢弃。
实际上,一个replica会同时持有这样几个状态副本:一个stable checkpoint,若干个未达到稳定状态的checkpoint(生成后暂未收集到个该状态的checkpoint消息),以及当前状态。stable checkpoint的最后的消息序号就是低水位,高水位(where is big enough so that replicas do not stall waiting for a checkpoint to become stable).
4. View Changes
client在有效时间内没法收到个有效结果,则广播request到所有的replica。backup收到后,若该请求从未执行,则转发给primary,并且启动timer,在timer到期前若没有收到primary发出的pre-prepare,则判断该primary异常,发出view-change消息。backup , ,是新的view number,是该backup的stable checkpoint 的最后消息序号,是证明的个checkpoint消息,是的集合,是达到 状态的消息(包含pre-prepare消息和个来自其他replica的matching消息,其中消息的序号大于)。
的primary(通过计算)收到个有效view-change消息后,广播,是收到的和primary自身的view-change消息的集合,是pre-prepare消息的集合。计算如下,比较复杂
- The primary determines the sequence number of the latest stable checkpoint in and the highest sequence number in a prepare message in .
- The primary creates a new pre-prepare message for view for each sequence number between and .
There are two cases: (1) there is at least one set in the component of some view-change message in with sequence number , or (2) there is no such set.
In the first case, the primary creates a new message where is the request digest in the pre-prepare message for sequence number with the highest view number in .
In the second case, it creates a new pre-prepare message where is the digest of a special null request; a null request goes through the protocol like other requests, but its execution is a no-op. (Paxos [18] used a similar technique to fill in gaps.)
也就是说,primary从latest stable checkpoint重放之后所有达到状态的消息(),重发pre-prepare。replica会对这些消息重走流程,prepare和commit阶段,但是不再重新执行请求,因为在上一个view内,对这些达到状态的消息已经执行过。
同时,replica记录收到的所有replica的view-change里最新的(最大)stable checkpoint记录到log里,并且对缺失的消息和最新的stable checkpoint(自身的stable checkpoint不是全局最新的stable checkpoint情况下)从其他的replica同步。
5. Correctness(重点)
- Safety
if is true then is false for any non-faulty replica (including ) and any such that
-
这是因为,到达意味着个replica选择发送,假设异常节点个数为, 则至少有个正常replica发送,同时也意味着至少有个replica发送。由于,说明这两部分正常节点有重叠,则至少有一个正常节点发送两个不同消息,矛盾。
The view-change protocol ensures that non-faulty replicas also agree on the sequence number of requests that commit locally in different views at different replicas. is true if and only if is true for all in some set of non-faulty replicas. if is true for some non-faulty then is true.
-
这是因为,到达状态,则意味着至少收到个正常replica的commit消息,也就是至少有个正常节点到达状态。而正常节点只有在接收到new-view消息才会从view 进入view ,而new-view消息里包含个view-change消息。与上面推导方式相似,则这两个集合必定有一个交集包含正常节点k。若然已经包含在的stable checkpont内,则最终被同步到所有正常节点;若然没有,则包含的view-change消息内,然后在view 中被重新执行三阶段流程,最终也达到提交一致。
- Liveness
- replica广播view-change进入,等待个view-change消息后启动计时器。如果在到时前没有收到new-view(新的primary是异常节点),则进入,计时器时间扩大成,这是为了避免view改变的过快。
- 在定时器过期前,如果收到个view-change消息,并且比自身的view number大,则发送这些大view number中最小的一个
- 个view-change消息才能触发view变更,而且primary的轮流选择方案也限定连续最多f轮选择就能选举到正常的primary。
Optimization
1. Reducing Communication
- 避免传输large result
A client request designates a replica to send the result; all other replicas send replies containing just the digest of the result.(指定单个replca回复完整result,其他回复该result的摘要用以校验)。 If the client does not receive a correct result from the designated replica, it retransmits the request as usual, requesting all replicas to send full replies.
- 减少流程步骤,replica提前返回reply。当replica收到足够prepare消息,达到了 状态,将要进入commit阶段。这时,如果小于该消息序号的之前的消息都已经执行生成了当前状态,则replica执行该请求,并且直接返回到client,同时继续后续的commit阶段。
- The client waits for matching tentative replies. If it receives this many, the request is guaranteed to commit eventually. Otherwise, the client retransmits the request and waits for non-tentative replies.
- A request that has executed tentatively may abort if there is a view change and it is replaced by a null request. In this case the replica reverts its state to the last stable checkpoint in the new-view message or to its last checkpointed state (depending on which one has the higher sequence number).
- 优化read-only操作。对于read-only请求,client直接发送到所有replica,replica直接返回结果到client。client收集个相同结果的回复,否则按照正常流程重新发起请求。
They(replica) send the reply only after all requests reflected in the tentative state have committed; this is necessary to prevent the client from observing uncommitted state.
题外话
所有的replica间需要全联接并且多次交互通信,系统中这些消息随着节点数指数增长。单次请求的最少消息数为.
私货
- 为什么需要至少个节点?
首先最后达成共识的正常节点数需要大于,否则这f个异常节点就可以达成错误的共识。即至少需要个正常节点达成共识。而达成共识时,有可能给最后共识投票的包含这f个异常节点,所以需要至少个节点投票达成共识,这样可以保证其中至少含有个正常节点。(例如P为异常节点向不同的正常节点提议不同的值,这也是造成上文提到个节点最后达成共识时只能保证多于个)。而最后的共识只能有一个,即达成共识的正常节点应该超过所有正常节点数的过半,否则个异常节点可以重复投票导致最后正常节点分裂成两组以上各自达成共识。因此,实际上最后的阈值,这样就保证了correctness正确性。接下来考虑liveness可用性。因为异常节点有可能不参与投票(P为正常节点),需要保证正常节点总数应该大于阈值,即。
References
[1] M. Castro and B. Liskov. Practical Byzantine Fault Tolerance. Proceedings of the Third Symposium on Operating Systems Design and Implementation, New Orleans, USA, February 1999
[2] 区块链PBFT共识算法节点主动恢复设计与实现
[3] 拜占庭将军问题深入探讨