# 以太坊协议层的七年之变

By [freeyao](https://paragraph.com/@freeyao) · 2022-09-13

---

以太坊协议层的七年之变
===========

什么是以太坊
------

什么是以太坊？一千个人有一千个答案，而本文想探讨的是最为一致的答案，即以太坊的协议是什么？或用更技术地描述——如果要开发以太坊的客户端(PoW链/ETH1)，我需要依照什么规则？

你没法找到一份规范描述以太坊当前的共识规则，因为以太坊的协议是通过增量更新来描述的。以太坊黄皮书描述了创世时的完整协议，而每一次协议变更都称为一次硬分叉(当然，也有人尝试用「网络升级」这个表述)，需要所有的客户端更新代码。简而言之，以太坊通过硬分叉来实现协议层的变化，变化的最小单元被称为以太坊改进提案(EIP, Ethereum Improvement Proposal)，一次硬分叉包含一组以太坊改进提案。 本文将回顾以太坊的历次硬分叉及其中包含的改进提案，试图展现过去的七年中以太坊究竟做了什么。

历次硬分叉介绍
-------

### 概况

以太坊的历次硬分叉可以通过[此页面](https://github.com/ethereum/execution-specs)查看。自 2015 年 7 月30日上线起，共进行了 14 次硬分叉，包含 39 个 EIP（「君士坦丁堡」和「彼得堡」视为同一次）。间隔最近的两次硬分叉是 26 天，间隔最远的两次则是 490 天。

硬分叉分为「主动升级」和「被动升级」。主动升级指的是开发团队主动对以太坊协议的修正，而被动升级则是「不得不」采取的行动，以应对潜在的安全性风险。被动升级至少包括「DAO Fork」、「Tangerine Whistle」、「Spurious Dragon」、「Muir Glacier」、「Arrow Glacier」、「Gray Glacier」，它们或处置黑客盗窃(DAO Fork)，或应对 DDOS 攻击(Tangerine Whistle, Spurious Dragon)，或仅仅处置难度炸弹(Muir Glacier, Arrow Glacier, Gray Glacier)。而「主动升级」大致符合白皮书的规划（至少在命名上），Frontier(Frontier, Frontier Thawing)、Homestead、Metropolis(Byzantium, Constantinople/Petersburg, Istanbul)，而 Berlin 和 London 则是以太坊路线图变更后的过渡性升级。此外，多次主动升级也包含了推迟难度炸弹的选项。

硬分叉是如何达成共识的呢？尽管关于硬分叉的协商并无成文规定，而是依照某种社区惯例进行，但其流程发生过一次变更，标志性事件是 Martin Holst Swende 提出了「以 EIP 为中心的升级」。这种新的硬分叉协商机制首次在 Berlin 升级使用，并避免了一次大型失误，细节将在后文中介绍。

### 代表性硬分叉解读

历次硬分叉背后蕴含着一些代表性事件，颇具戏剧性，包括 DAO 分叉、上海 DOS、双堡奇缘和拆弹危机。

#### DAO 分叉

DAO 分叉事件是以太坊发展过程中最为深远的一次事件。由于 the DAO 的智能合约被黑客攻击，约 360 万 ether 被黑客盗走，但有 28 天的冻结时间。在这期间，借助 Carbonvote ，持币者表达意愿，以太坊基金会[决定](https://blog.ethereum.org/2016/07/15/to-fork-or-not-to-fork)将这部分资金转移到新的智能合约，允许投资者提款。此次分叉产生了 Ethereum Classic，也引发了大量的社会争论。

#### 上海 DOS

在 Devcon 2 期间，以太坊核心开发者们齐聚上海，但以太坊网络却遭遇了大量的网络流量[攻击](https://blog.ethereum.org/2016/09/22/ethereum-network-currently-undergoing-dos-attack)，造成了拒绝服务(DOS)。由于 EXTCODESIZE 操作码所消耗的实际系统资源远高于攻击者所需支付的手续费，攻击者反复调用该操作码，造成全网大多数节点无法追上最新区块。开发者们一面协调矿池和全节点启用受影响较小的 Parity 客户端，一面协商降低区块 gas(从 5 M 降低至 1.5 M)。最终，借助 Tangerine Whistle 和 Spurious Dragon 两次硬分叉调整了相关操作码的价格，并做了状态清理，才缓解了 DOS 攻击的影响。这次硬分叉还带来了后续影响，由于对 EIP-161(纳入在 Spurious Dragon 中)的实现不当(Go-ethereum 和 Parity 各自错误地做了实现)，造成了共识分叉。

#### 双堡奇兵

你也许会好奇为什么在 7280000 高度会有「君士坦丁堡」和「彼得堡」两个分叉，仔细观察会发现两者的差别就在于「彼得堡」移除了 EIP-1283。

根据 [ChainSecurity 的报告](https://medium.com/chainsecurity/constantinople-enables-new-reentrancy-attack-ace4088297d9)，EIP-1283 会为部分合约引入重入攻击的风险。TrailOfBits 给出了[更详尽的分析](https://github.com/trailofbits/publications/blob/master/reviews/EIP-1283.pdf)并提供了可能受影响的合约列表。在硬分叉激活前 32 小时，以太坊基金会发文提醒节点升级或降级以推迟君士坦丁堡升级，随后发布新版本引入彼得堡硬分叉，客户端需要将「双堡」配置在同一块高或禁用君士坦丁堡硬分叉。

#### 拆弹危机

为什么 Muir Glacier 和 Istanbul 两次硬分叉之间只有 26 天，这是因为核心开发者们错误计算了难度炸弹的爆炸时间，导致在 Istanbul 中未纳入推迟难度炸弹的提案。等到发现难度炸弹即将要对网络产生影响时，[第 76 次核心开发者会议](https://github.com/ethereum/pm/blob/master/AllCoreDevs-Meetings/Meeting%2076.md)迅速接受了 EIP-2384，并纳入到 Muir Glacier 硬分叉中。

### 硬分叉决策流程变更

硬分叉是如何决定的？实际上以太坊长期缺少成文文档，更多依赖「社会共识」（如果我错了请改正）。EIP-233 试图规范分叉的正式流程，但并未被接受。

尽管本文无法展现以太坊社区对硬分叉决策流程的讨论，但以太坊的硬分叉决定流程显然发生过变化。在 Berlin 硬分叉之前，开发者首先确定硬分叉的时间，再决定要纳入哪些 EIP，确定之后再进行实现和测试。Berlin 前的每次硬分叉都是一个 Meta EIP，例如 Istanbul 硬分叉通过 [EIP-1679](https://eips.ethereum.org/EIPS/eip-1679) 定义(简称 HFM-1679)。

Martin Holst Swende 提出了 [EIP 为中心的硬分叉流程](https://ethereum-magicians.org/t/eip-centric-forking/3536)，其核心观点是将 EIP 的接受与硬分叉剥离，核心开发者聚焦于单个 EIP 的认可、实现和测试，当单个 EIP 被接受后，后续的硬分叉可选择纳入该 EIP。尽管在写作过程中尚未找到该流程是如何被以太坊核心开发者接受的，但是 Berlin 硬分叉弃用了 [HFM-2070](https://eips.ethereum.org/EIPS/eip-2070)，而是[采纳](https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md)了 Martin 提出的流程。

决策流程的变更很快就发挥了作用，在 Berlin 硬分叉测试网激活前两周，围绕 EIP-2315 的废留，开发者们展开了激烈的争论并最终移除了 EIP-2315 。由于新流程的采纳，最后时刻的变更并未对硬分叉造成太大影响，并最终按期进行。更多细节可参考本人撰写的《[移除EIP-2315:以太坊柏林升级前的紧急刹车](https://mirror.xyz/0xbeC73ba0817403cd11C11bE891D671EA30443562/bqXW83iwA68sVWrX7K0SP2V8L0mAgB72vkcHR6Xo6Jc)》

### 不是改变的改变

值得一提的是，以太坊的区块空间上限(Block gas limit)并非共识的一部分。矿工有权更改区块空间上限，每个区块的上限变化最多为 0.1%。不去硬编码这个数值主要是为了避免潜在的攻击风险。该数值变化的历史可参见 MyCrypto 撰写的[研究报告](https://blog.mycrypto.com/the-history-of-ethereums-block-size-block-gas-limit)。

EIP 深入分析
--------

这些 EIP 对以太坊究竟造成了什么影响，又是谁影响着以太坊的决策呢。本文整理了以太坊硬分叉中纳入的所有 EIP(不包含元提案)，详见链接。 [https://primitiveslane.notion.site/cbd45cf0289c414b86715b8a9e71b28e?v=838baf2ac3a442a6b3fa03e58c5dc0ae](https://primitiveslane.notion.site/cbd45cf0289c414b86715b8a9e71b28e?v=838baf2ac3a442a6b3fa03e58c5dc0ae)

### EIP 都在做什么

纳入硬分叉的 EIP 的类型主要包含计费模型变更、新操作码引入、难度炸弹、经济模型、预编译、安全考虑等。

#### 计费模型变更

计费模型变更是数量最多的 EIP，共有 10 个，占 25.6%。其通过增减操作码的单价，以平衡 gas 消耗和实际占用系统资源的不匹配，可消除系统被 DOS 攻击的隐患，或是有利于特定类型应用的部署。一部分 DOS 隐患是因为对系统资源的错误估计造成的，EIP-150 和 EIP-160 解决了这个问题；而另一部分 DOS 隐患则是系统的固有特性，伴随着状态数据增长，访问状态数据的实际资源消耗也随之增长，因此需要定期调整相应操作码的单价，典型的有 EIP-1884(我认为写得最好的 EIP 之一)。

由于以太坊的状态表示模型，计费模型必须定期调整，这是特性，而非故障。

#### 新操作码引入

共有 9 个 EIP 为以太坊虚拟机引入了新的操作码(不含 EIP-1884)，占 23.08 % 。其中比较有影响力的包括 EIP-7 (DELEGATECALL), EIP-1014(CREATE2), EIP-3198(BASEFEE) 等。

#### 难度炸弹

共有 6 个 EIP 在推迟难度炸弹，占 15.4 % 。有 2 次硬分叉伴随着经济模型的调整，即降低新区块奖励。其中有 3 次仅为了推迟难度炸弹而实施的分叉，即名字中带有 Glacier 的硬分叉。有关难度炸弹的历史，可见[原语里弄的报告](https://github.com/PrimitivesLane/Research/blob/main/%E9%9A%BE%E5%BA%A6%E7%82%B8%E5%BC%B9%E7%AE%80%E5%8F%B2.pdf)。

#### 经济模型

有 4 个 EIP 对经济模型有影响，2 个降低了新区块奖励，1 个调整了难度计算模型(EIP-100)，此外引发较多争议的 EIP-1559 调整了手续费市场。

#### 预编译

有 4 个 EIP 将一些密码学操作预编译，因此无需在 EVM 中执行，提升运行效率。包括 EIP-152, EIP-196, EIP-197, EIP-198。

#### 安全考虑

有 3 个 EIP 是出于安全考虑的，包括重放攻击保护(EIP-155)，状态前缀树清理(EIP-161)，合约代码大小限制(EIP-170)。

#### 其它

其它 EIP 还包括网络协议改进(EIP-8)，增加交易执行状态接口(EIP-658)，增加新的交易类型(EIP-2718)等。此外，EIP-2 包含了 Homestead 的所有更新，较为复杂。

### 谁为 EIP 做贡献

共有 43 人，77 人次参与了这些 EIP 的撰写，其中参与 2 个以上(含） EIP 的作者有 11 个。Vitalik Buterin 参与撰写的最多，共 17 个，占 43.6 %。Martin Swende 和 Christian Reitwiessner 各参与 5 个，Alex Beregszaszi 和 James Hancock 各参与 3 个，其余人参与的个数均不超过 2 个。

Martin Swende 是以太坊基金会安全负责人，Go-ethereum 工程师，EIP 为中心的硬分叉流程的提出者；Christian Reitwiessner 是 Solidity 语言的发明者。

另外有趣的是在新的硬分叉流程实施之后，EIP 作者的数量发生了显著变化，这或许说明新的流程提高了 EIP 的参与度。Berlin之前，28 个 EIP，43 人次，平均每个 EIP 有 1.54 个作者；Berlin之后，11 个 EIP，34 人次，平均每个 EIP 有 3.09 个作者，增长了一倍。

总结
--

本文介绍了以太坊历史上的历次硬分叉，解读了几次硬分叉背后的代表性事件，并详细描述了以太坊硬分叉决策流程向 EIP 为中心的转变。文章还深入分析了历次硬分叉中的 EIP 所发挥的作用以及他们背后的贡献者。

以太坊即将迎来新的时代，往事涌上心头，读者朋友，你有何感受？

延展阅读
----

[https://github.com/ethereum/execution-specs](https://github.com/ethereum/execution-specs) [https://github.com/ethereum/go-ethereum/blob/d10c28030944d1c32febba3f45ae8c175ab34063/params/config.go](https://github.com/ethereum/go-ethereum/blob/d10c28030944d1c32febba3f45ae8c175ab34063/params/config.go) [https://www.ethereum.cn/the-history-of-ethereum-hard-forks](https://www.ethereum.cn/the-history-of-ethereum-hard-forks) [https://etherscan.io/chart/gaslimit](https://etherscan.io/chart/gaslimit) [https://github.com/ethereum/go-ethereum/pull/3341/files#r89548312](https://github.com/ethereum/go-ethereum/pull/3341/files#r89548312) [https://blog.ethereum.org/2019/01/15/security-alert-ethereum-constantinople-postponement](https://blog.ethereum.org/2019/01/15/security-alert-ethereum-constantinople-postponement) [https://ethereum-magicians.org/t/hardfork-meta-eip-2070-berlin-discussion/3561](https://ethereum-magicians.org/t/hardfork-meta-eip-2070-berlin-discussion/3561) [https://notes.ethereum.org/@holiman/S1ELAYY7S?type=view](https://notes.ethereum.org/@holiman/S1ELAYY7S?type=view)

分享协议
----

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。

---

*Originally published on [freeyao](https://paragraph.com/@freeyao/GFDbZiEyJZYCPMPokCnr)*
