一个消息要传遍全网,需要多久?
这个数字后面会反复出现——它决定孤块率、决定区块能有多大、决定共识协议的安全假设成不成立。但它不是网卡参数,也不是"网速"。
打个比方
它更像一个消息在人群里传开需要多久。
这取决于两件事:每个人告诉几个人(每一跳的扇出),以及要传几轮才能覆盖所有人(跳数)。人越多,轮数按对数增长——不算太糟。而真正的代价藏在第一件事上:你告诉的人越多,传得越快,但你自己要说的话也越多(带宽)。
这一讲要拆的就是这个权衡,以及它如何决定了后面几讲的所有上限。
一、一个被反复使用却从未推导的量
后面几讲会不断用到一个数:
第 16 讲:孤块率 ≈ 传播时间 / 出块间隔
第 26 讲:扩容第一个瓶颈 —— 区块变大 ⟹ 传播变慢 ⟹ 孤块率↑
第 29 讲:数据可用性采样,采样请求也要在网络上跑
但"传播时间"本身从哪来? 它不是网卡参数,也不是"网速"——它是网络层协议设计的产物。这一讲把它拆开。
⭐ 顺序上它必须在共识之前:第 2 讲说过,共识协议的安全性证明都建立在某种网络假设之上(同步 / 部分同步 / 异步)。那个假设成不成立,由这一层决定。
⚠️ 这一讲读起来会像"工程细节",容易被当成可以跳过的一节。恰恰相反——上面那句话的意思是:前面所有漂亮的安全性证明,全都挂在这一层给出的那个数字上。 如果传播时间比协议假设的大一个数量级,那些证明就不再适用,而没有任何人会收到报错。
二、为什么必须是 P2P
第 1 讲的出发点是"没有可信第三方"。这个要求对网络层同样成立——如果所有交易都要经过某台服务器才能传开,那台服务器就能审查交易,前面所有的密码学都白做了。
于是网络层也必须是无许可的对等网络,代价是三条:
① 没有全局视图 —— 没有任何节点知道网络里有多少节点、都是谁
② 消息不可靠 —— 可能丢失、重复、乱序到达
③ 邻居可能撒谎 —— 你连上的节点可能是攻击者
⚠️ 注意网络层的安全目标和你的直觉可能不同:
不是【保密】—— 交易和区块本来就是公开数据,加密没有意义。
是【可达性】—— 让每一个诚实节点,最终都能收到每一条消息。
⭐ 换句话说,这一层要防的不是窃听,是"隔离"——第七节会说明,隔离一个节点足以让它被双花。
三、gossip:为什么是泛洪而不是广播
没有全局视图,就不可能"广播给所有人"。实际做法是泛洪(flooding):
① 每个节点维持少量邻居连接
比特币:默认 8 个出站 + 至多 117 个入站
以太坊 devp2p:默认约 13 个 peer
② 收到一条没见过的消息 ⟹ 验证它 ⟹ 转发给除来源外的所有邻居
③ 已经见过的消息直接丢弃(靠哈希去重)
消息像水波一样一圈一圈扩散。 覆盖全网需要的跳数,就是这个随机图的直径:
每个节点 d 个邻居,n 个节点:
第 0 跳 1 个节点知道
第 1 跳 d 个
第 2 跳 d² 个
...
第 k 跳 dᵏ 个
⟹ 覆盖全网需要 k ≈ log_d(n) 跳
代入真实数字:一万个节点、每个 8 个邻居,只要约 4.4 跳。 这就是 gossip 有效的原因——跳数随节点数对数增长,网络翻十倍,跳数只多一点点。
⚠️ 但第 ② 步里有一个容易被忽略的动作:先验证,再转发。
如果转发不验证:
攻击者发一个垃圾区块 ⟹ 全网帮他免费传播
⟹ ⭐ 这是一个放大 n 倍的 DoS
所以每一跳都必须验证。
⟹ 而验证是要花时间的,这段时间【进入了传播时间】。
⭐ 记住这个结构:安全(必须验证)和速度(验证费时)在这一层是直接冲突的。
四、传播时间的分解
⭐ 这一节把"消息在人群里传开"拆成两项:传几轮(跳数)和每轮花多久(每跳的时延与带宽)。 前面那个权衡——告诉的人越多传得越快、但自己要说的话也越多——在这里变成了具体的数。
传播时间 ≈ 跳数 × ( 物理延迟 + 验证时间 + 传输时间 )
↑ ↑ ↑
光速下限 重执行交易 区块大小/带宽
三项的性质完全不同:
| 分量 | 决定于 | 能不能靠加带宽改善 |
|---|---|---|
| 物理延迟 | 光速与地理距离 | 不能——这是硬下限 |
| 验证时间 | 区块里的交易量、状态访问 | 不能(是 CPU 和磁盘) |
| 传输时间 | 区块字节数 ÷ 带宽 | 能 |
物理延迟的下限值得算一下:
光在光纤中约 200,000 km/s(比真空慢 1/3)
地球半周长约 20,000 km
⟹ ⭐ 地球对跖点之间的单向延迟下限 ≈ 100 ms
实际跨洲 RTT 通常 150–250 ms(要绕路、要过路由器)
⚠️ 这一项是任何工程都消不掉的。 这正是第 26 讲那句"即使带宽足够,全球节点之间的物理延迟不会变"的出处——“多买带宽就能调大区块"之所以错,错在它只看见了三项里的一项。
一个可以算的模型
// 一次 gossip 传播的耗时模型。
// 这不是精确仿真,是数量级估算——但它足以说明每一项各自的权重。
package p2p
import "math"
type Params struct {
Nodes int // 全网节点数
Peers int // 每个节点的邻居数
HopLatency float64 // 每跳物理延迟(秒)
VerifyTime float64 // 每跳验证耗时(秒)
BlockBytes float64 // 每跳要传的字节数
Bandwidth float64 // 带宽(字节/秒)
}
// Hops 返回覆盖全网的期望跳数:随机图直径约 log_d(n)。
func Hops(nodes, peers int) float64 {
if nodes <= 1 || peers <= 1 {
return 0
}
return math.Log(float64(nodes)) / math.Log(float64(peers))
}
// Propagation 返回一个区块传遍全网的估算时间。
func Propagation(p Params) float64 {
perHop := p.HopLatency + p.VerifyTime + p.BlockBytes/p.Bandwidth
return Hops(p.Nodes, p.Peers) * perHop
}
// OrphanRate 就是第 16 讲那条公式。
// 它成立的前提是:出块是泊松过程,且传播期间挖出的块会成为孤块。
func OrphanRate(propagation, blockInterval float64) float64 {
return propagation / blockInterval
}
代入一万个节点、8 邻居、每跳 50 ms、验证 10 ms、100 Mbps:
1 MB 完整区块 15 KB 紧凑区块
────────────────────────────────────────────────────────
跳数 4.43 4.43
物理延迟/跳 50 ms 50 ms
验证/跳 10 ms 10 ms
传输/跳 80 ms ⭐ 1.2 ms
────────────────────────────────────────────────────────
每跳合计 140 ms 61 ms
传播时间 620 ms 271 ms
────────────────────────────────────────────────────────
孤块率 @ 600 秒出块 0.10% 0.045%
孤块率 @ 12 秒出块 ⚠️ 5.2% 2.3%
⭐ 看最后两行:同一个网络,出块间隔从 600 秒改成 12 秒,孤块率涨了五十倍。出块间隔和网络层不是两个独立的旋钮。
五、优化:把传输时间从关键路径上拿掉
上表已经泄露了答案——传输时间是三项里唯一能大幅压缩的,办法不是加带宽,是少传字节。
Compact Blocks(BIP-152)
关键观察:一个区块里的交易,你的邻居大概率早就有了——它们在几秒到几分钟前就通过交易池 gossip 传过来了。既然如此,出块时没必要再传一遍:
① 只发:区块头 + 每笔交易的 6 字节短 ID + coinbase 交易
⟹ 一个 2500 笔交易的区块,从 1 MB 压到约 15 KB
② 对方用短 ID 在自己的交易池里查找、重建区块
③ ⚠️ 如果缺了几笔,再发一轮 getblocktxn 要回来
⟹ 代价是多一个 RTT
⭐ 这是一次典型的"用带宽换延迟"的反向操作:数据被提前分散传输了(交易 gossip),出块时只传增量。
高带宽模式更进一步:不等对方请求,直接把紧凑区块推过去,省掉一次询问的往返。
中继网络
再往上是专用的低延迟骨干(比特币的 FIBRE、以太坊的各类 relay):预先建立的连接、前向纠错(丢包不用重传)、地理上分布的节点。
⚠️ 它的代价要看清楚:这是一层中心化的基础设施。矿工/验证者依赖少数几个中继商,等于在网络层引入了新的信任点——第 34 讲的 PBS 会把这个问题放大成一个结构性议题。
六、交易池的传播与 Erlay
区块之外,交易也要 gossip,而且冗余问题更严重:
朴素做法:收到一笔交易,转发给全部 8 个邻居
⟹ 每个节点会从多个邻居各收到同一笔交易一次
⟹ ⭐ 带宽消耗 ∝ 邻居数,而不是 ∝ 交易数
第一层缓解是 INV / GETDATA 两阶段:先只发 32 字节的哈希(inventory),对方发现自己没有才来要正文。省掉了重复的正文,但哈希本身仍然是按邻居数重复的。
Erlay(BIP-330) 换了个思路:与其泛洪,不如让两个节点协调各自交易池的差集:
用 minisketch(一种集合协调编码):
两个各有几千笔交易的节点,
只要交换 O(差集大小) 的数据,就能算出彼此缺什么。
⟹ 带宽从 O(邻居数) 降到近似 O(1)
⟹ 代价:延迟略增(要等一个协调周期)
⭐ 为什么这件事重要,值得说明白:带宽是"能不能跑一个节点"的门槛之一。降低带宽 = 更多普通人能跑全节点 = 第 3 讲意义上的去中心化。 这是一个网络层优化直接兑换成安全性的例子。
七、网络层的攻击面
Eclipse 攻击
目标不是攻破密码学,是把一个节点从真实网络里"摘出去”:
① 攻击者用大量 IP 占满受害节点的全部连接槽
② 受害者从此只能看见攻击者构造的"世界"
③ ⟹ 攻击者可以:
· 隐瞒真实的最长链 ⟹ 受害者以为一笔交易确认了(双花)
· 隐瞒新区块 ⟹ 受害者的算力全部浪费在旧高度上
· 把受害者的算力"借"给自私挖矿(第 16 讲)
⭐ 这个攻击的深刻之处在于它打的位置:
所有共识安全性证明,都有一条前提叫
"诚实节点之间能够互相通信"。
⚠️ Eclipse 攻击不攻击共识算法 —— 它攻击这条前提。
⟹ 算法一行没错,结论照样不成立。
防御全部是网络层的工程手段:出站连接优先信任(出站是自己选的,入站是别人找上门的)、地址表按 /16 网段分桶(同一网段的大量 IP 不能挤占同一个桶)、锚点连接(重启后回连上次的稳定 peer)、以及在高价值场景下手工指定 peer。
Sybil:网络层没有身份
⚠️ 这里有一个非常常见的误解,必须点破:
PoW / PoS 解决的是【出块权】的 Sybil 问题
—— 你不能靠多开节点获得更多出块机会。
它们【完全没有】解决【连接槽】的 Sybil 问题
—— 在网络层,开一万个节点仍然是免费的。
⭐ 所以 Eclipse 是 PoW 链和 PoS 链共有的问题,与共识机制无关。
网络分区与路由层攻击
拥有路由能力的对手(ISP、能发 BGP 通告的机构)可以劫持流量或切断区域连接。已有研究记录过针对比特币流量的 BGP 劫持事件。分区期间,两边各自认为自己在正常出块——这正是第 2 讲 CAP 与部分同步假设讨论的现实版本。
来源去匿名
⚠️ 一笔交易第一个广播它的节点,大概率就是发起者。 观察者只要连上足够多的节点、记录每笔交易的首次出现位置,就能把交易和 IP 关联起来——这在链上数据里是完全看不见的。Dandelion++ 之类的方案先让交易沿一条随机路径"茎"传播若干跳,再进入泛洪的"花"阶段,用来打断这个关联。第 33 讲会把它放回隐私的整体图景里。
八、网络假设与共识的联动
现在可以回答第 2 讲留下的问题了:出块间隔是怎么定出来的?
⭐ 出块间隔 = 传播时间 × 安全系数
安全系数越大,孤块率越低、越安全,但确认越慢。
| 链 | 出块间隔 | 传播时间量级 | 裕度 | 代价 |
|---|---|---|---|---|
| 比特币 | 600 秒 | ~1 秒 | 约 600× | 确认极慢 |
| 以太坊 | 12 秒 | ~1–2 秒 | 约 10× | ⚠️ 需要更精细的分叉选择(第 20 讲) |
| 高速链 | 亚秒级 | 同量级 | 接近 1 | 需要小而地理集中的验证者集合 |
⭐ 这张表解释了一件常被误读的事:一条链"出块快"不是工程上更强,是在网络裕度上取了更激进的位置,并用其它地方的代价(验证者门槛、更复杂的分叉选择、更高的重组概率)付了账。
⚠️ 裕度接近 1 时会发生什么:传播时间的波动(某次跨洲拥塞)直接变成分叉,链要靠更复杂的规则来收敛。这不是"更好的技术",是同一条权衡曲线上的另一个点——和第 3 讲对"不可能三角"的处理是同一个道理。
九、本讲小结
- ⭐ “传播时间"不是网速,是网络层协议设计的产物——它被第 16 讲的孤块率公式、第 26 讲的扩容瓶颈、第 29 讲的采样反复当常量用,这一讲把它推导出来。
- 网络层也必须无许可,否则中心服务器就能审查交易,前面所有密码学都白做。代价是没有全局视图、消息不可靠、邻居可能撒谎。
- ⚠️ 网络层的安全目标是可达性而不是保密——数据本来公开,要防的是"隔离"而不是"窃听”。
- gossip 的跳数是
log_d(n):一万节点、8 邻居只要约 4.4 跳。跳数随节点数对数增长,这是泛洪可行的根本原因。 - ⭐ 每一跳必须先验证再转发,否则就是一个放大 n 倍的 DoS。于是验证时间进入了传播时间——安全和速度在这一层直接冲突。
- ⭐⭐ 传播时间 ≈ 跳数 × (物理延迟 + 验证时间 + 传输时间),而三项里只有传输时间能靠加带宽改善。 光速给出的地球对跖点单向延迟下限约 100 ms,任何工程都消不掉——这才是"带宽够了就能调大区块"错在哪里。
- 同一个网络,出块间隔 600 秒 → 12 秒,孤块率涨约 50 倍。 出块间隔和网络层不是独立的旋钮。
- Compact Blocks 把 1 MB 压到约 15 KB,靠的不是压缩算法,是交易早就通过交易池传过去了,出块时只传增量——代价是缺交易时多一个 RTT。
- ⚠️ 中继网络是一层中心化基础设施:它降低了延迟,同时在网络层引入了新的信任点(第 34 讲 PBS 会放大这个问题)。
- Erlay 用集合协调代替泛洪,把交易传播带宽从
O(邻居数)降到近似O(1)。⭐ 带宽是跑节点的门槛,降带宽直接兑换成第 3 讲意义上的去中心化。 - ⭐⭐ Eclipse 攻击不攻击共识算法,它攻击共识证明的前提——“诚实节点之间能互相通信”。 前提不成立时,算法一行没错,结论照样不成立。
- ⚠️ PoW/PoS 解决的是出块权的 Sybil,完全没有解决连接槽的 Sybil——在网络层开一万个节点仍然免费。所以 Eclipse 是两类链共有的问题。
- 首个广播交易的节点大概率就是发起者,这个关联在链上数据里完全看不见(第 33 讲)。
- ⭐ 出块间隔 = 传播时间 × 安全系数:比特币约 600×,以太坊约 10×,高速链接近 1×。“出块快"不是技术更强,是在网络裕度上取了更激进的位置,并在别处付账。
思考题
- 为什么网络层不能用"一台服务器广播给所有人”?请从第 1 讲的出发点推导,而不是直接说"不去中心化"。
- 每个节点 12 个邻居、全网 5 万个节点,覆盖全网要几跳?如果节点数涨到 50 万呢?说明这个增长的性质。
- 如果 gossip 转发时不验证,攻击者能造成多大的放大?给出一个量化的估计。
- 传播时间的三个分量里,为什么只有一项能靠加带宽改善?请分别说明另外两项受什么限制。
- 用本讲的模型算:把区块从 1 MB 加到 8 MB(其余不变),传播时间和 12 秒出块下的孤块率各变成多少?
- Compact Blocks 把 1 MB 压到 15 KB,它压缩的到底是什么?在什么情况下它反而会变慢?
- 中继网络降低了延迟,它引入了什么新的信任假设?如果所有大矿工都用同一个中继商,会发生什么?
- Erlay 用带宽换了延迟。为什么这笔交换在"去中心化"这个目标下是划算的?
- 一条 PoS 链的开发者说"我们用 PoS,所以不怕 Sybil 攻击"。这句话在什么意义上对、什么意义上错?
- 为什么说 Eclipse 攻击"打的是共识证明的前提"?请写出中本聪共识安全性论证里被它推翻的那一条假设,并说明推翻之后结论为什么就不成立了。