直观的恒星共识协议

 
 

分布式共识实际上是Stellar的核心,但它可能很难理解。很容易迷路,因为有很多步骤,很多活动部件,还有很多规则要记住。在恒星发展基金会,我们通过首先建立高层次的理解来处理复杂的问题。在这篇文章中,我们将讨论恒星共识协议(SCP)的简化版本,并提供恒星网络如何通过SCP达成协议的具体例子。

法定人数回顾

我们之前的文章“为什么 Quorums 很重要以及 Stellar 如何接近它们”,主要关注仲裁切片和仲裁配置的含义。召回

仲裁切片是网络上给定节点选择信任和依赖的节点的子集。
如果一组节点不为空,则该节点是仲裁,并且每个成员都包含一个切片。

这些定义非常抽象。实际上,在配置节点时,操作员会指定他们信任的节点列表。它们还指定阈值,或此类列表中必须同意的最小节点数。我们将这些列表称为仲裁集。例如,如果操作员将其仲裁设置为阈值为 2 的 {A, B, C},则 {A, B}, {B, C} 或 {C, A} 必须同意,节点才能继续。{A, B}, {B, C} 或 {C, A} 都是仲裁切片。

现在,我们介绍另一个对SCP至关重要的概念:阻塞集

阻塞集是仲裁集中的任何一组节点,没有它,就不可能达成共识。

例如,给定一个阈值为 3 的 4 个节点的仲裁集(意味着 4 个节点中至少有 3 个必须同意),2 个节点的任意组合都会形成一个阻塞集。由于仲裁集中的任何节点都可以位于阻塞集中,因此可能存在许多不同的阻塞集。

一切都从语句开始

共识回合是就要在特定分类账中执行的交易达成一致的程序。语句是最小的共识回合构建块。在Stellar的上下文中,有效语句表达了节点对给定分类账达成一致的交易的不同意见。例如,“我投票支持一组交易 C 考虑用于此分类账”是一个有效的语句。同样,“我向此分类账应用了一组交易 C”也是一个有效的语句。

联合投票作为协议工具

一个分布式系统,比如恒星网络,需要一个共识机制来同意不同的陈述。我们如何确保节点不会过早地对语句执行操作(例如,确认支付交易)?更一般地说,节点何时知道确认某些内容是安全的?在恒星共识协议(SCP)中,协议是通过联合投票实现的。

为了理解联合投票,我们必须首先了解节点如何根据它从其仲裁集中学到的知识来推断网络状态。具体来说,节点不知道其仲裁集之外的哪些节点决定了什么,但网络必须就单个值达成一致。在网络上每个诚实节点100%同意声明之前,它必须经历联合投票的三个步骤:投票接受确认。给定一个语句 A,一个节点可能有四种意见:

  1. 我对A一无所知,也没有意见。
  2. 投票A,这是有效的,但我不知道采取行动是否安全。
  3. 接受A,因为有足够的节点支持这个声明,但我不知道对它采取行动是否安全。
  4. 确认A,采取行动是安全的。即使我的仲裁中的每个节点都没有确认 A,它们也无法确认除 A 之外的任何其他内容。

为了在上述州之间进行过渡(投票,接受,确认),联合投票规定了以下规则:

投票A,如果它与我之前的投票一致

如果出现以下任一情况,请接受 A

  • 仲裁切片中的每个节点都投票支持或接受 A
  • 我的屏蔽集接受了A(即使我过去投票支持与A相矛盾的东西,我也会忘记那次投票,并继续“接受A”)

确认A 如果仲裁切片中的每个节点都接受 A

但是,为什么联合投票真的是一个三步走的过程呢?难道不能通过简单地接受声明来达成协议吗?答案在于上面的规则之一:“我的阻塞集接受了A”。如果没有确认步骤,节点的阻塞集可以说服节点接受任何任意值。只有当所有阻塞集都是诚实的时,接受语句才有效,但不能保证它们会是诚实的。通过确认步骤,仅当其仲裁中的每个节点也接受该语句时,节点才会继续就该语句达成一致,从而产生最佳安全性。

SCP中的联合投票

每轮共识可以分为两个阶段:

  • 选择可包含在分类帐中的候选交易集(提名协议)
  • 确保网络可以一致确认和应用指定的交易集(投票协议)

直观地说,SCP就像一个漏斗:

  • 节点开始时有可能同意任何值(在Stellar的上下文中,值是一个交易集)
  • 提名语句旨在选择(理想情况下)少量的候选有效值 N。
  • Prepare 语句尝试确保可以提交该有限集合的子集 M(远小于 N)(即任何已确认指定的值)。
  • 提交语句要么继续提交已确认的预准备值,要么提交节点的阻塞集已提交的内容。
scp-funnel

提名

提名协议通过对诸如“提名事务集 C”之类的语句执行联合投票,帮助确定要同意的值集的范围。如果投票成功,交易集将添加到候选人列表中,以便稍后在投票协议中使用。有一个重要的不变性:一旦节点确认其第一个候选节点,它就会停止投票以提名任何新的事务集。它仍然可以接受并确认之前引入的提名语句,例如,如果它看到阻塞集接受了某些新值。这种不变性保证了在某些时候所有节点都将收敛于一个候选集。直观地说,如果网络上的每个节点都停止引入新值,但继续确认其他节点确认的内容,最终每个人都将得到相同的候选列表。

由于它无法判断其他节点是否已收敛(没有新值正在投票)或某些投票仍在传输中(尚未收到),因此节点可以在确认候选人后立即启动投票协议。投票协议的输入称为复合值,它是通过提名确认的候选人名单中设置的最大交易。

即使在节点确认其第一个候选人并启动投票协议之后,提名也会继续在后台运行,因此该节点在了解网络上其他节点接受和确认的提名时,可能会确认更多候选人。

投票

投票协议尝试安全地提交复合值,即使网络上的某些节点变得不可用。它由两个步骤组成:

  • “prepare”,验证节点的仲裁切片是否具有正确的值并愿意提交它
  • “commit”,这可确保节点的仲裁切片实际提交该值。

投票协议包括对声明“准备交易集C”和“提交交易集X”的联合投票。它表现得乐观,并尝试在提名可能仍在运行时提交复合值。在最佳情况下,网络的其余部分同意建议的值。在最坏的情况下,节点可能会在尝试提交其复合值时遇到困难;然后,它将超时并使用新的复合值(通过提名更新)再次尝试,或者将其投票切换到阻止集接受的内容(放弃其先前的投票)。

共识回合演练

出于此示例的目的,我们假设网络可以任意延迟或删除节点发送的消息。

请考虑以下仲裁配置,其中左侧和右侧都连接良好;然而,双方之间的依赖性是单向的。换句话说,左侧在其仲裁切片中具有来自右侧的节点,但相反。实际上,右侧是较大仲裁内的仲裁本身,如虚线所示。这意味着A在未经右侧节点同意的情况下可能无法继续。此外,A 是左侧的阻挡集。

Exact quorum slice configurations are omitted for simplicity.
为简单起见,省略了确切的仲裁切片配置。

1. 共识回合开始时,节点 A 通过投票给“提名 C”来引入交易集 C。在SCP中,节点广播其对声明的决定,例如投票,接受或提交,因此节点A也广播其提名投票。

a-consensus-round-begins

2.双方都收到节点A的广播消息,并且由于它是有效的,也投票提名C。

broadcast-a

3. Since every node voted to nominate C, it is safe to proceed to accept nomination of C. Again, nodes broadcast their acceptance.

everynodesafe

4. The connection quality may vary across nodes. In this example, imagine the network is pretty reliable on the left side, but is experiencing delays/outages on the right side. Because of these delays, the right side does not see acceptance of nomination of C. On the other hand, since the left side received the messages, and each node saw that a quorum slice accepted Nominate C (including node A, its blocking set), it proceeds to confirm the statement.

confirmstatement

5. Recall, when a “nominate” statement is confirmed, its transaction set is added to the candidate set that SCP keeps track of. We denote such sets with curly braces. The node then selects the composite value, which is the largest transaction set in the candidate set. In this example, the left side adds C to its (currently empty) candidate set, creating {C}. It then selects C as the composite value (pretty straightforward since C is the only value in the candidate set), and starts the ballot protocol by beginning to vote on a statement to prepare C.

beginning-vote

6. Recall, the right side and node A are experiencing outages, so messages “Accept Nominate C” are still in-transit. Without them, the right side cannot proceed to confirm the nomination of C. Meanwhile, there is a new transaction set D introduced by some nodes on the right side, and the vote to “Nominate D” is broadcast.

nominate-broadcast

7. Node A receives a vote to nominate D, and since it is valid, and A has not confirmed anything yet (recall nomination of C got stuck due to network unreliability), it votes to nominate D as well. Because the left side has already confirmed nomination of C, it may not vote for any new values (only accept whatever their blocking set accepts), so it does not vote for D.

doesnotvoteford

8. Since the right side voted to nominate D, they may accept D, and broadcast their messages. By now, the connectivity issues plaguing the right side have been fixed, so this time the network properly delivers those messages.

delivermessages

9. Since every node on the right side sees a quorum slice accept the nomination of D, they all proceed to confirm D. When new values are introduced, nodes include previous values they voted for or accepted in their messages. Node A previously accepted the nomination of C, so when the nodes on the right receive messages about D, they also learn about the acceptance of the nomination of C. The right side and A confirm C as well, and add it to the candidate set. At the end of this step, the candidate set for the right side is {C, D}.

candidateset

10. The right side (including A) may now compute its composite value and start the ballot protocol. In this example, let’s assume that transaction set D is bigger than transaction set C. Since that’s the case, the candidate set {C, D} produces the composite value D , which the right side votes to prepare.

valuedvote

11. Votes to prepare D are broadcasted successfully, and as the right side voted for it, they accept “Prepare D” and broadcast it.

prepareD

12. Recall that A is a blocking set for the left side — i.e., the left side may not proceed without the agreement of A. The left side gets the message that A accepted “Prepare D”, and accepts “Prepare D”, even though it voted for “Prepare C” earlier.

getmessagePrepare

13. At this point, all nodes may confirm “Prepare D”, as prescribed by the federated voting rules.

confirmPrepareD

14. Through another round of federated voting on the statement “Commit D”, the nodes on both sides agree that it is safe to act on D.

actonD

一旦节点确认“Commit D”,就可以安全地对D进行操作.节点将组成D的事务写入数据库,关闭分类帐,然后启动一个新分类帐。该过程每5秒一遍又一遍地重复,无限期地重复。