区块导入过程
导入队列
导入队列是存在于每个 Substrate 节点中的抽象工作队列。 它并不是 Rumtime 的一部分。 导入队列负责处理并且验证外来信息,如果信息是有效的,就将它们导入到节点的状态中。 导入队列处理的最基本信息就是区块本身,同时它也负责导入与共识相关的消息,如理由声明等,以及在轻客户端中确定性证明。
导入队列从网络中搜集传入元素,并将它们存储到一个池子中。 这些元素随后会被检查是否有效,如果无效则会被丢弃。 有效的元素将被导入到节点的本地状态里。
导入队列在 Substrate 中通过 ImportQueue
trait 进行抽象编码。 Trait的使用使每个共识引擎都能实现自己专门的导入队列,这将为导入队列的信息处理提供了一些优化机会,如可对同时进入网络的多个区块进行并行验证。
导入队列还通过 Link
trait 提供了一些 Hook,以便跟进其处理进度。
基本队列
Substrate 提供了 ImportQueue
的一种默认内存实现 称为BasicQueue
. BasicQueue
不做任何优化工作,只是依次执行验证和导入步骤。 然而,它确实通过使用Verifier
特质抽象出了验证的概念。Verifier.html">Verifier
这个trait,抽象出了验证的概念。
任何依赖于 BasicQueue
的共识引擎都必须要实现 Verifier
trait。 Verifier
的职责通常包括检查 inherent data,以及确保区块由适当的权威节点签名等。
Block Import Trait
当导入队列准备好导入一个区块时,它会将有关的区块传递给BlockImport
trait提供的一个方法。 此 BlockImport
trait相应地会执行将区块导入节点本地状态数据库的动作。
在每个Substrate节点中都会使用到的一个 BlockImport
trait的实现就是 Client
了,它包含了节点的整个区块数据库。 当一个区块被导入到客户端时,它就会被添加到该节点的已知区块库中。
区块导入管道
在最简单的情况下,区块会直接导入到客户端。 但是大多数共识引擎需要对导入的区块进行额外的验证,或者需要更新自己的本地备用数据库,或两者兼而有之。 为了让共识引擎能够达成其需求,我们通常会将客户端包在另一个同样实现了 BlockImport
trait的结构体当中。 该嵌套方式就使 "区块导入管道 "这个术语应运而生。
封装的一个典型例子就是 PowBlockImport
,它引用了另一个同样实现了BlockImport
trait的类型。 这就使得 PoW 共识引擎可以先进行与导入相关的记账,然后再将区块传递给嵌套的 BlockImport
模块,如客户端。 这种模式在 AuraBlockImport
, BabeBlockImport
, 和 GrandpaBlockImport
中也都有体现。
BlockImport
的嵌套不止限于一层。 事实上,对于同时使用出块引擎和终块确认引擎的节点来说,多层次嵌套十分常见。 例如,Polkadot 的区块导入管道包含了 BabeBlockImport
模块,该模块封装了一个GrandpaBlockImport
,而这个 GrandpaBlockImport 又封装了一层 Client
实现。
进一步学习
有几个 Recipes 的示例演示了区块导入管道是如何工作的: