术语表 /节点与软件
Headers-first 同步
- Headers-first 同步是什么?
- Headers-first 同步是一种启动策略:节点先下载并验证整条由80字节区块头组成的链,再请求区块体,然后并行获取这些区块体。
把下载分成两步,是首次同步得以实际可行的原因。Bitcoin Core 自0.10.0版本起就采用这种方式,该版本发布于2015年2月,它把区块获取从单一的有序队列变成了分散到所有已连接节点上的任务。如果您运行自己的节点来核实自己的余额,这就是初始同步只需数小时而不是数天的原因。
运作方式
Headers-first 同步从向对等节点索取区块头开始,而不是索取区块。
节点发送一条 getheaders 消息,其中带有区块定位器,即一份紧凑的哈希列表,用来说明节点目前认为自己所处的位置。对等节点回复一条 headers 消息,其中最多包含2,000个区块头,每个80字节,因此一次回复大约覆盖两周的链。对于每个区块头,节点检查它是否指向自己已有的父区块,以及它的哈希是否满足其 nBits 字段所声明的难度,然后从新的链顶继续询问,直到没有任何对等节点能提供更新的区块头。
只有这个骨架就位之后,区块体下载才会开始。由于节点已经知道自己所需每个区块的哈希,它可以同时向多个对等节点请求不同的区块段,而不必在某一条连接后面排队。Bitcoin Core 会限制自己对任一对等节点保留的未完成区块请求数量,当前代码中为十六个,并断开使窗口停滞的对等节点。先于其父区块到达的区块只会被暂存,这取代了过去把未验证数据暂存在磁盘上的旧做法。
区块头还附带两项保护措施。在所持区块头累计的工作量证明超过发布版本中编译的某个最低值之前,软件不会确认某条链,因此对等节点无法把新节点引诱到由容易的区块构成的廉价侧链上。另外,assumevalid 设置指定了每个版本随附的一个较新的区块哈希:埋在该区块之下的各区块中的签名不会被重新检查,这省去了数小时的椭圆曲线运算,而所有依赖当前链状态的规则仍然会被强制执行。
在哪里会遇到它
Headers-first 同步表现为节点追赶进度时两个互不一致的计数器。
在首次同步期间调用 getblockchaininfo,headers 字段会远远领先于 blocks 字段,往往相差数十万。图形界面客户端用通俗的语言表达了同一件事:只有在区块头到达之后,它才能估算完成时间,因为在此之前它不知道自己还要走多远。观察这两个数字逐渐接近,才是真实的进度条。
轻量级软件会刻意在前半部分就停下。SPV 钱包、紧凑过滤器客户端,或没有自己链后端的闪电网络节点只保留区块头,并在其上叠加默克尔证明或过滤器。这正是手机钱包能够通过移动数据核对自己历史记录的全部原因:区块头体积小且大小固定,区块体则两者都不是。
Headers-first 同步 vs blocks-first 同步
Headers-first 同步先了解链的形态,再下载其内容,而 blocks-first 同步则试图同时完成两者。旧方法发送一条 getblocks 消息,收到一条列出不超过500个区块哈希的 inv 消息,再从先回复的任一对等节点逐个拉取,因此一条缓慢的连接会拖慢其后的一切。乱序到达的数据只能存放在内存中的暂存池里,而攻击者可以用垃圾数据把它塞满。自0.10.0起,Bitcoin Core 就不再以这种方式同步,但它仍能识别这些消息,以便较旧的软件可以与其通信。