Runtime 执行流程
Substrate runtime的执行由Executive模块来协调。
与FRAME中的其他模块不同,Executive不是runtime模块, 而是一个普通的Rust模块,它负责调用区块链中包含的各种runtime模块。
Executive模块对外暴露了 execute_block
函数,以实现如下功能:
验证交易
在区块开始执行前,将检查签名交易的有效性。 这个行为不会产生任何副作用;它只是确保交易无论能否打包到区块,也不会引起程序崩溃。 因此, 对存储所作的更改将被丢弃。
执行区块
只要有效交易的队列不为空,Executive模块就开始执行区块。
初始化区块
区块初始化时,System模块和其他runtime模块都会首先调用其on_initialize
函数,把由模块定义的、需要前置的业务逻辑在交易执行前全部处理掉。 除System模块总是优先处理外,其余模块均按照在construct_runtime!
宏里定义的顺序来执行。
接下来是初始检查,该步骤将验证区块头中的父哈希是否正确,以及extrinsics trie的根是否囊括了所有的extrinsics。
执行Extrinsics
区块初始化完成以后,将按照交易优先级顺序执行每一个有效的extrinsic。 Extrinsics一定不能在rutnime逻辑中引起程序崩溃,否则系统将很容易受到用户攻击,而通过这种攻击,用户可不受任何惩罚地消耗计算资源。
当extrinsic执行时,原有存储状态不会提前被缓存下来,修改将直接应用到存储上。 因此,在更改存储状态之前,runtime开发人员应进行所有必要检查,以确保extrinsic能执行成功。 一旦extrinsic在执行过程中失败了,存储更改将不能回滚。
extrinsic执行时触发的事件也会写入存储。 因此,在完成所有待执行动作之前,不应该触发相关事件。 否则,倘若extrinsic在事件触发后才执行失败的话,该事件将不能回滚。
完结区块
执行完队列中所有的extrinsics后,Executive模块将调用每个模块的on_finalize
函数,以运行定义在区块末端的最终业务逻辑。 所有模块也将再次按照在 construct_runtime!
宏中定义的顺序来执行,但 System 模块须等到最后完成。
接下来是终极检查,将验证区块头中的摘要和存储根是否与计算结果相匹配。
后续步骤
进一步学习
- 了解如何在runtime测试中模拟Executive模块的协调功能。