回执与网络拓扑
区块链依靠节点之间相互传递区块和交易而存在。参与证明衡量的正是这项服务。当一个已注册节点从另一个已注册节点收到数据时,它会回复一份回执:一段用其节点密钥签名的简短声明。因此,工作的证明来自接收数据的对等节点,而不是来自声称完成工作的节点。哪些对等节点算数也不由节点自己决定:每隔 40 个区块,协议就用区块种子打乱注册表,并为每个节点分配其优先对等节点。
优先对等节点:拓扑
注册表每个 40 区块的周期(topology_period_blocks)重新打乱一次,约为 10 分钟。在一个周期的第一个区块处,每个已注册节点都会得到一个排序键:
key = SHA3-256(seed of the period's first block (64 bytes)
‖ node id (8 bytes, little-endian)
‖ period height (8 bytes, little-endian)
‖ "zbc-topology-v2")
按这些键对注册表排序,就得到打乱后的名单。每个节点从中被分配一个由 P 个连续节点组成的窗口,作为其优先对等节点,其中 P 为 max_assigned_peers:mainnet 上为 12,devnet 上为 8。位于位置 i 的节点,其窗口从位置 (i × P) mod L 开始,L 为名单长度;窗口始终包含完整的 P 个节点,当 P 与 L 有公因数时,各窗口的起点会被错开。这种关系是单向的:A 是 B 的优先对等节点,并不意味着 B 是 A 的优先对等节点。
由于顺序取决于一个它们谁都无法控制的种子,并且每 10 分钟变化一次,一群串通的节点无法安排彼此成为对等节点、只为彼此作保。连接速度不起任何作用,也没有人发布对等节点名单。
回执的内容
一份回执有 232 字节:
| 字段 | 字节数 |
|---|---|
| 发送方的节点密钥(推送数据的节点) | 32 |
| 接收方的节点密钥(签名的节点) | 32 |
| 参考区块:接收方的最新区块,高度 | 4 |
| 参考区块:哈希 | 32 |
| 数据类型:1 表示区块,2 表示交易 | 4 |
| 数据哈希:收到的是哪个区块或哪笔交易 | 32 |
| 接收方最新的回执批次根 | 32 |
| 接收方对以上 168 字节的 Ed25519 签名 | 64 |
只有已注册节点才出具并保存回执,且仅针对由另一个已注册节点推送的数据。未注册的节点可以跟随并检查链,但它既不出具也不赚取回执。回执不指明任何链,因此与交易不同(见 链绑定签名),它不带链绑定。
回执与钱包确认或手续费退还无关,那些是交易和余额的普通记录。
批次与发布
发送方保存它赚得的回执。对于每个高度,它把回执分组为最多 1,000 份的批次,按签名排序,置于一个默克尔根之下。当它产出自己的区块时,它会在其中发布回执的一个样本,分为两类:非链接回执,按原样出示;以及链接回执,附带针对较早批次根的默克尔证明出示。参考软件从大约 40 个区块之前采样回执;这是它的行为,而不是有效性规则。
验证者检查什么
对于区块中发布的每一份回执,每个节点都会检查以下各项,其中 n 为已注册节点的数量:
- 接收方的签名;
- 参考区块哈希是否为本链在该高度上的区块;
- 它的时效:回执在 max(max(100, 2 × n) + 1,
receipt_lifecycle) 个区块后过期,在 mainnet 上为 518,400 个区块(约 90 天); - 从高度 40 起,非链接回执的签名者是否为该区块出块者在回执所属周期内的优先对等节点;
- 从高度 80 起,每一份链接回执的默克尔证明;
- 数量:最多 2 × min(n − 1, P) 份回执,且永远不超过 200 份。
有些事情不是有效性规则,不应被误读为规则:验证者不检查回执的发送方是否为该区块的出块者、回执是否是新的、其数据是否约为 40 个区块之前的,也不检查链接的根此前是否已出现在链上。
从回执到分数
转发赚取的是回执,而不是分数。只有当节点在自己的区块中发布其回执时,分数才会变动:出示的数量多于一个约为其可出示数量一半的基准值,分数就上升;少于基准值,分数就下降。在高度 40 之前,分数不会有任何变动。详情见 参与分数,分数如何转化为收入见 coinbase 抽奖。