Substrate运行时std/no_std属性与WASM二进制引入逻辑疑问
解答
你的原有认知并没有错误,混淆「运行时代码的编译目标」和「Substrate节点的完整构建流程」是产生困惑的核心原因。
首先明确Substrate节点的构建流程
你在编译完整的Substrate节点时,运行时代码会先后经历两次独立编译:
- 第一步:将运行时代码关闭
std特性编译为Wasm二进制,产物会以静态字节数组的形式,生成到构建输出目录下的wasm_binary.rs文件中。 - 第二步:将运行时代码开启
std特性编译为原生机器码,和节点客户端其他依赖标准库的模块(P2P网络、RPC接口、数据库等)链接,生成最终的节点原生二进制。
对应代码的含义解释
你看到的两段代码分别适配两次编译的需求:
顶部属性声明
#![cfg_attr(not(feature = "std"), no_std)]
这段代码的逻辑完全符合你的原有认知:
- 当编译目标为Wasm时,关闭
std特性,触发no_std配置,仅使用core、alloc等无标准库依赖,适配Wasm沙箱的执行环境。 - 当编译目标为原生二进制时,开启
std特性,正常使用Rust标准库。
Wasm二进制嵌入逻辑
#[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
这段代码的作用是仅在编译原生节点时,把第一步生成的Wasm运行时字节数组嵌入到原生二进制中,这里的「设为可用」指的是让原生节点可以访问到初始版本的Wasm运行时资源,之所以要这么做,有两个核心原因:
- 运行时可以通过链上治理升级,升级后的Wasm字节码会存储在链上,所有节点执行交易时优先使用链上存储的最新Wasm运行时,而非本地原生编译的版本,保证全网共识一致,即使本地节点版本落后,也能正确执行最新的链逻辑。
- 同步历史区块时,需要使用对应区块高度的旧版本运行时逻辑执行交易,Wasm版本的运行时可以直接从链上读取,完美适配历史执行的需求。
「同时支持两种二进制」的含义
同一份运行时代码支持两种编译输出:
- 关闭
std特性编译得到的Wasm二进制:是链上执行的权威版本,全网通用,不受节点本地环境影响,是共识正确性的保证。 - 开启
std特性编译得到的原生二进制:属于性能优化项,只有当链上存储的Wasm运行时版本和本地原生版本完全一致时,节点可以选择用原生版本执行交易,速度远快于Wasm虚拟机执行,但不会改变共识结果。
误区澄清
开启std特性编译时,并没有同时使用Rust标准库和Wasm运行时,只是把Wasm二进制作为静态资源嵌入到原生程序中,和你把图片、配置文件嵌入程序的逻辑完全一致,这个嵌入的Wasm字节码不会在原生编译阶段被执行,只有当节点需要加载对应版本的运行时时,才会把它放到Wasm虚拟机中运行。
内容的提问来源于stack exchange,提问作者mkl
相关产品推荐
相关产品推荐

