|
| 1 | +# 共识层(CL)架构 |
| 2 | + |
| 3 | +> 许多区块链共识协议都是*可分叉的*。可分叉的链使用分叉选择规则,有时会发生重组。 |
| 4 | +> |
| 5 | +> 以太坊的共识协议结合了两个独立的共识协议。_LMD GHOST_ 本质上提供活性。_Casper FFG_ 提供最终性。这两者共同被称为 _Gasper_。 |
| 6 | +> |
| 7 | +> 在一个*活性*协议中,好的事情总会发生。在一个*安全*协议中,坏的事情永远不会发生。没有任何实用的协议能够始终保持安全性的同时又始终保持活性。 |
| 8 | +
|
| 9 | +## 分叉选择机制 |
| 10 | + |
| 11 | +如[BFT](BFT)中所述,由于各种原因 - 比如网络延迟、中断、消息乱序或恶意行为 — 网络中的节点可能会对网络状态有不同的认知。最终,我们希望每个诚实节点都能就一个相同的、线性的历史记录和系统状态的共同视图达成一致。协议的分叉选择规则正是帮助实现这种共识的机制。 |
| 12 | + |
| 13 | +#### 区块树 |
| 14 | + |
| 15 | +基于区块树和节点对网络本地视图的决策标准,分叉选择规则旨在选择最有可能成为最终规范链的分支。它选择在节点收敛到共同视图时最不可能被剪枝的分支。 |
| 16 | + |
| 17 | +<a id="img_blocktree"></a> |
| 18 | + |
| 19 | +<figure class="diagram" style="text-align:center; width:95%"> |
| 20 | + |
| 21 | + |
| 22 | + |
| 23 | +_分叉选择规则从候选区块中选择一个头区块。头区块标识了一条从创世区块开始的唯一线性区块链。_ |
| 24 | + |
| 25 | +</figcaption> |
| 26 | +</figure> |
| 27 | + |
| 28 | +#### 分叉选择规则 |
| 29 | + |
| 30 | +分叉选择规则通过选择分支末端的区块(称为头区块)来隐式地选择一个分支。对于任何正确的节点来说,任何分叉选择的首要规则是:被选中的区块必须根据协议规则是有效的,并且它的所有祖先区块也必须是有效的。任何无效区块都会被忽略,基于无效区块构建的区块也是无效的。 |
| 31 | + |
| 32 | +以下是几个不同分叉选择规则的例子: |
| 33 | + |
| 34 | +- **工作量证明(Proof-of-work)**:在以太坊和比特币中,使用"最重链规则"(有时被称为"最长链",但这种说法并不严格准确)。头区块是累积"工作量"最多的链的末端区块。 |
| 35 | + > 需要注意的是,与普遍的认知相反,以太坊的工作量证明协议[并没有使用](https://ethereum.stackexchange.com/questions/38121/why-did-ethereum-abandon-the-ghost-protocol/50693#50693)任何形式的 GHOST 作为分叉选择规则。这个误解一直存在,可能是因为[以太坊白皮书](https://ethereum.org/en/whitepaper/#modified-ghost-implementation)的缘故。最终当 Vitalik 被问到这个问题时,他确认虽然在 PoW 下曾计划使用 GHOST,但由于担心某些未明确的攻击而从未实施。最重链规则更简单且经过充分测试,运行良好。 |
| 36 | +- **Casper FFG (权益证明)**:在以太坊的 PoS Casper FFG 协议中,分叉选择规则是"遵循包含**最高高度**已证明检查点的链",并且永远不会回滚已最终确定的区块。 |
| 37 | +- **LMD GHOST (权益证明)**:在以太坊的 PoS LMD GHOST 协议中,分叉选择规则是选择"最贪婪最重可观察子树"。这涉及计算验证者对区块及其子孙区块的累积投票。它同样也应用 Casper FFG 的规则。 |
| 38 | + |
| 39 | +这些分叉选择规则都会为区块分配一个数值分数。得分最高的区块即为头区块。目标是让所有正确的节点在看到某个区块时,都会认同它是头区块并跟随它的分支。这样,所有正确的节点最终都会就一条从创世区块开始的规范链达成一致。 |
| 40 | + |
| 41 | +#### 重组和回滚 |
| 42 | + |
| 43 | +当节点接收到新的投票(在权益证明中包括对区块的新投票)时,它会根据这些新信息重新评估分叉选择规则。通常情况下,新区块会是当前头区块的子区块,并成为新的头区块。 |
| 44 | + |
| 45 | +然而,有时新区块可能是区块树中另一个区块的后代。如果节点没有这个新区块的父区块,它会向其对等节点请求该区块以及其他缺失的区块。 |
| 46 | + |
| 47 | +在更新后的区块树上运行分叉选择规则可能会显示,新的头区块位于与之前头区块不同的分支上。当这种情况发生时,节点必须执行重组(reorganisation)。这意味着它将移除(回滚)之前包含的区块,并采用新头区块所在分支上的区块。 |
| 48 | + |
| 49 | +例如,如果一个节点的链上有区块 $A, B, D, E,$ 和 $F$,并且它将 $F$ 视为头区块,它知道有区块 $C$ 的存在但在其当前的链视图中并不出现;该区块位于侧分支上。 |
| 50 | + |
| 51 | +<a id="img_reorg0"></a> |
| 52 | + |
| 53 | +<figure class="diagram" style="text-align:center"> |
| 54 | + |
| 55 | + |
| 56 | + |
| 57 | +<figcaption> |
| 58 | + |
| 59 | +_此时,节点认为区块 $F$ 是最佳头区块,因此它的链是区块 $[A \leftarrow B \leftarrow D \leftarrow E \leftarrow F]$_ |
| 60 | + |
| 61 | +</figcaption> |
| 62 | +</figure> |
| 63 | + |
| 64 | +当节点随后收到构建在区块 $C$ 上而不是当前头区块 $F$ 上的区块 $G$ 时,它必须决定是否应该将 $G$ 作为新的头区块。举例来说,如果分叉选择规则表明 $G$ 是更好的头区块,节点将回滚区块 $D、E$ 和 $F$。它会将这些区块从链上移除,就像从未收到过它们一样,并回退到区块 $B$ 之后的状态。 |
| 65 | + |
| 66 | +然后,节点会将区块 $C$ 和 $G$ 添加到它的链上并处理它们。在这次重组之后,节点的链将包含 $A、B、C$ 和 $G$。 |
| 67 | + |
| 68 | +<a id="img_reorg1"></a> |
| 69 | + |
| 70 | +<figure class="diagram" style="text-align:center"> |
| 71 | + |
| 72 | + |
| 73 | + |
| 74 | +<figcaption> |
| 75 | + |
| 76 | +_此时,节点认为区块 $G$ 是最佳头区块,因此它的链必须改变为区块 $[A \leftarrow B \leftarrow C \leftarrow G]$_ |
| 77 | + |
| 78 | +</figcaption> |
| 79 | +</figure> |
| 80 | + |
| 81 | +后来,可能出现一个区块 $H$,它构建在区块 $F$ 上,分叉选择规则说 $H$ 应该是新的头区块,节点将再次重组,回滚到区块 $B$ 并重新播放 $H$ 分支上的区块。 |
| 82 | + |
| 83 | +由于网络延迟,一两个区块的短重组是很常见的。除非链遭受攻击,或者分叉选择规则及其实现存在漏洞,否则较长的重组应该很少发生。 |
| 84 | + |
| 85 | +### 安全性和活性 |
| 86 | + |
| 87 | +在共识机制中,有两个关键概念:安全性和活性。 |
| 88 | + |
| 89 | +**安全性**意味着"坏事永远不会发生",比如防止双重支付或确认冲突的检查点。它确保一致性,这意味着所有诚实节点应该始终对区块链的状态达成一致。 |
| 90 | + |
| 91 | +**活性**意味着"好事最终会发生",确保区块链始终能添加新的区块,永远不会陷入死锁状态。 |
| 92 | + |
| 93 | +**CAP 定理**指出没有分布式系统能够同时提供一致性、可用性和分区容错性。这意味着当通信不可靠时,我们无法设计出在所有情况下都既安全又活跃的系统。 |
| 94 | + |
| 95 | +#### 以太坊优先保证活性 |
| 96 | + |
| 97 | +以太坊的共识协议旨在在良好的网络条件下同时提供安全性和活性。然而,在网络出现问题时,它优先保证活性。在网络分区的情况下,每个分区的节点都会继续产生区块,但无法达成最终确定性(这是一个安全性属性)。如果分区持续存在,每个分区可能会确定不同的历史记录,导致形成两条无法调和的独立链。 |
| 98 | + |
| 99 | +因此,虽然以太坊努力同时实现安全性和活性,但它倾向于确保网络保持活跃并继续处理交易,即使在严重的网络中断期间可能会带来潜在的安全性问题。 |
| 100 | + |
| 101 | +## 机器中的幽灵 |
| 102 | + |
| 103 | +以太坊的权益证明共识协议结合了两个独立的协议:[LMD GHOST](/wiki/cl/gasper?id=lmd-ghost.md) 和 [Casper FFG](/wiki/cl/gasper?id=casper-ffg.md)。这两者共同被称为 "Gasper" 共识协议。关于这两个协议的详细信息以及它们如何协同工作将在下一节 [Gasper] 中详细介绍。 |
| 104 | + |
| 105 | +Gasper 旨在结合 LMD GHOST 和 Casper FFG 的优势。LMD GHOST 提供活性,通过定期产生新区块确保链持续运行。然而,它容易产生分叉且在形式上并不安全。另一方面,Casper FFG 通过定期对链进行最终确定来提供安全性,防止长程回滚。 |
| 106 | + |
| 107 | +本质上,LMD GHOST 保持链向前推进,而 Casper FFG 通过确定区块来确保稳定性。这种组合使以太坊能够优先保证活性,意味着即使 Casper FFG 无法确定区块,链仍然能继续增长。尽管这种组合机制并非完美且存在一些复杂性,但它是一个在实践中运行良好的工程解决方案。 |
| 108 | + |
| 109 | +## 架构 |
| 110 | + |
| 111 | +以太坊是一个由节点组成的去中心化网络,这些节点通过点对点连接进行通信。这些连接由运行以太坊客户端软件的计算机形成。 |
| 112 | + |
| 113 | +<a id="img_network"></a> |
| 114 | + |
| 115 | +<figure class="diagram" style="text-align:center"> |
| 116 | + |
| 117 | + |
| 118 | + |
| 119 | +<figcaption> |
| 120 | + |
| 121 | +_节点不需要运行验证者客户端(绿色节点)就可以成为网络的一部分,但是要参与共识,则需要质押 32 ETH 并运行验证者客户端。_ |
| 122 | + |
| 123 | +</figcaption> |
| 124 | +</figure> |
| 125 | + |
| 126 | +### 共识层的组件 |
| 127 | + |
| 128 | +- **信标节点**:信标节点使用客户端软件来协调以太坊的权益证明共识。例如 Prysm、Teku、Lighthouse 和 Nimbus。信标节点与其他信标节点、本地执行节点以及(可选的)本地验证者进行通信。 |
| 129 | + |
| 130 | +- **验证者**:验证者客户端是允许人们在以太坊共识层质押 32 ETH 的软件。验证者在权益证明系统中负责提议区块,取代了工作量证明中的矿工。验证者只与本地信标节点通信,后者为其提供指令并将其工作广播到网络中。 |
| 131 | + |
| 132 | +承载真实应用的主要以太坊网络被称为以太坊主网。以太坊主网是以太坊的实时生产环境,用于铸造和管理真实的以太币(ETH)并具有实际货币价值。 |
| 133 | + |
| 134 | +此外还有测试网络,用于铸造和管理测试用的以太币,供开发者、节点运营者和验证者在使用主网真实 ETH 之前测试新功能。每个以太坊网络都有两层:执行层(EL)和共识层(CL)。每个以太坊节点都包含这两层的软件:执行层客户端软件(如 Nethermind、Besu、Geth 和 Erigon)和共识层客户端软件(如 Prysm、Teku、Lighthouse、Nimbus 和 Lodestar)。 |
| 135 | + |
| 136 | +<a id="img_node-layers"></a> |
| 137 | + |
| 138 | +<figure class="diagram" style="width: 80%;text-align:center"> |
| 139 | + |
| 140 | + |
| 141 | + |
| 142 | +</figure> |
| 143 | + |
| 144 | +**共识层**负责维护共识链(信标链)并处理从其他对等节点接收到的共识区块(信标区块)和证明。**共识客户端**参与一个独立的[点对点网络](/wiki/cl/cl-networking.md),其规范与执行客户端不同。它们需要参与区块传播以接收来自对等节点的新区块,并在轮到它们提议时广播区块。 |
| 145 | + |
| 146 | +执行层和共识层客户端并行运行,需要保持连接以进行通信。共识客户端向执行客户端提供指令,执行客户端则将交易包传递给共识客户端以包含在信标区块中。通信通过本地 RPC 连接使用 **Engine-API** 实现。它们共享一个 [ENR](/wiki/cl/cl-networking?id=ethereum-enr),但每个客户端使用独立的密钥(eth1 密钥和 eth2 密钥)。 |
| 147 | + |
| 148 | +### 控制流程 |
| 149 | + |
| 150 | +**当共识客户端不是区块生产者时:** |
| 151 | + |
| 152 | +1. 通过区块 gossip 协议接收区块。 |
| 153 | +2. 对区块进行预验证。 |
| 154 | +3. 将区块中的交易作为执行负载发送到执行层。 |
| 155 | +4. 执行层执行交易并验证区块状态。 |
| 156 | +5. 执行层将验证数据发送回共识层。 |
| 157 | +6. 共识层将区块添加到其区块链中并对其进行证明,将证明广播到网络中。 |
| 158 | + |
| 159 | +**当共识客户端是区块生产者时:** |
| 160 | + |
| 161 | +1. 收到成为下一个区块生产者的通知。 |
| 162 | +2. 调用执行客户端中的创建区块方法。 |
| 163 | +3. 执行层访问交易内存池。 |
| 164 | +4. 执行客户端将交易打包成区块,执行它们,并生成区块哈希。 |
| 165 | +5. 共识客户端将交易和区块哈希添加到信标区块中。 |
| 166 | +6. 共识客户端通过区块 gossip 协议广播区块。 |
| 167 | +7. 其他客户端验证区块并对其进行证明。 |
| 168 | +8. 一旦得到足够验证者的证明,区块就会被添加到链头,被证明合理性并最终确定。 |
| 169 | + |
| 170 | +### 状态转换 |
| 171 | + |
| 172 | +状态转换函数在区块链中至关重要。每个节点维护一个反映其对世界认知的状态。 |
| 173 | + |
| 174 | +节点通过按顺序应用区块使用"状态转换函数"来更新它们的状态。这个函数是"纯函数",意味着其输出仅依赖于输入且没有副作用。因此,如果每个节点从相同的状态(创世状态)开始并应用相同的区块,它们最终都会得到相同的状态。如果不是这样,就说明出现了共识失败。 |
| 175 | + |
| 176 | +如果 $S$ 是信标状态,$B$ 是信标区块,则状态转换函数 $f$ 为: |
| 177 | + |
| 178 | +$$S' \equiv f(S, B)$$ |
| 179 | + |
| 180 | +这里,$S$ 是前置状态,$S'$ 是后置状态。函数 $f$ 随着每个新区块的到来而迭代执行以更新状态。 |
| 181 | + |
| 182 | +### 信标链状态转换 |
| 183 | + |
| 184 | +与区块驱动的工作量证明不同,信标链是槽驱动的。状态更新取决于槽的进展,而不依赖于区块是否存在。 |
| 185 | + |
| 186 | +信标链的状态转换函数包括: |
| 187 | + |
| 188 | +1. **每槽转换**: $S' \equiv f_s(S)$ |
| 189 | +2. **每块转换**: $S' \equiv f_b(S, B)$ |
| 190 | +3. **每周期转换**: $S' \equiv f_e(S)$ |
| 191 | + |
| 192 | +每个函数都按照信标链规范中定义的特定时间更新链。 |
| 193 | + |
| 194 | +### 有效性条件 |
| 195 | + |
| 196 | +从前置状态和已签名区块得到的后置状态是通过 `state_transition(state, signed_block)` 计算的。导致未处理异常(例如,断言失败或越界访问)或 uint64 溢出/下溢的转换都是无效的。 |
| 197 | + |
| 198 | +### 信标链状态转换函数 |
| 199 | + |
| 200 | +从前置状态 `state` 和已签名区块 `signed_block` 得到的后置状态是通过 `state_transition(state, signed_block)` 定义的。触发未处理异常(例如,`assert` 断言失败或列表越界访问)的状态转换被视为无效。导致 `uint64` 溢出或下溢的状态转换也被视为无效。 |
| 201 | + |
| 202 | +```python |
| 203 | +def state_transition(state: BeaconState, signed_block: SignedBeaconBlock, validate_result: bool=True) -> None: |
| 204 | + block = signed_block.message |
| 205 | + # Process slots (including those with no blocks) since block |
| 206 | + process_slots(state, block.slot) |
| 207 | + # Verify signature |
| 208 | + if validate_result: |
| 209 | + assert verify_block_signature(state, signed_block) |
| 210 | + # Process block |
| 211 | + process_block(state, block) |
| 212 | + # Verify state root |
| 213 | + if validate_result: |
| 214 | + assert block.state_root == hash_tree_root(state) |
| 215 | +``` |
| 216 | + |
| 217 | +```python |
| 218 | +def verify_block_signature(state: BeaconState, signed_block: SignedBeaconBlock) -> bool: |
| 219 | + proposer = state.validators[signed_block.message.proposer_index] |
| 220 | + signing_root = compute_signing_root(signed_block.message, get_domain(state, DOMAIN_BEACON_PROPOSER)) |
| 221 | + return bls.Verify(proposer.pubkey, signing_root, signed_block.signature) |
| 222 | +``` |
| 223 | + |
| 224 | +```python |
| 225 | +def process_slots(state: BeaconState, slot: Slot) -> None: |
| 226 | + assert state.slot < slot |
| 227 | + while state.slot < slot: |
| 228 | + process_slot(state) |
| 229 | + # Process epoch on the start slot of the next epoch |
| 230 | + if (state.slot + 1) % SLOTS_PER_EPOCH == 0: |
| 231 | + process_epoch(state) |
| 232 | + state.slot = Slot(state.slot + 1) |
| 233 | +``` |
| 234 | + |
| 235 | +## Resources |
| 236 | + |
| 237 | +- Vitalik Buterin, ["Parametrizing Casper: the decentralization/finality time/overhead tradeoff"](https://medium.com/@VitalikButerin/parametrizing-casper-the-decentralization-finality-time-overhead-tradeoff-3f2011672735) |
| 238 | +- [Engine API spec](https://hackmd.io/@n0ble/consensus_api_design_space) |
| 239 | +- [Vitalik's Annotated Ethereum 2.0 Spec](https://notes.ethereum.org/@vbuterin/SkeyEI3xv) |
| 240 | +- Ethereum, ["Eth2: Annotated Spec"](https://github.com/ethereum/annotated-spec) |
| 241 | +- Martin Kleppmann, [Distributed Systems.](https://www.youtube.com/playlist?list=PLeKd45zvjcDFUEv_ohr_HdUFe97RItdiB) |
| 242 | +- Leslie Lamport et al., [The Byzantine Generals Problem.](https://lamport.azurewebsites.net/pubs/byz.pdf) |
| 243 | +- Austin Griffith, [Byzantine Generals - ETH.BUILD.](https://www.youtube.com/watch?v=c7yvOlwBPoQ) |
| 244 | +- Michael Sproul, ["Inside Ethereum"](https://www.youtube.com/watch?v=LviEOQD9e8c) |
| 245 | +- [Eth2 Handbook by Ben Edgington](https://eth2book.info/capella/part2/consensus/) |
0 commit comments