覆盖第 1–8 讲每题都给完整解答,不是"答案略"。建议先自己做完再看。


题 1 双花的本质

(a) 构造一个具体的双花场景,说明冲突的两笔交易各自都完全合法

(b) 由此说明"双花是排序问题而不是验证问题"。

(c) 银行方案里,原子性依赖什么前提?去掉这个前提后哪一步失效?

解答

(a) Alice 余额 30。她同时发出:

Tx₁:Alice → Bob   30
Tx₂:Alice → Carol 30

单独看每一笔:签名有效、余额充足(30 ≥ 30)、格式正确。任何验证函数都会通过。

网络延迟导致左半边节点先收到 Tx₁、右半边先收到 Tx₂:

左半边:接受 Tx₁ ⟹ 余额归零 ⟹ 拒绝 Tx₂(余额不足)
右半边:接受 Tx₂ ⟹ 余额归零 ⟹ 拒绝 Tx₁

(b) ⭐ 两边的验证逻辑完全相同且都正确,得到的结论却相反。唯一的差别是它们看到的顺序不同。 ⟹ 问题不在"如何判断一笔交易是否合法",而在"如何让所有人对顺序达成一致"。

(c) 前提是只有一个写入者。去掉它之后,“检查余额"和"扣款"之间就不再原子——两个写入者可以各自读到同一个余额 30,各自判断"够用”,然后各自扣款。这正是数据库里的经典竞态,而分布式环境下它无法靠加锁解决,因为没有一个共同的锁。


题 2 FLP 与网络模型

(a) FLP 的三个限定词各是什么?分别去掉一个会怎样?

(b) 为什么异步模型下无法区分"崩溃"和"缓慢"?

(c) 比特币是怎么绕过 FLP 的?

解答

(a)

限定词 去掉后
异步 同步/部分同步下共识可解——可以用超时判定故障
确定性 允许随机化即可绕过(Ben-Or):对手无法预测下一步,构造不出无限延迟序列
同时保证安全性和活性 只保证其中一个是平凡的(永不决定 ⟹ 安全;随便决定 ⟹ 活)

(b) ⭐ 异步的定义是"消息最终会到,但没有时间上界"。既然没有上界,任何超时都不能证明对方死了——它可能只是慢。而崩溃和缓慢在观测上完全相同:都是"没收到消息"。

(c) ⭐ 它换了一个更弱的问题:

① 把"确定性最终性"换成"概率最终性"——⭐ 从不真正"决定",只是概率递增
② 假设网络同步(10 分钟出块间隔 ≫ 秒级传播时间)

两个前提各自都足以让 FLP 不适用。它不是解决了经典共识问题,而是换了一个问题。


题 3 拜占庭下界

(a) 推导 n > 3f

(b) 为什么"最多只能等 n−f 个回复"?如果协议要求等 n−f+1 个会怎样?

(c) 有签名的同步网络下门槛降到 n ≥ f+2。为什么区块链仍用 n ≥ 3f+1

解答

(a)

等待时最多只能等 n − f 个回复      (那 f 个可能永不回话)
这 n − f 个里最多 f 个来自拜占庭节点(回了,但内容是假的)
⟹ 可信的诚实回复至少 (n−f) − f = n − 2f 个

要让诚实的大多数压过可能的谎言:
        n − 2f > f
   ⟹    n > 3f

f 被减了两次:一次因为它们可能不回话(活性代价),一次因为它们可能说谎(安全性代价)。

(b) 因为 f 个拜占庭节点可以选择永远沉默。⚠️ 若协议要求等 n−f+1 个,那么在这 f 个全部沉默时协议永远阻塞 ⟹ 活性被违反

(c) 因为区块链运行在部分同步模型下,不是同步模型。签名解决的是"中转者篡改他人消息",但它无法解决"网络延迟导致无法区分故障"。在部分同步下,即使有签名,n ≥ 3f+1 依然是必要的。


题 4 不可能三角的量化

一条链声称 5,000 TPS,平均交易 500 字节。

(a) 算出年产生的原始数据量。

(b) 全节点还能放在家用机器上吗?

(c) 这条链是提高了 T 还是提高了 R

解答

(a)

带宽:5,000 × 500 = 2.5 MB/s = 20 Mbps 【持续】
每天:2.5 × 86,400 = 216 GB
每年:216 GB × 365 ≈ 78.8 TB

(b) ⚠️ 不能。 78.8 TB/年意味着:

① 需要多块大容量硬盘,且每年都要加
② ⭐ 更致命的是【同步时间】:新节点要下载 78.8 TB,
   即使千兆网满速(125 MB/s)也要约 7 天,
   而实际还要执行和写状态,只会更慢
⟹ 普通人退出,只剩数据中心

(c)提高的是 R 它没有改变"每个全节点验证每笔交易"这个架构,只是把对机器的要求抬高了 —— 按第 3 讲的定义,这直接等于降低去中心化。


题 5 生日攻击

(a) 128 位输出的哈希,找碰撞需要多少次尝试?每秒 10¹² 次要多久?

(b) 为什么 SHA-256 只有 128 位碰撞安全性?

(c) “MD5 被破解了,所以用 MD5 存密码不安全”——这句话对吗?

解答

(a)

2^(128/2) = 2⁶⁴ ≈ 1.845 × 10¹⁹ 次

10¹² 次/秒 ⟹ 1.845 × 10⁷ 秒 ≈ 0.585 年

不到七个月。这就是为什么 128 位输出的哈希今天不能用于需要抗碰撞的场景。

(b) 因为找碰撞时攻击者可以自由选择两个输入,而生日悖论让这个自由度把成本从 2ⁿ 降到 2^(n/2)哈希的安全级别必须按最弱的那条性质来标称。

(c) ⚠️ 结论对,但理由错。

⭐ MD5 被攻破的是【抗碰撞性】,而密码哈希依赖的是【抗原像性】——
   后者至今没有被有效攻破。

MD5 存密码不安全的真正原因是:
   ① 它【太快】——攻击者每秒能试几十亿个候选口令
   ② 通常没有加盐 ⟹ 彩虹表可用
⟹ 应该用 bcrypt / scrypt / Argon2 这类【故意很慢】的函数。

题 6 Merkle 树的两个漏洞

(a) 完整推演 [A,B,C][A,B,C,C] 在比特币构造下的每一层,说明根为何相同。

(b) 攻击者据此能做什么?受害节点在哪一步出错?

(c) 无域分隔时的第二原像攻击怎么做?加上前缀后为何失效?

解答

(a)

[A, B, C]:
  第 0 层:h(A), h(B), h(C)
  ⚠️ 奇数 ⟹ 复制最后一个:h(A), h(B), h(C), h(C)
  第 1 层:H(h(A),h(B)),  H(h(C),h(C))
  根:     H( H(h(A),h(B)), H(h(C),h(C)) )

[A, B, C, C]:
  第 0 层:h(A), h(B), h(C), h(C)     ← 已是偶数,无需复制
  第 1 层:H(h(A),h(B)),  H(h(C),h(C))
  根:     完全相同

(b)

① 攻击者截获合法区块 X = [A,B,C],构造 Y = [A,B,C,C] 转发
② 受害节点:区块头哈希 ✅、Merkle 根 ✅
   ⚠️ 但验证交易时发现 C 重复 ⟹ 双花 ⟹ 区块无效
③ 节点把【这个区块哈希】记入无效缓存
④ 真正的区块 X 到达时,哈希与 Y 完全相同
   ⟹ 一查缓存,直接丢弃
⟹ 该节点永久停在旧链上(永久 DoS,不需要任何算力)

出错的根源:Merkle 构造不是单射的——两个不同的叶子序列给出同一个根。

(c)

无域分隔时:leaf(x) = H(x),node = H(左‖右)
⟹ ⚠️ 内部节点的值和叶子的值来自【同一个哈希空间】,无法区分。

于是一棵 4 叶子树的两个内部节点值 (h₀‖h₁) 和 (h₂‖h₃),
可以被冒充成一棵 2 叶子树的两个【叶子】,
   两者算出的根完全相同 ⟹ 对同一个根给出了另一个"原像"。

加上前缀后:
   leaf = H(0x00 ‖ x),node = H(0x01 ‖ 左 ‖ 右)
⟹ 两类哈希的输入前缀不同,落在【不相交】的空间里
⟹ 冒充需要找到跨前缀的哈希碰撞 —— 不可行

题 7 ECDSA 随机数重用:完整演算

某系统用 ECDSA,群阶 n = 101(为便于手算取的小素数)。观察到同一私钥的两个签名,r 相同

r = 40
签名一:s₁ = 55,消息哈希 z₁ = 20
签名二:s₂ = 33,消息哈希 z₂ = 91

(a) r 相同意味着什么?

(b) 解出 k

(c) 解出私钥 d,并验算。

解答

(a) r = (k·G).x mod n,它只依赖 k。所以 r 相同 ⟹ 两次用了同一个 k

(b)

s₁ = k⁻¹(z₁ + r·d)
s₂ = k⁻¹(z₂ + r·d)
相减:s₁ − s₂ = k⁻¹(z₁ − z₂)

⟹ k = (z₁ − z₂) · (s₁ − s₂)⁻¹ mod 101
     = (20 − 91) · (55 − 33)⁻¹
     = (−71) · 22⁻¹
     = 30 · 22⁻¹          (−71 ≡ 30 mod 101)

求 22⁻¹:22 × 23 = 506 = 5×101 + 1  ⟹ 22⁻¹ = 23

⟹ k = 30 × 23 = 690 ≡ 690 − 6×101 = ⭐ 84

(c)

由 s₁·k = z₁ + r·d
⟹ d = (s₁·k − z₁) · r⁻¹ mod 101
     = (55 × 84 − 20) × 40⁻¹
     = (4620 − 20) × 40⁻¹
     = 4600 × 40⁻¹
     4600 ≡ 4600 − 45×101 = 55

求 40⁻¹:40 × 48 = 1920 = 19×101 + 1 ⟹ 40⁻¹ = 48

⟹ d = 55 × 48 = 2640 ≡ 2640 − 26×101 = ⭐ 14

验算: k⁻¹17 × 6 = 102 ≡ 1,而 84 ≡ −17,所以 k⁻¹ = −6 ≡ 95

签名一:z₁ + r·d = 20 + 40×14 = 580 ≡ 75
        s₁ = 95 × 75 = 7125 ≡ 7125 − 70×101 = 55  ✅

签名二:z₂ + r·d = 91 + 560 = 651 ≡ 45
        s₂ = 95 × 45 = 4275 ≡ 4275 − 42×101 = 33  ✅

两个签名都对上了,私钥 d = 14 被完全恢复。

⚠️ 注意这个计算的成本:几次模乘。而 r 是公开在链上的——攻击者只需扫描全链找 r 重复的签名对。这就是 Sony PS3 私钥被算出来的全部过程。


题 8 Schnorr 聚合与恶意密钥

(a) 用线性性推导两方签名聚合。

(b) Bob 声称 Q₂ = Q' − Q₁Q' 是他自己选的)。他能做什么?

(c) 持有证明为什么能挡住它?

解答

(a)

Alice: s₁ = k₁ + e·d₁
Bob:   s₂ = k₂ + e·d₂        (同一个挑战 e)
────────────────────────────
s = s₁ + s₂ = (k₁+k₂) + e·(d₁+d₂)

⭐ 这是对聚合公钥 Q = Q₁ + Q₂ = (d₁+d₂)·G、
   聚合承诺 R = R₁ + R₂ 的一个合法 Schnorr 签名。

(b)

聚合公钥变成 Q = Q₁ + Q₂ = Q₁ + (Q' − Q₁) = ⭐ Q'

而 Q' 是 Bob 自己选的,他知道对应的私钥 d'。
⟹ Bob 可以【单独】为聚合公钥签名,
   完全不需要 Alice 参与。
⟹ 一个"2-of-2 多签"实际上被 Bob 一个人控制了。

(c) ⭐ 持有证明要求:注册公钥时,必须附上一个用该公钥对应私钥、对公钥自身的签名。

Bob 声称的 Q₂ = Q' − Q₁:
   ⚠️ 他不知道 Q₂ 对应的私钥(那需要知道 d' − d₁,而 d₁ 是 Alice 的)
⟹ 他签不出这个证明 ⟹ 公钥注册被拒

题 9 承诺方案

(a) 一个链上密封拍卖用 c = keccak256(出价),出价范围 0–1000 整数。写出攻破它的完整步骤。

(b) 修复方案。修复后隐藏性依赖什么假设?

(c) 修复后仍然存在什么问题?无法靠加大保证金根治的原因是什么?

解答

(a)

① 攻击者本地计算 keccak256(0), keccak256(1), ..., keccak256(1000)
   ⭐ 一共 1001 次哈希,毫秒级完成
② 建一张 哈希 → 出价 的查找表
③ 从链上读取所有人提交的承诺,逐个查表
⟹ 所有人的出价被完全还原,隐藏性为零

⚠️ 根因:x 的熵只有约 10 比特,而承诺方案的隐藏性要求输入不可枚举。

(b)

c = keccak256(出价 ‖ r),r 是 32 字节随机数
揭示时同时公布 出价 和 r

隐藏性依赖的假设:哈希的抗原像性 + r 有足够熵。注意这两条缺一不可——如果 rblock.timestamp 生成,熵仍然很低,攻击照样成立。

(c) ⚠️ 最后揭示者优势(第 8 讲第三节):

最后一个揭示的人,能先算出"我揭示会导致什么结果"。
若结果不利,他可以选择【不揭示】(认赔保证金)。
⟹ ⭐ 他获得了一次对最终结果的"重掷"机会。

加大保证金不能根治,因为这是一个比较关系:只要操纵收益 > 保证金,放弃保证金就仍然划算。而操纵收益取决于奖池大小,是可以任意大的。真正的解法是 VRF 或 VDF——用密码学消除"选择权"本身。


题 10 链上随机数

(a) 一个 NFT 合约用 blockhash(block.number - 1) 决定稀有度。矿工/验证者能做什么?普通用户能做什么?

(b) VRF 的"唯一性"为什么是要害?

(c) RANDAO 仍有偏置。VDF 是怎么消除它的?

解答

(a)

出块者:
   ⭐ 他能看到自己出的块会导致什么结果。
   若抽到的不是稀有 NFT,他可以【丢弃这个块重挖】。
   代价只是一个区块奖励——只要稀有 NFT 价值更高,攻击就划算。

普通用户:
   他能【预先模拟】:在一笔交易里先读 blockhash,
      算出结果,若不利就 revert 整笔交易。
   ⟹ 由于 EVM 的原子性,这等于免费重试无限次
      (只损失 Gas)

第二条常被忽略,但它不需要任何特权,人人可做。

(b) 唯一性意味着:给定 skinput,输出唯一确定,持有者没有任何选择空间。

⚠️ 如果不唯一(比如允许多个有效输出),
   持有者就能算出多个候选、挑一个对自己有利的公布
⟹ 退化成"最后揭示者优势"

所以唯一性把"可以选择"变成了"只能公布或不公布"——把一个连续的选择空间压缩成了一个二元决定。

(c)

RANDAO 的问题:出块者能预见自己出块对种子的影响,
⚠️ 可以选择不出块来"重掷"(代价是放弃区块奖励)。

VDF 的性质:必须【串行】计算 T 步,无法并行加速,但结果可快速验证。

把 RANDAO 输出再过一遍 VDF:
   ⟹ 出块者在决定"出不出这个块"的几秒钟内,
      根本【来不及】算出 VDF 结果
   ⟹ 他无从判断这个块对自己有没有利
   ⟹ 操纵的【信息优势】被物理时间消灭了

关键在于:VDF 不是让操纵变难,而是让操纵者在做决定时"不知道该往哪边操纵"。


相关第 1–8 讲