ETH原理 - 1.共识与挖矿机制

2026-01-18ETHWeb3基础

以太坊的目标不仅是做“去中心化的货币”,更是要做“去中心化的计算平台”。这就要求它在一个开放的点对点网络中,既要像比特币那样保证状态的一致性和安全性,又要支持更快的出块时间和复杂的智能合约执行。

共识机制和挖矿机制,是以太坊实现这一目标的核心。以太坊从早期的工作量证明(Proof of Work, PoW)起步,经历了 Ethash 算法、GHOST 协议、难度炸弹等一系列设计与演化,最终在 The Merge 升级后,完全切换到了权益证明(Proof of Stake, PoS)。

本文只聚焦这条演化线:以太坊是如何通过 PoW 挖矿来达成共识,又是如何一步步迁移到 PoS 的,以及背后关键算法和设计取舍。

 

概览

以太坊在不同阶段采用过两套共识思路:

  • PoW 时代:
    • 使用 Ethash 作为挖矿算法,通过算力竞争来选出新区块。
    • 出块时间大约 12–15 秒,远短于比特币的 10 分钟,导致分叉更频繁。
    • 采用GHOST 协议和叔块奖励来减轻频繁分叉带来的中心化偏向。
    • 引入难度炸弹,为未来从 PoW 迁移到 PoS 埋下“倒计时”。
  • PoS 时代:
    • 共识由质押 ETH 的验证者驱动,不再依赖大量算力和电力消耗。
    • Beacon 链负责共识和随机性,原来的 PoW 链变成执行层,专注于智能合约执行。
    • 采用 LMD-GHOST 作为分叉选择规则,Casper FFG 提供终局性(finality),配合奖励与惩罚机制保证安全性。

 

理解这两套机制的关键,是搞清楚三件事:

1.谁来提出新区块(proposer / miner)? 2.全网如何决定哪条链是“主链”(fork choice)? 3.系统如何奖励诚实参与者、惩罚恶意行为?

 

区块与三棵树

在深入共识和挖矿之前,先把以太坊“区块里到底装了什么”讲清楚,这会反复出现在后面的内容里。

从抽象上看,以太坊是一个带记账本的状态机

  • 状态机的状态是“所有账户和合约当前的状态”;
  • 输入是一批交易(转账、合约调用、部署合约等);
  • 状态机执行完这批交易之后,从旧状态转移到新状态。

 

为了让所有节点都能验证“这条状态转移历史是不是合法的”,每个区块都会在区块头里记录三棵关键的树的根哈希(Root):

-状态树(stateRoot)

  • 记录“世界状态”,即所有账户和合约的状态。
  • 每个账户包含四个核心字段:nonce(账户已经发出的交易次数)、balance(余额)、codeHash(合约字节码的哈希)、storageRoot(该合约存储的根哈希)。
  • 这些账户被组织成一棵Merkle-Patricia Trie(MPT,一种把键值映射存成前缀树,并在每个节点加上哈希的结构),节点哈希通常使用 Keccak-256。MPT 的结构和实现细节会在后面的文章专门讲解,这里先有个整体印象即可。 -交易树(txRoot)
  • 本区块中所有交易按顺序编号,作为 key 写入另一棵 MPT,value 是交易的 RLP 编码。
  • 这棵树的根叫 txRoot,证明“这个区块里到底有哪些交易,以及它们的顺序”。 -收据树(receiptsRoot)
  • 每笔交易执行完之后会生成一条 receipt(收据),记录执行结果、消耗的 gas、日志(logs)、日志布隆过滤器(logsBloom,快速筛选“哪些区块里可能包含我关心的日志事件)、状态是否成功等。
  • 所有收据也按顺序写入一棵 MPT,根哈希是 receiptsRoot

 

和比特币相比:

  • 比特币区块头里只有一棵“交易 Merkle 树”的根哈希,代表本区块所有交易;
  • 以太坊则直接把“状态树 + 交易树 + 收据树”三棵树的根都放进区块头,描述更精细,也更方便后续做轻节点、日志过滤等功能。

从交易到区块头的大致流程可以画成这样:

flowchart LR
  T["交易列表"] --> E["EVM 执行"]
  E --> S["状态树 stateRoot"]
  E --> X["交易树 txRoot"]
  E --> R["收据树 receiptsRoot"]
  S --> H["区块头"]
  X --> H
  R --> H
  H --> POW["共识 / 挖矿"]

后面无论是 PoW 还是 PoS,都是在这个“已经包含三棵树根哈希的区块头”基础上做工作。

 

PoW 时代

Ethash 挖矿

以太坊早期采用工作量证明(PoW)共识,使用的挖矿算法叫Ethash。它和比特币的 SHA-256 PoW 在总体思路上类似——都是通过不断试 nonce 直到 hash 小于某个目标值——但在细节上做了很多改动。

设计目标(对比 BTC)

  • 比特币使用纯计算密集型的 SHA-256,两轮哈希即可,挖矿很快演化为 ASIC 主导; -Ethash 希望做到【这里是重点】
    • 通过memory-hard设计,让大容量、高带宽内存成为瓶颈,以此来抵制 ASIC;
    • 对轻节点友好:轻节点无需完整存储几 GB 的挖矿数据,也能验证某个 nonce 是否有效。

为此,Ethash 采用了 cache + DAG(dataset) 的结构

 

几个关键概念先记住:

  • epoch:区块高度被划分为若干个 epoch,每 30000 个区块一个 epoch;
  • seed:每个 epoch 对应一个 32 字节的 seed,用来生成 cache;
  • cache:约 16 MB 的数组,由 seed 生成,用来派生 DAG;
  • DAG(dataset):几 GB 级别的大数组,挖矿时会多次随机访问;
  • nonce:64 位随机数,由矿工自行选择和遍历,用来“尝试运气”,改变挖矿哈希的输入;
  • mixHash:挖矿过程中对多次 DAG 访问结果混合后的中间哈希,写入区块头,供验证者检查。

 

下面分步展开。

Epoch 与 seed 计算

对于任意区块高度 blockNumber

  • 先计算 epoch:epoch = floor(blockNumber / 30000)
  • 对于第 0 个 epoch,设定一个固定的初始 seed(例如 32 字节全 0);
  • 对于后续的第 n 个 epoch,seed 的计算可以理解为“在前一个 seed 上重复做 Keccak-256”:
    • seed_0 = 0x0000...00
    • seed_n = Keccak-256(seed_{n-1})

这样,每个 epoch 都有一个不可预先控制的 seed,任何节点只要知道 epoch 编号,都可以本地计算出该 epoch 的 seed。

 

Cache 的生成

有了 seed,就可以生成约 16 MB 的 cache。可以粗略理解为:

  1. 先用 Keccak-512 连续哈希 seed,生成一串 64 字节的块,填满整个 cache:
    • i 个位置的初始值:cache[i] = Keccak-512(seed || i)
  2. 再对整个 cache 做多轮“搅拌”:
    • 每一轮中,每个 cache[i] 都会根据自身和其它位置的值重新计算,例如:
      • 取前一个元素 cache[(i-1) mod N]
      • 再根据当前值算出一个索引,从 cache 的另一个位置取一个元素;
      • 把这些数据按位异或(XOR,一种简单的比特运算)拼在一起,再过一轮 Keccak-512,作为新的 cache[i]

这样,每个 cache[i] 都依赖于 seed 和cache 内其他条目,想伪造其中一小部分几乎不可能,必须整体按照规则生成。

 

DAG 的生成

DAG(也叫 dataset)是挖矿时真正要频繁访问的大数组,它的体积远大于 cache(GB 级 vs MB 级)。生成过程大致是:

  1. 对于第 i 个 DAG 元素,先从 cache 中取一个“起点”:
    • 例如 data = Keccak-512(cache[i mod cacheSize] XOR i)
  2. 然后进行多轮混合,每一轮都:
    • 根据当前 data 计算一个索引,从 cache 中取另一个元素;
    • 使用一种简单但高效的函数(FNV hash,一种快速的非密码学哈希函数)把 data 和这个元素混合在一起;
  3. 多轮混合后,对最终的 data 再做一次 Keccak 哈希,得到固定长度的 DAG 节点。

整个 DAG 的每个元素都以这种方式生成,矿工通常会:

  • 在挖矿前生成整个 DAG,存放在显存 / 内存中;
  • 轻节点则不存 DAG,而是当需要验证某个挖矿结果时,根据 cache按需生成对应的 DAG 元素即可。

 

挖矿 puzzle:Hashimoto 流程

有了 DAG,Ethash 的挖矿流程(Hashimoto)大致如下:

  1. 矿工先构造好区块头(包括父块 hash、三棵树根哈希 stateRoot/txRoot/receiptsRoot、时间戳、难度等,不含 noncemixHash)。
  2. 选择一个 64 位的候选 nonce,把区块头和 nonce 拼起来,做一次 Keccak-256,得到初始的 headerHash
    • headerHash = Keccak-256(blockHeader || nonce)
  3. headerHash 初始化一个称为 mix 的状态向量,然后进行多轮循环:
    • 每一轮用 FNV hash 从 DAG 中选出一个位置:index = FNV(mix, round) mod dagSize
    • 读取 DAG[index],再用 FNV 把它和 mix 混合:mix = FNV(mix, DAG[index])
    • 如此往复循环 64 轮
  4. 循环结束后,压缩 mix 得到 mixHash,再对其做一次 Keccak-256 得到最终结果 hash:
    • mixHash = Compress(mix)
    • result = Keccak-256(mixHash)
  5. 检查 result 是否小于当前难度目标 target
    • 如果 result < target,则认为挖矿成功,把 noncemixHash 写入区块头并广播;
    • 否则就换一个新的 nonce 继续从第 2 步开始尝试。

在这个过程中:

  • Keccak 哈希提供了密码学上的抗篡改和不可预测性;
  • FNV 提供了快速混合和索引计算,强调内存访问顺序的不可预测;
  • 大量对 DAG 的随机访问,使得挖矿瓶颈在内存带宽,而不只是纯算力。

 

nonce 的角色(顺带澄清账户 nonce)

在 Ethash 挖矿里提到的 nonce 是“区块头的 PoW 随机数”,与账户里的 nonce 完全不同:

-账户 nonce:表示某个外部账户已经发出了多少笔交易,防止重放攻击(这点在你原始笔记“重放攻击”那一节会详细展开); -区块 nonce:只在 PoW 时代存在,用于挖矿 puzzle,矿工可以在合法范围内任意选择和遍历,用来改变区块头哈希的输入,从而“摇彩票”。

 

矿工在尝试时可以:

  • 顺序递增 nonce(0,1,2,3,…);
  • 或者随机挑选 nonce
  • 甚至在 nonce 空间用完时(2^64 次尝试)调整区块里的其它可调字段(例如 coinbase 交易中的 extra nonce)来继续搜索。

 

总结

和比特币“先挑选交易、先算 PoW、后执行验证”的观感不同

以太坊的挖矿流程总结如下:

  1. 矿工从本地的交易池(mempool)中挑选一批尚未打包的交易(通常会优先选择 gas price 较高的交易,以获得更多手续费)。每个节点在挖矿期间的交易池内容可能略有不同,这不要紧,因为真正写进区块的“那一批交易”以后会成为共识的一部分。
  2. 在本地按固定顺序执行这些交易:
    • 使用 EVM 按字节码规则执行合约;
    • 更新世界状态树(stateRoot),并记录每笔交易对应的收据(写入 receiptsRoot),并把每笔交易写入交易树(txRoot);
    • 如果交易结构是合法的,但是在执行过程中触发了 revert(例如 require 条件不满足、余额不够转账等),那么这笔交易仍然会被打包进区块
      • 状态修改会回滚到执行前的样子;
      • 已经消耗掉的 gas 不会退还,收据里会标记执行失败。
    • 之所以这样设计,是为了防止 DoS 攻击,让攻击有代价:如果执行失败的交易不消耗 gas,攻击者就可以大量发送“必定失败”的交易,使得正常交易无法执行。
  3. 计算出三棵树的根哈希 stateRoottxRootreceiptsRoot,连同父块哈希、时间戳、难度、gasLimit、gasUsed 等字段一起组成区块头(还缺 noncemixHash)。
  4. 在这个固定的区块头上运行 Ethash 挖矿,遍历不同的 nonce,直到找到一个使结果 hash 小于 target 的解。
  5. 一旦某个节点 PoW 成功,区块里的交易和状态已经完全确定,可以立即广播;其他节点收到区块后,按同样顺序执行这些交易,并重建三棵 MPT,检查根哈希是否一致,就能验证区块是否正确

 

与比特币 PoW 的差异

  • 从算法结构上看:比特币是“两轮 SHA-256 + 简单拼接”,Ethash 是“Keccak + DAG + FNV + 多轮随机内存访问”;
  • 从硬件要求上看:比特币主要拼算力,Ethash 还需要看内存带宽和容量;
  • 从数据结构上看:比特币只对本区块交易做一棵 Merkle 树,Ethash 挖的是“已经绑定了三棵 MPT 根哈希的区块头”;
  • 从轻节点友好性上看:两者都可以做 SPV,Ethash 多了一层“cache + 按需生成 DAG 元素”的设计,方便轻节点验证挖矿结果。

 

难度调整

以太坊希望保持大约 12–15 秒的出块时间,从而比比特币有更快的确认速度、更好的交互体验。

以太坊的难度是区块级别动态调整的,每个新区块的 difficulty 都是根据父块的信息计算出来的,而不是矿工随便填的。

忽略难度炸弹时,可以把难度调整公式粗略理解为:

newDifficulty=parentDifficulty+parentDifficulty2048×adjFactor\text{newDifficulty} = \text{parentDifficulty} + \frac{\text{parentDifficulty}}{2048} \times \text{adjFactor}

其中:

adjFactor=max(yΔt10,99)\text{adjFactor} = \max\bigl(y - \frac{\Delta t}{10},\,-99\bigr)

Δt=timestampcurrenttimestampparent\Delta t = \text{timestamp}_{\text{current}} - \text{timestamp}_{\text{parent}} 是父子块之间的时间间隔(秒);

yy 与父区块的 uncle 数有关,下面会讲什么是 uncle 区块,如果父区块中包括了 uncle,则 y 为 2,否则 y 为 1

直观理解:

  • 如果出块间隔 Δt<10\Delta t < 10 秒,那么 1Δt/10>01 - \Delta t/10 > 0 (不含叔块),adjFactor 为正,难度会略微上调
  • 如果 Δt>10\Delta t > 10 秒,adjFactor 为负,难度会下调,出块会变得稍微容易一点;
  • 下调幅度有一个下限 99-99,也就是“每个区块最多只能把难度下调约 99/204899/2048 倍”,避免因某次时间戳异常导致难度瞬间暴跌。

 

和比特币的对比:

  • 比特币每 2016 个区块才整体调整一次难度,反馈周期在小时级甚至天级;
  • 以太坊则是每个区块都可以微调难度,对算力变化响应更快,更适合十几秒的高频出块节奏。

 

其他节点如何知道“新难度是多少”?

  • 新区块的 difficulty 字段写在区块头里,但它不是“矿工说多少就多少”;
  • 任意节点在验证新区块时,会拿到父块头和新区块的时间戳,自己在本地套同样的公式算出 expectedDifficulty
  • 如果新区块头里的 difficulty 和本地算出来的不一致,这个区块就会被视为非法、直接丢弃。

因此,难度调整逻辑本身就是共识的一部分,矿工如果不按规则来,就挖不出能被别人接受的区块。

 

难度炸弹

在常规难度调整之外,以太坊还引入了一个特殊的“难度炸弹”(Difficulty Bomb,也叫 Ice Age):

  • 在难度计算公式中,除了上面的“按出块时间微调”的那一项之外,还有一项随区块高度指数增长的“额外难度”;
  • 当区块高度足够高时,这个额外项会快速增大,使得总难度剧烈上升,出块时间越来越长,最终网络几乎停滞。

可以把炸弹项粗略写成:

bomb={2k2,k>00,k0\text{bomb} = \begin{cases} 2^{k - 2}, & k > 0 \\ 0, & k \le 0 \end{cases}

其中 kk 取决于区块高度:

k=blockNumberbombDelay100000k = \left\lfloor \frac{\text{blockNumber} - \text{bombDelay}}{100000} \right\rfloor
  • bombDelay 是一个“偏移量”,用来控制炸弹真正开始生效的高度;
  • 每经过 100000 个区块,kk 增加 1,炸弹项就大约翻一倍。

 

以太坊社区为了延缓难度炸弹生效,曾经把实际用于计算的区块号往后调过三百万个,这样在计算 kk 时,就相当于“假装链还没走那么远”,炸弹项暂时回到 0 或者很小的值。随着时间推移, blockNumber\text{blockNumber} 继续增长,炸弹又会慢慢变强,如果社区再次想继续使用 PoW,就需要再做一次升级推迟它。

从治理角度看:

  • 没有难度炸弹,社区在“到底要不要切 PoS”这样的议题上可能会无限拖延;
  • 有了炸弹,如果不升级,出块时间会越来越长、用户体验越来越差,大家就有动力转成PoS;
  • 也是防止转成 PoS 之后,还有矿工继续要用旧有 PoW 的机制挖矿

 

GHOST 共识

由于以太坊出块很快,区块在网络中的传播需要时间,多个矿工几乎同时挖出新区块是常态,这会自然产生临时分叉。

如果像比特币那样只采用简单的“最长链规则”,会有两个问题:

1.个体矿工更容易吃亏

  • 当一个个人矿工和一个大型矿池几乎同时挖出区块时,由于矿池拥有更多算力,它更容易在自己的那条链上快速接上下一块,使这条链成为最长链。
  • 个人矿工挖出的那条分支会更容易变成孤块,工作量白费。 2.算力中心化偏向(centralization bias)
  • 长期来看,大矿池的收益更稳定,小矿工不划算,大家会越来越倾向加入大矿池,导致算力集中。

GHOST

 

为缓解这个问题,以太坊引入了GHOST 共识协议,让这些废弃的区块也能够获得一些安慰的奖励

不只看哪条链“最长”,而是看哪条链背后承载的“有效工作量”最多,即哪条链的子树(包含主链和旁枝)的总工作量更大。

  • 给输在分叉竞争中的区块一定奖励(叔块奖励),把它们的工作量部分计入主链的安全性;
  • 对包含叔块的主链区块也给一点额外奖励,鼓励矿工尽快把看到的叔块“收编”进主链。

 

叔块规则

  • 当两个区块几乎同时接在同一个父块后,形成分叉,最终只有一条分支会成为主链,另一条分支上的区块就变成了“叔块候选”。
  • 新的主链区块可以在自己的区块头中引用这些“叔块候选”,每个新区块最多可以包含 2 个叔块。
  • 只有被主链区块引用的叔块,才会获得奖励;没被引用的就和普通孤块一样,只能算白挖了。

 

奖励方面:

  • R 为当时的基础出块奖励

  • k = B.number − U.number(叔块与包含它的主链区块之间的高度差)

  • 叔块本身可以获得接近完整出块奖励的大部分(约 7/8),并且跨度越远奖励越少,只有高度差不超过 6(k=1..6)的叔块才可被包含并获奖。

  • 奖励公式为: UncleReward=8k8R(k=1,2,3,4,5,6)\text{UncleReward} = \frac{8 - k}{8} \cdot R \quad (k = 1,2,3,4,5,6)

  • 引用叔块的新区块也能获得少量额外奖励,鼓励矿工积极把叔块包含进来,每包含 1 个叔块,可额外获得 R / 32

  • 每个区块最多可包含 2 个叔块,一个叔块只能被包含一次

  • 只有分叉之后的第一个区块才能获得uncle reward,后面的区块都没有奖励。

    • 如果不这样设计,而是给侧链上的每个区块都发奖励的话,那么分叉攻击的代价就会很低
    • 因为分叉攻击不成功,它们整条侧链的节点也能都够得到一些补偿,还不如去冒险发动攻击,成功了的话反而能够得到一大笔钱。

 

这种设计的效果是:

  • 对个体矿工更友好:哪怕最终输在了分叉竞争中,只要自己的区块被包含为叔块,也能拿到大部分奖励;
  • 减轻中心化偏向:大矿池不再在分叉竞争中拥有过于压倒性的优势,小矿工的“白挖风险”被降低。

 

叔块上链过程

假设主链高度这样增长(从左到右是时间):

时间 →
主链:  B0 ─ B1 ─ B2 ─ B3 ─ B4 ─ B5 ─ B6 ─ B7 ─ B8

假设在 B2 之后:

  • 矿工 A 挖出了 B3
  • 几乎同时,矿工 C 也挖出了一个区块 U3
        B2
         │
   ┌─────┴─────┐
  B3           U3   ← 合法区块,但没赢主链

此时:

  • B3 成为主链
  • U3 成为叔块候选(ommer candidate) -U3 已经存在了,但还没拿到任何奖励

 

后续区块继续正常出块,主链继续往前:

主链: B3 ─ B4 ─ B5 ─ B6

U3 一直“躺在网络里”,每个新矿工都有资格把 U3 引用进自己的区块

假设在挖 B6 时,矿工 D 做了两件事:

  1. 构造新区块 B6
  2. B6 的区块头里填上:
    • ommers = [U3]

于是关系变成:

主链: B3 ─ B4 ─ B5 ─ B6
                        ↘
                          U3

此时高度差 k=B6.numberU3.number=3k = B6.number - U3.number = 3 ,发生三件事:

1.U3 被正式承认为叔块 2.U3 的矿工立刻获得 (8-3)R/8 的叔块奖励 3.B6 的矿工获得 R/32 的 inclusion reward

 

PoW 到 PoS 过渡

为了实现向 PoS 的平滑迁移,以太坊社区采取了"分阶段、双链并行"的策略:

研发阶段(2014-2020)

  • 早在 2014 年,Vitalik Buterin 等人就开始研究 PoS 共识,提出了 Casper 系列协议;
  • 2017-2018 年,社区逐步将 Casper 设计细化为两部分: -Casper FFG(Friendly Finality Gadget):提供终局性保证,确保已确认的区块不可逆; -LMD-GHOST:提供分叉选择规则,决定哪条链是当前的主链。
  • 2019 年底,以太坊 2.0(后改名为"共识层升级")的规范逐步冻结,Beacon 链的设计基本成型。

Phase 0:Beacon 链启动(2020 年 12 月)

  • 2020 年 12 月 1 日,Beacon 链正式启动,标志着以太坊 PoS 的实质性开始;
  • 此时 Beacon 链是一条完全独立的链,与原 PoW 主链并行运行,有自己的创世状态和区块;
  • Beacon 链只负责:
    • 管理验证者的注册、激活、退出;
    • 运行 PoS 共识(LMD-GHOST + Casper FFG);
    • 生成随机数(RANDAO)并为未来的 slot 分配 proposer 和委员会; -不执行任何用户交易,不运行 EVM,不维护账户状态

 

如何成为 Beacon 链验证者

在 PoW 主链上,有一个特殊的智能合约叫Deposit Contract:

  • 任何人都可以调用这个合约,向其中存入至少 32 ETH;
  • 存款时需要提供:
    • 一个 BLS 公钥(用于 Beacon 链上的签名和投票);
    • 提款凭证(withdrawal credentials,用于未来取回质押);
    • 对这些信息的 BLS 签名,证明私钥持有者确实同意质押。
  • 一旦存款交易在 PoW 链上被确认,Beacon 链会监听这个合约的事件,把新的验证者登记到自己的候选队列中;
  • 候选验证者按先后顺序排队,逐步被激活为"活跃验证者",开始参与出块和投票。

这种设计的巧妙之处在于:

  • PoW 主链负责"锁定 ETH"这个经济行为,利用已有的链上状态和共识;
  • Beacon 链负责"用这些质押来驱动新的 PoS 共识",两条链各司其职,互不干扰。

 

双链并行运行

在长达近两年的时间里,以太坊网络处于"双链共存"的状态:

PoW 主链(执行层)

  • 继续使用 Ethash 挖矿,矿工提出新区块;
  • 所有用户交易、智能合约执行、状态更新都在这条链上进行;
  • 区块头中依然包含 stateRoottxRootreceiptsRootnoncemixHash 等字段;
  • 难度炸弹被多次推迟,以便给 Beacon 链的成熟争取时间。

Beacon 链(共识层)

  • 按照 PoS 规则每 12 秒左右产生一个 slot;
  • 验证者按 RANDAO 随机性被分配为 proposer 和委员会成员;
  • 每个 slot 的 proposer 提出一个 Beacon 区块,包含:
    • 对上一个 slot 的投票(attestations);
    • 新的验证者存款、退出、slashing 等共识消息;
    • RANDAO reveal(用于生成随机数); -不包含任何用户交易
  • Beacon 链的区块和状态完全独立于 PoW 主链,两者唯一的联系就是 Deposit Contract。

这种"分而治之"的设计让社区可以:

  • 在真实环境中测试和完善 PoS 共识,积累验证者运营经验;
  • 观察 Beacon 链的稳定性、终局性、抗攻击能力;
  • 为最终的合并做好充分准备,同时不影响主网用户的日常使用。

 

The Merge

经过近两年的并行运行和多次测试网演练,以太坊在 2022 年 9 月 15 日完成了历史性的The Merge(合并)

技术上如何合并

合并的核心是让 PoW 主链"停止自己的共识,接受 Beacon 链的共识":

1.Terminal Total Difficulty(TTD)

  • 以太坊协议预先设定了一个"终端总难度"值,大约对应 2022 年 9 月中旬;
  • 当 PoW 主链的累积总难度首次达到或超过这个 TTD 时,PoW 共识立即停止:
    • 矿工不再继续挖矿;
    • Ethash 算法、nonce、mixHash 等字段被废弃;
    • 难度炸弹不再需要,因为链已经彻底切换到 PoS。

2.执行层与共识层的"插拔式"对接

  • 在合并前,客户端软件已经被重构为两部分: -执行客户端(Execution Client):负责 EVM 执行、状态管理、交易池,对应原来的 PoW 客户端(如 Geth、Nethermind); -共识客户端(Consensus Client):负责 PoS 共识、验证者管理、分叉选择,对应 Beacon 链客户端(如 Prysm、Lighthouse)。
  • 两个客户端通过一个标准的 JSON-RPC 接口(Engine API)通信:
    • 共识客户端告诉执行客户端"现在应该基于哪个父区块构造新区块";
    • 执行客户端执行交易、计算状态转移,把结果返回给共识客户端;
    • 共识客户端把这个结果包装成 Beacon 区块,广播并收集投票。

3.区块结构的变化

  • 合并后,以太坊区块分为两部分: -Beacon 区块(Consensus Layer):包含 slot、epoch、proposer 索引、attestations、RANDAO、Casper FFG 投票等; -执行负载(Execution Payload):包含原 PoW 区块头的大部分字段(stateRoottxRootreceiptsRootgasUsed 等)和交易列表,但不再有 noncemixHash
  • 每个 Beacon 区块都嵌入一个执行负载,代表"这个 slot 里执行层发生的状态变化"。

对用户的影响

从最终用户角度看,合并前后几乎感觉不到变化:

  • 所有账户余额、合约代码、合约存储、历史交易记录完全保留;
  • 发送交易、调用合约、部署合约的方式和 API 完全不变;
  • Gas 费用、交易确认时间在短期内也基本保持不变(出块时间仍然约 12 秒)。

真正改变的是底层:

-谁来提出新区块:从"算力最强的矿工"变为"被 RANDAO 随机抽中的验证者"; -如何达成共识:从"PoW 最长链"变为"PoS 的 LMD-GHOST + Casper FFG"; -安全性来源:从"算力很贵"变为"质押会被罚没"; -能耗:The Merge 使以太坊能耗下降了约99.95%,从 20+ TWh/年降至几乎可以忽略不计。

 

小结

回顾整个过渡过程,可以看出以太坊社区的几个关键考量:

-稳妥优先:通过 Beacon 链独立运行近两年,充分测试 PoS 共识的稳定性,避免直接在主网上做"大爆炸式"升级; -向后兼容:执行层(原 PoW 链)的状态、交易格式、EVM 语义完全不变,只是共识层被替换; -社区共识:难度炸弹提供了"倒计时压力",让社区有明确的迁移动力; -分层架构:合并后的"执行层 + 共识层"分离设计,为后续的扩容和客户端多样性提供了更清晰的模块边界。

 

PoS 时代

Slot 和 Epoch

为了组织 PoS 共识,Beacon 链把时间划分为精确的时间单位:

Slot(时隙)

  • 每个 slot 约12 秒,是以太坊 PoS 的基本时间单位;
  • 理论上每个 slot 产生一个区块,但如果 proposer 离线或恶意不出块,这个 slot 可以是“空的”;
  • Slot 编号从创世开始递增:slot 0, slot 1, slot 2, ...

Epoch(时期)

  • 32 个连续的 slot组成一个 epoch;
  • 1 个 epoch 约6.4 分钟(32 × 12 秒);
  • Epoch 是 Casper FFG 处理终局性的基本单位,也是计算奖励和惩罚的周期。

时间线示例

Epoch N:
  Slot 0  [12s] proposer_A 出块 | 委员会_1 投票
  Slot 1  [12s] proposer_B 出块 | 委员会_2 投票
  Slot 2  [12s] proposer_C 出块 | 委员会_3 投票
  ...
  Slot 31 [12s] proposer_Z 出块 | 委员会_32 投票

Epoch N+1:
  Slot 32 [12s] ...
  ...

在每个 epoch 开始时:

  • 协议会为该 epoch 的每个 slot 预先分配好 proposer(从所有活跃验证者中随机抽取);
  • 把所有活跃验证者均匀分配到 32 个 slot 对应的 32 个委员会(每个 slot 一个委员会);
  • 这些分配基于 RANDAO 随机性,后面会详细讲解。

 

验证者与质押

在 PoS 时代,以太坊不再依赖算力挖矿,而是依赖验证者(validator)和质押的 ETH。

成为验证者

任何人都可以成为验证者,基本步骤如下:

1.准备质押

  • 至少需要32 ETH作为一个验证者单元的质押量;
  • 如果有更多 ETH,可以创建多个验证者(64 ETH = 2 个验证者,96 ETH = 3 个验证者,以此类推);
  • 也可以通过质押池(Lido、Rocket Pool 等)参与,但底层仍然是以 32 ETH 为单位的验证者。

2.生成密钥对

  • 需要生成两对密钥: -验证密钥(Validator Key):用于签名区块和 attestations,必须始终在线; -提款密钥(Withdrawal Key):用于未来取回质押,可以离线冷储储。
  • 使用BLS 签名方案,支持签名聚合,下面有解释。

3.存入 Deposit Contract

  • 在执行层调用 Deposit Contract,向其中转入 32 ETH;
  • 提交验证公钥、提款凭证和签名;
  • 这笔交易一旦被执行层确认,Beacon 链会监听到该事件。

4.等待激活

  • 验证者首先进入“待激活队列”(activation queue);
  • 为了网络稳定,每个 epoch 只能激活有限数量的验证者(大约是当前总验证者数的 1/65536,每个 epoch 最多能激活 4 个);
  • 激活后,验证者状态变为“活跃”,开始参与共识。

5.持续运行

  • 验证者需要运行一个节点(包括执行客户端 + 共识客户端);
  • 在每个 epoch 中,验证者会被分配到不同的 slot 和委员会,负责投票或出块;
  • 只要按要求完成任务,就能获得奖励;如果离线或作恶,就会被惩罚。

6.退出和提款

  • 验证者可以主动发起退出,进入“退出队列”;
  • 退出后,验证者不再参与共识,但质押还需要等待一段时间(约 27 小时)才能提取;
  • 提款功能在 The Merge 之后的 Shanghai/Capella 升级(2023 年 4 月)中才正式启用。

 

验证者生命周期示例

以一个具体的验证者为例:

Slot 1000: Alice 向 Deposit Contract 存入 32 ETH
           ↓
Slot 1050: Beacon 链检测到存款,Alice 的验证者进入“待激活队列”
           ↓
Slot 1500: Alice 的验证者被激活,状态 = “活跃”
           ↓
Slot 1500-50000: Alice 持续执行以下任务:
                 - 每个 epoch 至少投票一次(attestation)
                 - 偶尔被选为 proposer,提出新区块
                 - 偶尔被选为聚合者(aggregator),聚合委员会签名
           ↓
Slot 50000: Alice 决定退出,发送 Voluntary Exit 消息
           ↓
Slot 50200: Alice 的验证者被标记为“已退出”,不再参与共识
           ↓
Slot 51000: 等待期结束,Alice 可以提取 32 ETH + 奖励 - 惩罚

 

随机性:RANDAO

要做到“随机选择 proposer 和委员会”,系统需要高质量的随机数。以太坊 PoS 使用的是基于RANDAO的随机性:

RANDAO 原理

RANDAO 的基本思想是“多方贡献随机值,混合后难以预测”:

1.每个 proposer 提交 reveal

  • 当验证者被选为 proposer 时,必须在自己的区块中包含一个RANDAO reveal;
  • 这个 reveal 是用验证者私钥对 epoch 编号的 BLS 签名:

reveal=BLS_Sign(validator_privkey,epoch)\text{reveal} = \text{BLS\_Sign}(\text{validator\_privkey}, \text{epoch})

2.混合所有 reveals

  • 在一个 epoch 内,所有 proposers 的 reveals 会被收集起来;
  • 协议把这些 reveals 通过 XOR(按位异或)混合,得到一个“混合随机数”:

randao_mix=reveal1reveal2...reveal32\text{randao\_mix} = \text{reveal}_1 \oplus \text{reveal}_2 \oplus ... \oplus \text{reveal}_{32}

3.生成 epoch 的随机种子

  • 每个 epoch 的 randao_mix 会被用来为未来的几个 epoch 生成随机种子;
  • 具体来说,epoch N 的 randao_mix 会影响 epoch N+2 和之后的 proposer/委员会分配;
  • 这样的延迟设计是为了防止 proposer 通过“不出块”来操纵随机数。

为什么这样设计

  • 单个验证者无法预测最终的 randao_mix,因为它依赖于所有 proposers 的 reveals;
  • 即使某个验证者想“不出块来影响随机数”,也会损失出块奖励,并且只能影响一个 bit 位,对整体随机性影响很小;
  • 只要绝大多数验证者诚实,单个验证者就很难预判“自己会在什么时候被选中、会跟谁分在一个委员会”,也就难以提前策划针对性的政击。

具体分配流程

Epoch N-2: 收集所有 proposer 的 RANDAO reveals
              ↓
           混合成 randao_mix_N-2
              ↓
Epoch N:   使用 randao_mix_N-2 作为种子
              ↓
           为 epoch N 的每个 slot 分配 proposer:
             - 用 hash(randao_mix, slot, "proposer") mod validator_count
             - 得到验证者索引,即为该 slot 的 proposer
              ↓
           把所有活跃验证者打乱并分配到 32 个委员会:
             - 用 hash(randao_mix, epoch, committee_index) 作为 shuffle 种子
             - 对验证者列表进行 Fisher-Yates shuffle
             - 平均分成 32 份,每份对应一个 slot 的委员会

 

BLS 签名与聚合

PoS 下,每个 epoch、每个 slot 的投票数量都非常多。以太坊主网有超过90 万个活跃验证者(2024 年数据),如果所有签名都单独发送和存储,带宽和存储会被打爆。为此,以太坊采用了BLS 签名与聚合技术:

BLS 签名的特性

  • BLS(Boneh-Lynn-Shacham) 是一种基于椭圆曲线配对的签名算法;
  • 最大的特点是签名可聚合(aggregatable):
    • 如果多个人对同一条消息签名,这些签名可以“加起来”形成一个聚合签名;
    • 验证时,只需要一次配对运算,就能同时验证整个聚合签名的有效性;
    • 公钥也可以聚合,聚合公钥对应聚合签名。

 

Attestation 的签名对象

在讲解聚合流程之前,先要明确验证者到底在签什么。

Attestation Data 结构

每个验证者在每个 epoch 中至少需要发出一次 attestation,包含以下数据:

AttestationData = {
    # 时间信息
    "slot": 12345,                    # 该 attestation 所属的 slot
    "index": 5,                       # 委员会编号(0-31)
    
    # LMD-GHOST 分叉选择用
    "beacon_block_root": "0xabc...",  # 验证者认为的当前链头区块的根哈希
    
    # Casper FFG 终局性用
    "source": {
        "epoch": 100,
        "root": "0x123..."            # 上一个已证明检查点的根哈希
    },
    "target": {
        "epoch": 101,
        "root": "0x456..."            # 当前 epoch 的 checkpoint 的根哈希
    }
}

验证者签名的是这整个 AttestationData 对象:

  1. 首先对 AttestationData 进行 SSZ 序列化(Simple Serialize,以太坊的标准序列化格式);
  2. 再对序列化结果计算 hash tree root,得到一个 32 字节的哈希值;
  3. 最后用 BLS 私钥对这个哈希值签名:

signing_root=hash_tree_root(AttestationData)\text{signing\_root} = \text{hash\_tree\_root}(\text{AttestationData}) signature=BLS_Sign(validator_privkey,signing_root)\text{signature} = \text{BLS\_Sign}(\text{validator\_privkey}, \text{signing\_root})

为什么要签这些字段

  • beacon_block_root:表明验证者认为哪个区块是当前的链头,用于 LMD-GHOST 分叉选择;
  • sourcetarget:表明验证者认为哪条链是合法的,用于 Casper FFG 终局性;
  • slotindex:证明这是哪个 slot 的哪个委员会的投票,防止重放攻击。

 

分叉选择:LMD-GHOST

PoS 下同样可能出现分叉:

  • proposer 未能在 slot 内及时广播区块,导致部分节点暂时继续在旧链头上构建,从而产生短暂分叉
  • 同一个区块在网络中传播不均,导致不同节点在同一时刻看到不同链头,形成视图不一致的分叉
  • 节点同步或验证速度差异使部分节点短时间内滞后于最新链头,从而跟随不同分支
  • 在区块刚被提议而投票尚未充分传播时,不同节点对权重优势的判断不同,可能引发极短暂分叉
  • 等等其他一系列可能出现分叉的情况

 

以太坊 PoS 采用两层机制来处理分叉并给出终局性保证:

-LMD-GHOST 分叉选择规则:决定当前的“主链头部”,哪个区块是 head; -Casper FFG 终局性机制:给出“最终确认”保证,确保已确认的区块不可逆。

先来看LMD-GHOST(Latest Message Driven GHOST),它是 PoW 时代 GHOST 协议在 PoS 下的演化版本。

基本思想

  • 每个验证者会周期性地对它认为的“最佳链头”投票(发出 attestation);
  • 每个投票带有权重,权重等于该验证者质押的 ETH 数量(每个验证者都是 32 ETH,所以权重相等);
  • 对于每个验证者,只看它最近一次投票(latest message),防止重复计数;
  • 从创世区块开始,沿着子树“累计投票权重”最大的子节点一直往下走,直到再往下没有子节点为止,这个叶子节点就是当前头区块(head)。

具体算法

假设当前区块树如下:

         A (justified checkpoint)
         │
    ┌────┼────┐
    B         C
    │         │
   ┌┼┐      ┌┼┐
   D E      F G

假设有 100 个验证者,他们的最新投票分布如下:

  • 40 个验证者投给 D
  • 20 个验证者投给 E
  • 30 个验证者投给 F
  • 10 个验证者投给 G

LMD-GHOST 算法执行步骤:

1.从 justified checkpoint A 开始

  • A 有两个子节点:B 和 C

2.计算每个子节点的总权重

  • B 的子树包括 D 和 E,总权重 = 40 + 20 =60
  • C 的子树包括 F 和 G,总权重 = 30 + 10 =40

3.选择权重最大的分支

  • B 的权重(60)> C 的权重(40),选择 B

4.继续在 B 的子节点中选择

  • D 的权重 = 40
  • E 的权重 = 20
  • 选择 D

5.D 没有子节点,结束

  • 当前 head =D

 

Attestation 的结构

每个 attestation 包含:

Attestation = {
    "slot": 12345,                    # 哪个 slot 发出的投票
    "committee_index": 5,             # 哪个委员会
    "beacon_block_root": 0xabc...,   # 投给哪个 beacon 区块(LMD-GHOST 用)
    "source": {                       # Casper FFG 用:上一个 justified checkpoint
        "epoch": 100,
        "root": 0x123...
    },
    "target": {                       # Casper FFG 用:当前 epoch 的 checkpoint
        "epoch": 101,
        "root": 0x456...
    },
    "signature": ...                  # BLS 签名
}
  • beacon_block_root:这是 LMD-GHOST 用来计算分叉选择的字段,指向验证者认为的当前链头;
  • sourcetarget:这是 Casper FFG 用来确认终局性的字段,后面会详细讲解。

效果

可以把它想象成:

  • 整棵区块树上,每条边都背着“支持这条分支的总质押”;
  • 共识规则总是沿着“权重最大”的路径向下走;
  • 只要大多数质押诚实并且按时投票,所有节点最终都会认为同一个区块是 head。

 

终局性:Casper FFG

仅靠分叉选择规则,链的头部仍然可能回滚。为了给出更强的“最终确认”保证,以太坊引入了Casper FFG(Friendly Finality Gadget):

基本概念

  • 每个 epoch 的第一个 slot 的区块被称为checkpoint 区块;
  • 验证者在每个 attestation 中,除了投票给 LMD-GHOST 的 head,还要同时投票给两个 checkpoint: -source checkpoint:上一个已被 justified 的 checkpoint; -target checkpoint:当前 epoch 的 checkpoint。
  • 这种投票可以理解为“我认为从 source 到 target 的这条链是有效的”。

 

Justified 和 Finalized

1.Justified(被证明)

  • 当某个 checkpoint 获得 ≥ 2/3 有效质押的投票时,它会被标记为justified;
  • 这意味着绝大多数验证者认为这个 checkpoint 是合法的。

2.Finalized(终局)

  • 如果一个 justified checkpoint 的直接后继 checkpoint也被 justified,则前者升级为finalized;
  • 用公式表示:如果 checkpoint B 在 epoch N,checkpoint C 在 epoch N+1,并且:
    • B 是 justified
    • C 也是 justified
    • C 的 source 指向 B
  • 那么 B 被 finalized。

具体例子

Epoch 98:  Checkpoint A (finalized)
              ↓
Epoch 99:  Checkpoint B (justified, source=A)
              ↓
Epoch 100: Checkpoint C (justified, source=B)  ← B 现在被 finalized
              ↓
Epoch 101: Checkpoint D (正在收集投票...)

步骤拆解:

  1. Epoch 99 时:
    • 验证者们投票 source=A, target=B
    • 当 B 获得 ≥ 2/3 投票,B 变为 justified
  2. Epoch 100 时:
    • 验证者们投票 source=B, target=C
    • 当 C 获得 ≥ 2/3 投票,C 变为 justified -同时,B 从 justified 升级为 finalized
  3. Epoch 101 时:
    • 验证者们投票 source=C, target=D
    • 当 D 获得 ≥ 2/3 投票,D 变为 justified -C 从 justified 升级为 finalized

终局性的保证

一旦某个 checkpoint 被 finalized:

  • 正常情况下,链不会再回滚到它之前;
  • 如果有人试图构造一条与已 finalized 区块冲突的分叉链,至少有 1/3 的质押必须在两条链上给出相互矛盾的投票,这部分质押会被 slash(罚没)。

 

数学保证

假设总质押量为 N:

  • 要让 checkpoint A finalized,需要 ≥ 2N/3 的投票;
  • 要让与 A 冲突的 checkpoint B 也 finalized,也需要 ≥ 2N/3 的投票;
  • 但是 2N/3 + 2N/3 > N,意味着至少有 N/3 的验证者必须在两条链上都投票;
  • 这些验证者会被检测到并被 slash,损失至少 N/3 的质押。

因此,攻击 finalized checkpoint 的经济代价非常高,这就是 PoS 终局性的安全性来源。

 

与比特币的对比

  • PoW(比特币):安全性来自攻击者必须维持 51% 算力很长时间,“6 个确认之后很安全”是概率性的;
  • PoS(以太坊):安全性来自攻击者必须冒着被罚没大额质押的风险去破坏已经 finalized 的状态,终局性是经济上的“确定性”。

 

验证过程

现在我们详细讲解一个 attestation 从生成到最终被包含进区块并验证的完整过程。

第一阶段:验证者本地生成并广播

假设在 slot 1000,验证者 Alice 被分配到 committee 5。

1.构造 AttestationData

  • Alice 的客户端运行 LMD-GHOST,判断当前链头是区块 B,记录其 beacon_block_root;
  • 查询当前 justified checkpoint (已证明检查点)作为 source;
  • 计算当前 epoch 的 checkpoint 作为 target;
  • 组装成完整的 AttestationData

2.签名

  • AttestationData 计算 signing root;
  • 用自己的 BLS 私钥签名:

sigAlice=BLS_Sign(privkeyAlice,signing_root)\text{sig}_{\text{Alice}} = \text{BLS\_Sign}(\text{privkey}_{\text{Alice}}, \text{signing\_root})

3.广播到网络

  • Alice 把包含以下内容的消息发布到 P2P 网络的attestation subnet:

    Message = {
        "data": AttestationData,      # 投票数据
        "signature": sig_Alice,        # Alice 的签名
        "aggregation_bits": [1],       # 一个 bit 数组,表示只有 Alice 自己
        "committee_index": 5           # 委员会 5
    }
  • 这条消息会被路由到committee 5 对应的子网(attestation subnets,一种 P2P topic)。

 

第二阶段:委员会内聚合

在 committee 5 中,协议预先选出了16 个 aggregator(通过 RANDAO 随机性 + modulo 计算)。假设 Bob 是其中一个 aggregator。

1.Aggregator 监听子网

  • Bob 的客户端订阅 committee 5 的 attestation subnet;
  • 在 slot 1000 的时间窗口内(通常是 slot 开始后的前 4 秒),Bob 会收到来自同一委员会其他成员的 attestations。

2.验证单个 attestation

  • 对于收到的每个 attestation,Bob 需要验证:
    • AttestationData 是否合法(例如 slot 是否匹配,source/target 是否正确);
    • 签名是否有效:

BLS_Verify(pubkeysender,signing_root,signature)\text{BLS\_Verify}(\text{pubkey}_{\text{sender}}, \text{signing\_root}, \text{signature}) - 发送者是否真的在 committee 5 中。

3.聚合签名

  • 假设 Bob 收到了 5000 个来自 committee 5 的有效 attestations(包括 Alice 的); -关键点:
    • 所有被聚合的签名,必须是对“完全相同的消息”签的,因此这 5000 个 attestations 的 AttestationData 必须完全相同
    • 如果收到的 AttestationData 有不一致的,那会按 AttestationData 分组
  • Bob 聚合这些签名:

agg_sig=sigAlice+sigBob+...+sig5000\text{agg\_sig} = \text{sig}_{\text{Alice}} + \text{sig}_{\text{Bob}} + ... + \text{sig}_{5000}

  • 同时构造一个 aggregation_bits 位图:
    • 这是一个 bit 数组,长度等于 committee 5 的总人数(假设 28000 人);
    • i 个 bit 设为 1 表示第 i 个验证者参与了这个聚合,设为 0 表示没有参与;
    • 例如:[1,1,0,1,0,0,1,...] 表示第 0,1,3,6,... 个验证者参与了。

4.广播聚合后的 attestation

  • Bob 把聚合后的结果发布到aggregated attestation subnet(另一个 P2P topic):

    AggregatedAttestation = {
        "data": AttestationData,           # 相同的投票数据
        "signature": agg_sig,              # 聚合签名(96 字节)
        "aggregation_bits": [1,1,0,1,...], # bit 数组(~3.5 KB)
        "committee_index": 5
    }
  • 这个消息会被 proposers 监听并收集。

 

第三阶段:Proposer 收集并包含进区块

在 slot 1001,验证者 Charlie 被选为 proposer。

1.收集聚合后的 attestations

  • Charlie 的客户端订阅所有 32 个 committee 对应的 aggregated attestation subnets;
  • 从中收集尽可能多的聚合后的 attestations(通常来自多个 slot,不只是 slot 1000);
  • 每个 slot 有 32 个 committee,每个 committee 可能有多个 aggregator,所以 Charlie 可能收到数百个聚合后的 attestations。

2.去重和选择

  • 对于同一个 committee、同一个 slot 的多个聚合,Charlie 会选择包含验证者最多的那个(通过 aggregation_bits 中 1 的数量判断);
  • 如果两个聚合有不同的验证者集合,Charlie 甚至可以在本地再次聚合它们(只要 AttestationData 相同)。

3.包含进区块

  • Charlie 构造 slot 1001 的 Beacon 区块,其中包含一个 attestations 字段;
  • 这个字段是一个数组,每个元素是一个 AggregatedAttestation;
  • 每个区块最多能包含约 128 个聚合后的 attestations(协议限制)。

 

第四阶段:其他节点验证区块

其他验证者(David)收到 Charlie 提议的区块后,需要验证其中的所有 attestations。

1.对于每个 AggregatedAttestation

a.验证 AttestationData 的合法性

  - 检查 `slot` 是否在允许的范围内(通常是当前 slot 前的 32 个 slot);
  - 检查 `source` 和 `target` 是否符合 Casper FFG 的规则;
  - 检查 `beacon_block_root` 指向的区块是否存在。

b.查询参与者列表

  - 根据 `committee_index` 和 `slot`,查询该 slot 该 committee 的完整验证者列表(这个信息是由 RANDAO 确定性计算的,所有节点都能本地计算出相同结果);
  - 例如,committee 5 在 slot 1000 的成员是 `[validator_123, validator_456, ..., validator_28000]`。

c.根据 aggregation_bits 提取参与者公钥

  - 遍历 `aggregation_bits`,如果第 `i` 位是 1,就把第 `i` 个验证者的公钥加入列表;
  - 假设提取出了 5000 个公钥:`[pubkey_Alice, pubkey_Bob, ..., pubkey_5000]`。

d.聚合公钥

  - 使用 BLS 公钥聚合算法(本质上是椭圆曲线上的点加法):
    

agg_pubkey=pubkeyAlice+pubkeyBob+...+pubkey5000\text{agg\_pubkey} = \text{pubkey}_{\text{Alice}} + \text{pubkey}_{\text{Bob}} + ... + \text{pubkey}_{5000} - 这个计算非常快,因为只是点加法,不涉及配对。

e.验证聚合签名

  - 计算 signing root:
    

signing_root=hash_tree_root(AttestationData)\text{signing\_root} = \text{hash\_tree\_root}(\text{AttestationData}) - 执行 BLS 验证:

BLS_Verify(agg_pubkey,signing_root,agg_sig)\text{BLS\_Verify}(\text{agg\_pubkey}, \text{signing\_root}, \text{agg\_sig}) - 这个验证涉及一次椭圆曲线配对运算,是最贵的部分,但一次就能验证 5000 个签名

f.检查是否有重复投票

  - 确保同一个验证者没有在同一个 slot 对多个不同的 `AttestationData` 签名;
  - 记录每个验证者在每个 slot 的投票,检测重复。

2.所有 attestations 通过验证后

  • 区块被认为有效;
  • David 会接受这个区块,并将其中的投票信息用于更新 LMD-GHOST 和 Casper FFG 的状态。

 

整个流程的效率分析

假设一个 slot 有 32 个 committee,每个 committee 有 28,000 个验证者,总共 896,000 个验证者。

没有聚合的情况:

  • 网络传输:896,000 个签名 × 96 字节 =86 MB
  • 验证成本:896,000 次 BLS 验证(每次约 1-2ms) =数十分钟
  • 区块大小:86 MB + 其他数据

有聚合的情况(16 aggregators/committee):

  • 网络传输第一阶段(subnet):896,000 个单独 attestation,但只在小范围的 subnet 内传播
  • 网络传输第二阶段(aggregated):32 committees × 16 aggregators = 512 个聚合,每个约 3.6 KB =1.8 MB
  • 区块大小:最多 128 个聚合 × 3.6 KB =约 460 KB
  • 验证成本:128 次 BLS 验证 =数百毫秒

压缩比:

  • 网络传输:从 86 MB 降至 1.8 MB(约50倍)
  • 区块大小:从 86 MB 降至 460 KB(约190倍)
  • 验证时间:从数十分钟降至数百毫秒(约1000倍)

这就是为什么 BLS 签名聚合对于以太坊 PoS 至关重要——它使得上百万验证者的系统在工程上变得可行。

 

奖励与惩罚

在 PoS 中,安全性不再来自“算力很贵、难以伪造”,而是来自“质押很贵,被 slash 会很疼”。共识协议通过奖励和惩罚把“诚实行为”变成参与者的最优选择。

奖励机制

验证者的收益主要来自几部分:

1. 基础奖励(Base Reward)

  • 每个活跃验证者都有一个“有效质押余额”(最多 32 ETH);
  • 协议会根据全网总质押量计算一个基础利率:

Base Reward=Effective Balance×Base Reward FactorTotal Active Balance\text{Base Reward} = \frac{\text{Effective Balance} \times \text{Base Reward Factor}}{\sqrt{\text{Total Active Balance}}}

  • 全网质押越多,单个验证者的基础奖励越低(平方根关系),这样设计是为了平衡安全性和成本。

具体示例:

  • 假设全网有 30,000,000 ETH 质押(大约 937,500 个验证者);
  • Base Reward Factor = 64 (2024 年的协议参数);
  • 单个验证者的每 epoch 基础奖励 ≈ 0.00004 ETH;
  • 年化收益率(APR) ≈ 3.5-4.5%(不含 MEV 和小费)。

 

2. 投票奖励(Attestation Rewards)

验证者每个 epoch 至少需要提交一次 attestation,奖励由三部分组成:

-Source 投票奖励:正确投票给 source checkpoint; -Target 投票奖励:正确投票给 target checkpoint; -Head 投票奖励:正确投票给 LMD-GHOST 的 head 区块。

每部分的奖励计算公式:

Reward Component=Base Reward×Total Attesting BalanceTotal Active Balance\text{Reward Component} = \text{Base Reward} \times \frac{\text{Total Attesting Balance}}{\text{Total Active Balance}}

直观理解:

  • 如果 100% 的验证者都正确投票,你能得到完整的奖励;
  • 如果只有 80% 的验证者投票,你只能得到 80% 的奖励;
  • 这鼓励验证者尽快投票,因为别人不投会减少你的收益。

 

3. 提议奖励(Proposer Rewards)

当验证者被选为 proposer 时,可以获得额外奖励:

-包含 attestations 的奖励:

  • 每包含一个有效的 attestation,proposer 能得到该 attestation 奖励的 1/8;
  • 如果一个区块包含 128 个 attestations,proposer 能得到 128 × 1/8 = 16 份基础奖励。

-Sync Committee 奖励(后期升级增加):

  • Sync committee 是一小组验证者(约 512 个),负责给轻客户端提供快速同步;
  • Proposer 包含 sync committee 签名也能获得少量奖励。

-交易费用(Priority Fees):

  • 用户交易的小费(基础费用被销毁,只有小费给 proposer);
  • 在网络拥堵时,这部分可以很可观。

-MEV 收益:

  • 通过 MEV-Boost 等机制,proposer 可以把区块构造权卖给专业的 builder,获得 MEV 提取值的大部分;
  • 这部分收益可能是基础奖励的数倍甚至数十倍。

 

4. Inclusion Delay Penalty

  • 如果你的 attestation 被延迟包含进区块:
    • 在同一个 slot 包含:奖励 = Base Reward
    • 延迟 1 个 slot:奖励 = 7/8 × Base Reward
    • 延迟 2 个 slot:奖励 = 6/8 × Base Reward
    • 以此类推,最多延迟 32 个 slot(1 个 epoch)
  • 这鼓励验证者尽快发出 attestation,也鼓励 proposer 尽快包含 attestations。

 

惩罚机制

惩罚机制主要分两类:

1. 轻微惩罚:离线和低参与度

Inactivity Penalty(离线惩罚)

  • 如果验证者没有提交 attestation:
    • 每个 epoch 会损失一小部分质押(大约等于如果在线应该得到的奖励);
    • 长期离线会逐步侵蚀质押的本金。

Inactivity Leak(不活跃泄漏)

在极端情况下(网络分区、大量验证者离线),如果链无法 finalize checkpoint 超过 4 个 epoch,会触发Inactivity Leak:

  • 所有没有积极投票的验证者质押会被逐步“泄漏”掉;
  • 泄漏率会随时间加快:

Leak Penalty=Base Reward×Epochs Since Last FinalityInactivity Penalty Quotient\text{Leak Penalty} = \text{Base Reward} \times \frac{\text{Epochs Since Last Finality}}{\text{Inactivity Penalty Quotient}}

  • 这样,即使现在有很多人离线或作壁上观,剩下那部分坚持在线投票的验证者仍然可以最终重新获得 ≥ 2/3 的有效质押,恢复 finality。

设计目的:

  • 防止网络永久卡死:即使 1/3 以上的验证者离线,系统仍然能恢复;
  • 但这个惩罚很重:如果离线 50 天,可能损失 50% 以上的质押。

 

2. 重罚:Slashing

针对明显的恶意行为,协议会直接罚没质押:

1.双重提议(Double Proposal)

  • 在同一个 slot 提出两个不同的 beacon 区块,试图制造分叉攻击。 2.双重投票(Double Vote)
  • 在同一个 epoch 对两个不同的 target checkpoint 投票; 3.环绕投票(Surround Vote)
  • 同一个验证者在两次投票中,后一票的 source 比前一票更早、而 target 却更晚,从而在时间上“包住”前一票。

 

Slashing 惩罚的三个阶段

1.初始罚没

  • 一旦被检测到恶意行为,立即罚没1 ETH(或有效质押的 1/32);
  • 验证者被标记为“已 slash”,不能再参与共识。

2.相关性罚没(Correlation Penalty)

  • 在同一时间窗口内(18 天),如果有多个验证者被 slash,每个被 slash 的验证者还会额外罚没:

Correlation Penalty=Effective Balance×Total Slashed BalanceTotal Active Balance\text{Correlation Penalty} = \text{Effective Balance} \times \frac{\text{Total Slashed Balance}}{\text{Total Active Balance}}

  • 如果 1% 的验证者被同时 slash,每个被 slash 者额外损失 1% 的质押;
  • 如果 33% 的验证者被同时 slash,每个被 slash 者额外损失 33% 的质押。 -这个设计的目的是使大规模攻击的成本极高:单个验证者出错损失小,但协同攻击损失巨大。

3.退出等待

  • 被 slash 的验证者需要等待约 36 天才能提取剩余质押;
  • 在这期间,质押继续受到 inactivity leak 的影响。

 

Slashing 总损失示例

-单个验证者出错(0.01% 被 slash):

  • 初始罚没:1 ETH
  • 相关性罚没:几乎为 0
  • 总损失:约 1 ETH、损失率 ≈ 3%

-大规模攻击(33% 被 slash,试图破坏 finality):

  • 初始罚没:1 ETH
  • 相关性罚没:32 × 33% ≈ 10.5 ETH
  • Inactivity leak:约 5+ ETH(36 天)
  • 总损失:约 16.5 ETH,损失率 ≈ 50%+

 

余额更新

奖励和惩罚不是链上转账,而是“状态里直接修改余额”

每个共识节点都维护同一份 state.balances[](按验证者 index 存余额),并在每个 epoch 依据各验证者的履职情况(投票是否正确/及时、是否被包含、是否出块、是否被罚没等)先算出每人的“余额增量/减量”,然后直接修改其余额。没有一笔一笔的“转账交易”,状态更新是 O(n) 的内部计算。

之所以能形成共识,是因为这套计算规则是协议的一部分,如果有人“加错了”,它算出来的状态根会与网络多数不一致,该节点就会认为后续区块无效而被迫回到正确链上,因此错误不会变成“链上事实”

 

小结

从 PoW 到 PoS,以太坊在共识与挖矿(更准确地说,是区块产生和确认机制)上经历了一条清晰的演化路线:

  • PoW 阶段通过 Ethash、快速难度调整、难度炸弹和叔块奖励,在保持较快出块和智能合约支持的同时,尽力降低算力中心化风险,并为未来升级留出了“倒计时开关”。
  • 随着 PoS 技术成熟,以太坊通过 Beacon 链和 The Merge 完成了共识层的替换,用质押和投票取代了纯算力竞争,大幅降低能耗,并为后续的扩容路线(Rollup、分片)提供了更灵活的基础。

理解这条演化线,有助于你在学习以太坊其他部分(状态树、交易树、收据树、EVM、Gas 机制等)时,把它们放到一个统一的“分布式状态机 + 共识协议”的大框架下来看:

  • 执行层决定状态如何变化;
  • 共识层决定哪条状态变化历史被全网接受;
  • 经济激励和惩罚机制,保证大多数参与者有动力保持诚实。

作者:加密鲸拓

版权:此文章版权归 加密鲸拓 所有,如有转载,请注明出处!