区块种子(VRF)
每个 ZooBC 区块都携带一个 64 字节的种子,它是链的随机性来源。它决定谁可以产出下一个区块、谁赢得 coinbase 抽奖、出块者出示哪些回执,以及对等节点之间如何相互指派。从 0.5.8 版本起,种子是一个可验证随机函数(VRF)的输出:对于给定的出块者和给定的前一个种子,只存在唯一一个有效值,而区块头中一个简短的证明让每个节点都能校验它。
为什么种子不能被选择
如果出块者可以从多个候选值中挑选其区块的种子,它就能把下一次出块者抽签引向自己或同伙,让 coinbase 抽奖发生倾斜,或者决定某个应用中一笔投注的结果。普通的数字签名不足以充当种子:同一条消息上可以存在不止一个有效签名,因此出块者可以尝试多个签名,保留自己中意的那个。
VRF 堵住了这扇门。它就像一个只有唯一有效结果的签名:持有密钥的人可以计算出输出和证明,任何拥有公钥的人都可以验证该证明,而对于同一密钥和同一输入,每一个有效证明都产生相同的输出。
种子是如何生成的
- 输入称为 alpha,是前一个区块 64 字节种子的 SHA3-256 哈希(32 字节)。
- 出块者用其节点密钥(见 节点的密钥)运行基于 Ed25519 的 ECVRF,采用 ZooBC 变体。
- 输出就是新的 64 字节种子。证明为 80 字节:一个 32 字节的曲线点、一个 16 字节的挑战值和一个 32 字节的标量。
- 证明放入区块头,而区块签名覆盖区块头:被签名的区块头字节共 284 个(其他字段的 204 字节加上 80 字节的证明),参与哈希的字节共 348 个。
验证者要求提供证明,用出块者的密钥针对 alpha 验证该证明,并拒绝种子不等于证明输出的区块。
新区块中的任何内容都不是输入:既不是其交易,也不是其时间戳。因此出块者无法通过重新安排其区块来操纵种子,而种子链可以从任意区块一直核验回创世区块,创世区块的种子全为零。来自出块者自己计算机的随机数无法被任何人验证,所以不使用这种随机数。
ZooBC 变体
ZooBC 的 ECVRF 是 ECVRF-EDWARDS25519-SHA512-TAI 的一个变体。映射到曲线时只接受素数阶的候选点,并且不乘以余因子直接使用它们;验证时还会拒绝挑战值或标量为零的证明。因此,标准的 RFC 9381 库会给出不同的输出;任何编写节点或验证器的人都必须实现这一变体。
大约每 1500 万对密钥和输入中就有一对(概率为 (15/16)^256,约 6.7 × 10^-8)不存在有效证明。此时被抽中的出块者无法产出该区块,轮次转给下一轮的出块者。
唯一剩下的手段:弃权
被抽中的出块者如果不喜欢其区块将携带的种子,只有一个选择:扣住区块不发。轮次随即在 15 秒后转到下一个被抽中的节点,由它用自己的种子产出区块,而这个种子同样不可预测。扣块只是把骰子重掷一次;它永远无法选择结果。
这也有代价。被跳过的出块者会记一次错过轮次,并在其下一个自己的区块之前处于“缺席”状态,没有任何收入,还会失去参与分数(见 错过轮次、暂停与除名)。较晚的区块在分叉选择中的权重也更低。参与证明的设计分析估计,持有席位比例为 a 的对手能提前 n 个区块预测种子的概率约为 a 的 n 次方:持有三分之一的席位时,提前三个区块预测的概率约为 1/27。
还有两条规则堵住了旁门:
- 节点密钥的更换要在包含该更换的区块之后 120 个区块(30 分钟)才生效,因此出块者无法用预先签好的密钥轮换来操纵同伙的下一个种子。
- 前一个区块的出块者在下一次抽签中被降级,因此只要还有其他节点能够应答,就没有任何节点能同时掌握一个区块的内容和裁定这些内容的种子(见 优先轮次、空区块与前一个出块者)。
种子决定什么
- 下一个区块的出块者抽签(见 抽签);
- coinbase 抽奖 的中奖者;
- 出块者必须出示的回执(见 回执与网络拓扑);
- 对等节点拓扑,取自每个 40 区块周期中第一个区块的种子;
- 应用中的随机抽签。
种子是公开的:任何人都可以从链上读取它们,并重现上述每一次抽签。