快照与快速同步
加入一条已有多年历史的链的节点,不必从区块 0 起重放每个区块。它跟随脊链,下载脊链所指定的最新快照,逐个分块地核对它,并与对等节点交叉核对,然后只应用此后产出的区块。这就是快速同步。在任何时候,节点都不会仅凭某个网站或单个对等节点的一面之词来认定链的状态。
快照
快照是链在某一高度的完整状态,包括每一个余额、席位和记录。每隔 main_snapshot_interval 个区块(mainnet 上为 172,800 个区块,约 30 天),每个节点都会为触发高度以下 760 个区块处的状态写一份快照。(devnet 上:每 240 个区块一次,深度 80 个区块。)各节点会把这项工作错开最多 60 个区块,但每个节点的快照逐字节完全相同。
快照是一个 CBOR 文档,包含链的状态表(共 57 张)、最近的区块头、最近的交易、仍在付费保存的交易(见 付费保存数据(存续)),以及最近发布的回执。它被切成 102,400 字节的分块,每个分块以其 SHA3-256 哈希命名。这些哈希组成的列表,加上整个文件的哈希、参考高度、有效期和该链的创世哈希,构成清单,由一个脊链区块发布。
快照内部没有任何签名。它的权威来自指定它的脊链区块:一个由三分之二席位联署的区块,而每个席位都先把清单与自己的快照逐个哈希比对过。
每个节点只保留最新的三代快照。自区块 0 以来的每一份快照由去中心化存储保存,而不是由每个节点保存:每份快照的分块由抽签选出的五个已注册节点持有(见 归档与读取过去)。
快速同步的步骤
当节点落后至少 100 个区块、且持有一份高于自身高度的可用清单时,它会尝试快速同步。它必须持有携带该清单的脊链区块及其签名。最新的清单即使过了有效期也仍然可用。
- 同步脊链。 远远落后的节点,只要一个脊链区块带有该区块自身所需门槛的签名(三分之二,后备区块则为过半数),即使该区块的参考区块高于自己的链顶,也会存储它。等到自己的主链到达该参考区块时,它再检查该区块的内容,若不匹配则停止并发出警报。
- 获取分块,从已连接的已注册对等节点处获取,最多分为 16 组,并按命名每个分块的哈希逐一核对。
- 核对整个文件,对照清单中的完整文件哈希。
- 加载状态,在单个数据库事务中完成。
- 与对等节点核对样本(见下文)。
- 应用剩余区块,逐一进行完整验证,直到链顶。
见证:与对等节点核对状态
哈希相符只能证明这些字节就是清单所指定的字节,并不能证明这些字节就是真实的状态。脊链的联署者已经会把每份清单与自己的快照比对;从 0.5.8 版本起,新加入的节点还会自行再做一项检查,以防收到的是一份伪造的清单。
加载完成后,节点从新状态中挑选至多 8 个账户和 8 个注册表节点。选择由快照自身的哈希驱动,因此每个新加入的节点挑选的都是相同的行,伪造者无法事先知道该把哪些行做得前后一致。节点向 3 个随机的已注册对等节点询问每个账户在快照高度的余额,以及每个节点在该高度的密钥和状态。关于同一条链在同一高度的诚实回答完全一致。
- 至少 2 个对等节点一致且没有矛盾:节点继续。
- 矛盾的回答未被压倒:它把询问范围扩大到 7 个对等节点,此时需要至少 2 个一致的回答,且至少占应答者的三分之二。
- 否则,它拒绝该快照,回退为逐块同步。
如果应答的对等节点太少,节点就仅凭哈希继续。
为什么结果值得信任
- 指定该快照的脊链区块,是在每个联署者把清单与自己的快照比对之后,由三分之二的席位联署的。
- 每个分块以及整个文件都必须与清单中的哈希相符。
- 只要有足够的对等节点应答,就会从加载后的状态中抽取一个样本,与随机选出的独立已注册对等节点核对。
- 快照之后的每个区块都经过完整验证。
- 脊链本身可以由随节点发布版本附带的版本锚点来锚定。
没有人手工为快照签名,不信任任何网站,也不从区块 0 起重放任何东西。