PBFT协议的理解 PBFT是一种分布式节点间的状态复制算法,在总节点数为n的情况下,它能容错不超过 ? n ? 1 3 ? \lfloor \frac {n-1}{3} \rfloor ?3n?1??的拜占庭节点,使得大多数的状态复制机(replica)保持一致的状态如果需要保持分布式节点之间的系统状态的一致性,只需要做到如下两点即可:
- 系统中节点就client的request分配一个唯一的编号,并且对于系统中任意两个非故障节点,同一个request对应相同并且唯一的编号.
- 系统中的节点有序的执行request,保证任意两个非故障节点的系统状态一致.
系统模型
- 网络是异步的分布式网络,网络中节点之间会出现信息发送失败, 延迟,重复发送,甚至无序的情况.
- 系统中可能有一个强力的恶意节点联合其他节点,故意导致发送信息出现延迟或者故意延迟正确的节点发送的信息,但是恶意节点不能无限延迟.
- 恶意节点无法伪造诚实节点的签名,而恶意节点之间可能串通,可以相互伪造签名.
- 恶意节点无法通过一条消息的摘要计算出消息体.
- 恶意节点无法针对一个消息m,找到另外一个对应的消息m’使得它们的摘要相同.(摘要是对消息求hash).
- 系统中总结点数为n的时候, pbft协议最多容忍 ? n ? 1 3 ? \lfloor \frac{n-1}{3}\rfloor ?3n?1??的拜占庭节点.
- Replica, 状态复制机,所有的replica执行pbft协议,保持系统状态一致性.
- Primary, 在pbft协议中,每个primary都有一个任期,称之为view, 在一个view中,只有一个replica被称之为primary,可以理解为leader.
- Backup,除了primary意外的replica都称之为backup.
- client, 向primary发送request以请求服务.
在PBFT协议中,主要有三个身份,client,primary和backup, 所有状态复制机中有一个主节点,称之为primary, 其他状态复制机称之为backup. client向primary发出操作请求,primary把client的所有请求发送给所有backup, primary和其他backup通过按照PBFT协议对操作进行响应,然后将执行结果返回给client. pbft论文中默认client在收到前一个request的执行结果之前不会发送下一个request.每一个primary在其任期内对应一个view, 下一任primary上任时,view自动加1.一个view中只有一个primary.
整个算法的整体流程如下:
- client向primary发送一个request.
- primary向其他备份节点广播request.
- 所有节点执行request并且向client返回执行结果
- client从f+1个不同的节点处得到相同的执行结果,则认为执行结果可靠并且接收这个结果.
1. client发出Request
client向primary发出一个request, request的具体形式如下:
< R E Q U E S T , o , t , c > σ i
o o o表示operation,即具体执行的操作,t表示timestamp, 之所以需要加上时间戳,是在一个client在发出多次相同operation的时候,primary用timestamp进行区分, c表示client编号, 最后client将这些信息进行签名,以证实自身合法的身份.
注意: 如果client发送给了非primary节点,那么该节点会将client的request转发给primary.
2. pre-prepare阶段
为了确保所有的非拜占庭节点保持一致的状态,primary需要对 request 分配一个序号(sequence number),如果全体状态复制机能够对每一个 r e q u e s t request request的序号达成一致,那么就能达成整个系统服务状态的一致性. 首先,primary会检查 r e q u e s t request request的合法性,客户端的检查客户端的签名是否正确, 如果不正确,则拒绝执行后续步骤; 如果正确,则给 r e q u e s t request request分配一个唯一的序列号n,然后向其他backup广播一条 p r e ? p r e p a r e pre-prepare pre?prepare(可以翻译为预准备)消息,形式如下:
< < P R E ? P R E P A R E , v , n , d > σ p , m > \left < \left < PRE-PREPARE, v, n, d \right > _{\sigma p}, m \right > ??PRE?PREPARE,v,n,d?σp?,m?
v v v是当前view序号,n n n是为request?分配的序列号, d是request的摘要,m代表client的request信息.
backup收到pre-prepare消息时,会验证如下消息:
- request的签名是否正确, 确保client的签名不是伪造的.
- view的序列号与当前backup当前的view序列号相等.
- backup确定当前 view下,序号n没有分配给另外一个不同的request.
- n应该在某个范围[h, H].
3. prepare阶段
backup在prepare阶段,向本地的log中插入 pre-prepare消息以及自身的 prepare消息 ,并向其他backup(包括primary)广播一条prepare消息,prepare消息形式如下:
< P R E P A R E , v , n , d , i > σ i \left < PREPARE, v, n, d,i\right >_{\sigma i} ?PREPARE,v,n,d,i?σi?
其中 i i i是backup的编号,σ i \sigma_i σi?表示节点 i i i的签名, backup(包括primary)收到来自其他节点的prepare消息时,会验证如下信息:
- view序号和当前backup的view序号相同.
- n在[h,H]范围内.
如果replicai i i收到 2 f + 1 2f+1 2f+1(包括自身的)相同的 prepare消息,并且这些消息与replica收到的 pre-prepare消息的m , v , n m, v, n m,v,n相同, 则认为 prepared(m, v, n, i) 为真,并将***prepared(m, v, n, i)*插入log, 然后进入commit 阶段.
pre-prepare和prepare阶段的作用 p r e p a r e d ( m , v , n , i ) prepared(m, v, n, i) prepared(m,v,n,i)的主要作用就是,在一个view中一个request必然唯一的对应一个序列号n, 因为 p r e p a r e d ( m , v , n , i ) prepared(m, v, n, i) prepared(m,v,n,i)为真的条件是,有 2 f + 1 2f+1 2f+1个节点广播信息验证了 m , v , n m, v, n m,v,n的一一对应关系.
证明:2 f + 1 2f+1 2f+1中最多 f f f个叛徒, 那么至少还有 f + 1 f+1 f+1个诚实的节点发送了与当前primary的pre-preapre消息匹配的prepare消息, 或者这 f + 1 f+1 f+1个节点中存在primary, primary发送 pre-prepare, 其他 f f f个诚实的节点发送了与primary一致的prepare 信息 ; 此时剩余 f f f个诚实节点不会对另外一个 request m’达成一致,即不会认为p r e p a r e d ( m ′ , v , n , i ) prepared(m', v, n, i) prepared(m′,v,n,i)为真, f个拜占庭节点认同它们也发出f个 p r e p a r e d ( m ′ , v , n , i ) prepared(m', v, n, i) prepared(m′,v,n,i), 总共只能得到 2 f 2f 2f个 p r e p a r e d ( m ′ , v , n , i ) prepared(m', v, n, i) prepared(m′,v,n,i)信息, 由于数量不足,这无法使得 p r e p a r e d ( m ′ , v , n , i ) prepared(m', v, n, i) prepared(m′,v,n,i)为真. 这也解释了为什么prepared阶段需要 2 f + 1 2f+1 2f+1个节点的认同.因此, pre-prepare和prepare两个阶段保证了在同一个view下,n和request的一一对应关系.
如果 backupi i i判定p r e p a r e d ( m , v , n , i ) prepared(m, v, n, i) prepared(m,v,n,i)为真,则进入commit阶段.
4. commit阶段
backup进入commit阶段时, 向其他backup(包括primary)广播一条commit消息,具体形式如下:
< C O M M I T , v , n , D ( m ) , i > σ i \left < COMMIT, v, n, D(m), i \right >_{\sigma i} ?COMMIT,v,n,D(m),i?σi?
其他replica收到commit消息后,验证如下信息:
- 节点 i i i的签名正确.
- view的序号和本地view序号相同.
- n在[h,H]范围内.
- 节点 i i i的 p r e p a r e ( m , n , v , i ) prepare(m, n, v,i) prepare(m,n,v,i)为真,即节点 i i i也进入commit阶段.
- 节点 i i i收到 2 f + 1 2f+1 2f+1(包括自身)一致的commit信息, 并且它们与本地的 pre-prepare的 m , v , n m, v, n m,v,n全部相同.
节点 i i i每一个节点 i i i的状态都代表系统从执行了最低的序列号到当前序列n的所有request后的状态. c o m m i t t e d ? l o c a l ( m , n , v , i ) committed-local(m, n, v, i) committed?local(m,n,v,i)为真确保所有节点能够按照相同顺序执行request.执行完毕后所有节点会返回给client执行结果. 执行结果的形式如下:
< R E P L Y , v , t , c , i , r > σ i
其中t是client对应的request的时间戳.
Garbage Collect - Checkpointoint
Checkpoint消息有两个作用:
- 删除多余的log,节省replica的存储.
- replica丢失状态时,可以向其他节点请求系统状态,checkpoint可以证明传输数据的合法性.
< C H E C K P O I N T , n , d , i > σ i
其中d是当前n对应的request执行后的系统状态的摘要, i i i是节点编号.
如果收到 2 f + 1 2f+1 2f+1 相互一致并且合法的checkpoint信息,这些checkpoint代表的系统状态是合法的,该checkpoint被称之为stable checkpoint,节点可以删除序列号n之前所有的日志.
当其他节点请求本地传输某个系统状态时,2 f + 1 2f+1 2f+1个checkpoint消息可以作为传输的系统状态合法的证明.
checkpoint协议用来对下一次序列号的范围进行设定,当某个checkpoint被证明合法后,对序列号范围可以调整为H = h+k, 其中h是上一次稳定检查点的序列号, 假如每100个请求后进行一次检查点验证, k可以设置为200, 即H = 200.
View Change(视图切换)
view-change消息是为了应用系统中primary发生故障后用于切换新的primary.
backup广播View Change消息 backup每次收到一个request时,就会启动一个计时器,如果计时器超时之后,仍然没有执行该request,该backup认为primary出现了故障,遂发出一个view-change消息,格式如下:
< V I E W ? C H A N G E , v + 1 , n , C , P , i > σ i
v + 1 v+1 v+1表示下一个视图的编号, n表示backupi i i已知的最近的一个stable checkpoint的编号, C是一个集合,其中包括 2 f + 1 2f+1 2f+1个有效的检查点信息以证明该checkpoint是stable checkpoint. P是一个集合,集合中的元素是 P m P_m Pm?,P m P_m Pm?也是一个集合, 每一个 P m P_m Pm?中包含序列号大于n的pre-prepare消息以及与之匹配的 2 f 2f 2f个prepared消息.
新primary广播new-view消息 view序号为v+1时,对应的primary计算方式为 ( v + 1 ) % n (v+1)\% n (v+1)%n. 当v+1视图的primary收到 2 f 2f 2f个有效的view-change消息时,向其他所有节点广播new-view消息,具体格式如下:
< N E W ? V I E W , v + 1 , V , O > σ p
其中 V V V是一个集合, 包括 2 f + 1 2f+1 2f+1(包含新的primary自身)个view-change消息, O是pre-prepare消息的集合.
O的计算方式如下:
- primary首先确定两个变量 m i n ? s min-s min?s和 m a x ? s max-s max?s,m i n ? s min-s min?s指的是primary收到的所有view-change消息中最新的stable checkpoint的对应的序号n, m a x ? s max-s max?s指的是view-change消息中,集合 P P P中所有 P m P_m Pm?中pre-prepare消息编号的最大值.
- primary为编号为 ( m i n ? s , m a x ? s ] ( min-s, max-s] (min?s,max?s]范围内的request, 重新创建view序号为v+1的pre-prepare信息. 这里包含两种情况:
- primary收到的所有view-change消息中,至少有1个集合P不为空, 即存在某个编号 n ∈ ( m i n ? s , m a x ? s ] n\in (min-s, max-s] n∈(min?s,max?s]的pre-prepare消息.
- 所有的view-change消息中的集合P全部为空.
< < P R E ? P R E P A R E , v + 1 , n , d > σ p , m > \left < \left < PRE-PREPARE, v+1, n, d \right > _{\sigma p}, m \right > ??PRE?PREPARE,v+1,n,d?σp?,m?
其中d是该request的摘要, n的范围为 ( m i n ? s , m a x ? s ] (min-s, max-s] (min?s,max?s]
第二种情况下, primary创建一个null request的pre-prepare消息,格式如下:
$ \left < PRE-PREPARE, v+1, n, d^{null} \right > _{\sigma p} $
primary将 O O O中的信息插入log之中,如果primary计算得到的 m i n ? s min-s min?s大于primary本地最新的stable checkpoint的编号n,则primary也会将相应的checkpoint的proof插入到log之中, 然后丢弃stable checkpoint之前的所有信息,执行后续的pre-prepare信息.
backup验证new-view消息 当backup收到primary发送的new-view信息时, 检查如下信息:
- primary签名是否正确.
- new-view中包含的view-change消息是否合法
- 根据new view信息中的集合V计算集合 O O O, 检查计算得到的 O O O是否消息中的 O O O一致.
**注意:**backup如果没有最新的stable checkpoint的状态,应该首先向其他节点请求最新的stable checkpoint的系统服务状态,然后再执行 ( m i n ? s , m a x ? s ] (min-s, max-s] (min?s,max?s]范围内的request,否则会导致系统状态不一致.
PBFT协议讨论 为什么PBFT协议中节点总数 N > 3 f N>3f N>3f, 为什么pbft协议后两个阶段至少需要 2 f + 1 2f+1 2f+1的投票?
设系统总节点数为 N N N, 拜占庭节点数目为 f f f,于是剩余的诚实节点的数量为 N ? f N-f N?f.
假设节点 i i i想从prepare阶段成功地进入commit阶段的前提是 i i i收到总共 Q Q Q(包括自己的)个与它收到的< < P R E ? P R E P A R E , v , n , d > σ p , m > \left < \left < PRE-PREPARE, v, n, d \right > _{\sigma p}, m \right > ??PRE?PREPARE,v,n,d?σp?,m?相互匹配的 < P R E P A R E , v , n , d , k > σ k \left < PREPARE, v, n, d,k\right >_{\sigma k} ?PREPARE,v,n,d,k?σk? 消息, (匹配定义参看前文),Q Q Q的数量应该足够多,使得 i i i可以非常确定p r e p a r e d ( m , n , v , j ) prepared(m, n, v, j) prepared(m,n,v,j)为真而 p r e p a r e d ( m ′ , n , v , j ) prepared(m', n, v, j) prepared(m′,n,v,j)不可能为真,其中 m ′ ≠ m m'\ne m m′?=m,j j j可以等于 i i i.
系统中存在拜占庭节点, 所以 Q Q Q个投票中很可能包括 f f f个拜占庭节点的投票, 如果 f f f个拜占庭节点和一部分诚实的节点的数量(假设数量为A)大于等于Q, 它们可以达成共识, 唯一的确定 ( m , n , v ) (m, n, v) (m,n,v)的对应关系; 但是这 f f f个拜占庭节点又想和剩余部分的诚实节点(假设数量为B并且A>B)达成另外一个共识, 唯一的确定 ( m ′ , n , v ) (m', n, v) (m′,n,v)的对应关系, 为了阻止这种情况的发生, 只需要保证 f f f个拜占庭节点和剩余的诚实节点的数量小于Q,它们就无法确定另外一个共识:( m ′ , n , v ) (m', n, v) (m′,n,v).在此情况下, 如果一部分诚实的节点就 ( m , n , v ) (m, n, v) (m,n,v) 达成共识后不会担心会有其他不同的共识产生, 于是得到如下不等式
f + A < = Q → 2 f + 2 A < = 2 Q f+A<=Q\rightarrow 2f+2A<=2Q f+A<=Q→2f+2A<=2Q, 由于 A + B = N ? f < 2 A A+B =N-f < 2A A+B=N?f<2A, 于是可以得到:
2 f + N ? f < = 2 Q → N + f < 2 Q { 1 } 2f+N-f <= 2Q\rightarrow N+f<2Q \quad\quad\quad\quad \bold \{1\} 2f+N?f<=2Q→N+f<2Q{1}
另外一方面, f f f个拜占庭节点可能根本不会参与投票,这时必须要保证:
Q ≤ N ? f → 2 Q ≤ 2 N ? 2 f { 2 } Q\leq N-f \rightarrow 2Q \leq 2N-2f \quad\quad\quad\quad \bold \{2\} Q≤N?f→2Q≤2N?2f{2}
综合上述{1}和{2}两个不等式,可以得到如下结果:
N + f < 2 N ? 2 f → N > 3 f N+f<2N-2f \rightarrow N>3f N+f<2N?2f→N>3f
当 N = 3 f + 1 N = 3f+1 N=3f+1 时,2 Q > 4 f + 1 2Q > 4f +1 2Q>4f+1, 此时可以取 Q = 2 f + 1 Q = 2f+1 Q=2f+1就能满足要求.
另外,PBFT的论文中对于假设恶意节点数量为f时,总结点数n>3f的推导作了另外一番解释:
client发起一个请求后,系统中可能由于f个拜占庭节点不对请求进行响应,client需要对 n ? f n-f n?f个响应进行处理,但是如果没有响应的 f f f个节点并非拜占庭节点, client还必须从 n ? f n-f n?f中排除 f f f个响应,即 n ? f ? f = n ? 2 f n-f-f = n-2f n?f?f=n?2f, 这些响应数量至少应该超过拜占庭节点的数量, 即 n ? 2 f > f , n > 3 f n-2f>f, n > 3f n?2f>f,n>3f.
PBFT的三个步骤中将commit步骤去掉可以吗?
现在撤销 c o m m i t commit commit步骤, 只有 p r e ? p r e p a r e pre-prepare pre?prepare阶段和 p r e p a r e prepare prepare,如果 p r e p a r e ( m , n , v , i ) prepare(m,n,v,i) prepare(m,n,v,i)为真,则直接执行request并且返回给client.
假设有部分诚实的节点数量为 f f f,成功的收到了 2 f + 1 2f+1 2f+1与本地pre-prepare匹配的 p r e p a r e prepare prepare消息, 它们执行了对应的request-m, 假设m对应的序号是 n n n, view序号是 v v v. 但是其他 2 f + 1 2f+1 2f+1个节点在接收 p r e p a r e prepare prepare消息过程中网络不好, 没能收到足够的 2 f + 1 2f+1 2f+1个匹配的 p r e p a r e prepare prepare消息, 所以它们不会执行这个m.(pbft协议中假设网络中信息传输会出现丢失的情况). 然后这 2 f + 1 2f+1 2f+1个节点进行了 v i e w ? c h a n g e view-change view?change, 并且primary在这 2 f + 1 2f+1 2f+1个节点中产生, 这 2 f + 1 2f+1 2f+1个 v i e w ? c h a n g e view-change view?change消息中没有request-m的对应的 2 f 2f 2f个prepared消息,并且如果出现2 f + 1 2f+1 2f+1个节点的 view-change消息集合中的 P为空,新的primary重新发布 n e w ? v i e w new-view new?view消息时,消息中的O只包含了一个null-request的pre-prepare消息:
< P R E ? P R E P A R E , v + 1 , n , d n u l l > σ p \left < PRE-PREPARE, v+1, n, d^{null} \right > _{\sigma p} ?PRE?PREPARE,v+1,n,dnull?σp?
此时执行了request-m的 f f f个节点验证 new-view后通过,它们本地的序号n对应request-m,但是new-view中的序号 n n n对应的是一个null request, 而不是request-m. 这就导致了这诚实的 f f f个节点和另外 2 f + 1 2f+1 2f+1个节点的系统状态已经出现了不一致.这不满足PBFT协议保证系统一致性的另外一个条件,所有系统中的非故障节点都必须对request的执行达成一致.
实际上,引入 c o m m i t commit commit步骤中, c o m m i t t e d ? l o c a l l y committed-locally committed?locally为真才执行request这一条件, 就是保证至少有 2 f + 1 2f+1 2f+1个节点(其中至少 f + 1 f+1 f+1个诚实的节点)的 p r e p a r e d ( m , v , n ) prepared(m,v,n) prepared(m,v,n)为真, 这些节点一起执行request.
view-change时,这 2 f + 1 2f+1 2f+1个执行了reqeust-m的节点中,至少有 f + 1 f+1 f+1个节点参与其中,这个节点的参与,保证了request-m在view序号为 v+1的时候分配的编号仍然是 view序号为v时的序号,这就是为什么论文中说 c o m m i t commit commit步骤保证了不同 v i e w view view中request的有序执行.
如果client在发出request之后在一定时间内没有收到足够多的响应怎么办?
client会将request广播给所有节点, 如果收到的节点已经处理了这条request,则会直接向client返回执行结果.如果该节点没有处理过这条request,则会将这条request转发给primary. 如果primary在一段时间内不广播该request,其他节点会怀疑primary作恶并且进行 v i e w ? c h a n g e view-change view?change操作以更换primary.
为什么client收到 f + 1 f+1 f+1个一致的结果就认为结果是正确的?
在pbft协议中最多有 f f f个恶意的节点,当client收到 f + 1 f+1 f+1个相同的执行结果时,说明其中至少包含一个诚实的节点的执行结果. 诚实的节点返回执行结果的前提就是, 分别在prepared和committed-local达成了一致,并且这个一致性是唯一的,这也能说明系统中诚实的节点达成了唯一的一致, 所以client可以相信执行结果.
backup 验证pre-prepare消息时为什么n的范围需要在[h, H]之间
为了防止恶意的primary选择一个超级大的序列号n耗尽序列号的空间.不过这个解释不太令人信服,我暂时也不太清楚.
pbft协议中的log有什么作用?
- log可以记录节点是否执行了某个request.
- 另外如果发生 v i e w ? c h a n g e view-change view?change时可能会从最新的stable checkpoint重新执行request,log记录了request, 方便重新执行.
- 其他节点请求stable point时可以直接传输 c o m m i t t e d ? l o c a l committed-local committed?local消息方便该节点恢复.
[2] G. Bracha and S. Toueg. Asynchronous Consensus and Broadcast Protocols. Journal of the ACM, 32(4), 1995
[3] Castro M, Liskov B. Practical Byzantine fault tolerance[J]. operating systems design and implementation, 1999: 173-186.
推荐阅读
- 推动NFT走出监管困境,BSN推出支持NFT基础设施网络
- 腾讯|SaaS的收入模型有哪些(终于有人讲明白了)
- 就业方向上什么才是最重要的(--- 来自程序猿的迷茫。(C++?Java?or算法?))
- 区块链中加密货币的含义
- 波场万倍潜力币HYL23号21:09分 正式上线JustSwap
- 《瀚兰房地产开发区块链应用及案例分享》BSN培训精华回顾
- 对联盟链而言,跨链协议为什么重要()
- 区块链能够应用在哪些行业
- BSN区块链服务网络中密钥托管模式和公钥上传模式有啥区别()
- 币圈人物传|币圈大佬今何在 唯有一诺正当时