mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 15:47:58 +00:00
Embed runtime version as a custom section (#8688)
* emit a custom section from impl_runtime_apis! This change emits a custom section from the impl_runtime_apis! proc macro. Each implemented API will result to emitting a link section `runtime_apis`. During linking all sections with this name will be concatenated and placed into the final wasm binary under the same name. * Introduce `runtime_version` proc macro This macro takes an existing `RuntimeVersion` const declaration, parses it and emits the version information in form of a linking section. Ultimately such a linking section will result into a custom wasm section. * Parse custom wasm section for runtime version * Apply suggestions from code review Co-authored-by: David <dvdplm@gmail.com> * Fix sc-executor integration tests * Nits Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Refactor apis section deserialization * Fix version decoding * Reuse uncompressed value for CallInWasm * Log on decompression error * Simplify if * Reexport proc-macro from sp_version * Merge ReadRuntimeVersionExt * Export `read_embedded_version` * Fix test * Simplify searching for custom section Co-authored-by: David <dvdplm@gmail.com> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
@@ -33,14 +33,14 @@ use sp_version::{NativeVersion, RuntimeVersion};
|
||||
use codec::{Decode, Encode};
|
||||
use sp_core::{
|
||||
NativeOrEncoded,
|
||||
traits::{
|
||||
CodeExecutor, Externalities, RuntimeCode, MissingHostFunctions,
|
||||
RuntimeSpawnExt, RuntimeSpawn,
|
||||
},
|
||||
traits::{CodeExecutor, Externalities, RuntimeCode, RuntimeSpawnExt, RuntimeSpawn},
|
||||
};
|
||||
use log::trace;
|
||||
use sp_wasm_interface::{HostFunctions, Function};
|
||||
use sc_executor_common::wasm_runtime::{WasmInstance, WasmModule, InvokeMethod};
|
||||
use sc_executor_common::{
|
||||
wasm_runtime::{WasmInstance, WasmModule, InvokeMethod},
|
||||
runtime_blob::RuntimeBlob,
|
||||
};
|
||||
use sp_externalities::ExternalitiesExt as _;
|
||||
use sp_tasks::new_async_externalities;
|
||||
|
||||
@@ -188,64 +188,81 @@ impl WasmExecutor {
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform a call into the given runtime.
|
||||
///
|
||||
/// The runtime is passed as a [`RuntimeBlob`]. The runtime will be isntantiated with the
|
||||
/// parameters this `WasmExecutor` was initialized with.
|
||||
///
|
||||
/// In case of problems with during creation of the runtime or instantation, a `Err` is returned.
|
||||
/// that describes the message.
|
||||
#[doc(hidden)] // We use this function for tests across multiple crates.
|
||||
pub fn uncached_call(
|
||||
&self,
|
||||
runtime_blob: RuntimeBlob,
|
||||
ext: &mut dyn Externalities,
|
||||
allow_missing_host_functions: bool,
|
||||
export_name: &str,
|
||||
call_data: &[u8],
|
||||
) -> std::result::Result<Vec<u8>, String> {
|
||||
let module = crate::wasm_runtime::create_wasm_runtime_with_code(
|
||||
self.method,
|
||||
self.default_heap_pages,
|
||||
runtime_blob,
|
||||
self.host_functions.to_vec(),
|
||||
allow_missing_host_functions,
|
||||
self.cache_path.as_deref(),
|
||||
)
|
||||
.map_err(|e| format!("Failed to create module: {:?}", e))?;
|
||||
|
||||
let instance = module
|
||||
.new_instance()
|
||||
.map_err(|e| format!("Failed to create instance: {:?}", e))?;
|
||||
|
||||
let instance = AssertUnwindSafe(instance);
|
||||
let mut ext = AssertUnwindSafe(ext);
|
||||
let module = AssertUnwindSafe(module);
|
||||
|
||||
with_externalities_safe(&mut **ext, move || {
|
||||
preregister_builtin_ext(module.clone());
|
||||
instance.call_export(export_name, call_data)
|
||||
})
|
||||
.and_then(|r| r)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_core::traits::CallInWasm for WasmExecutor {
|
||||
fn call_in_wasm(
|
||||
impl sp_core::traits::ReadRuntimeVersion for WasmExecutor {
|
||||
fn read_runtime_version(
|
||||
&self,
|
||||
wasm_code: &[u8],
|
||||
code_hash: Option<Vec<u8>>,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
ext: &mut dyn Externalities,
|
||||
missing_host_functions: MissingHostFunctions,
|
||||
) -> std::result::Result<Vec<u8>, String> {
|
||||
let allow_missing_host_functions = missing_host_functions.allowed();
|
||||
let runtime_blob = RuntimeBlob::uncompress_if_needed(&wasm_code)
|
||||
.map_err(|e| format!("Failed to create runtime blob: {:?}", e))?;
|
||||
|
||||
if let Some(hash) = code_hash {
|
||||
let code = RuntimeCode {
|
||||
code_fetcher: &sp_core::traits::WrappedRuntimeCode(wasm_code.into()),
|
||||
hash,
|
||||
heap_pages: None,
|
||||
};
|
||||
|
||||
self.with_instance(&code, ext, allow_missing_host_functions, |module, instance, _, mut ext| {
|
||||
with_externalities_safe(
|
||||
&mut **ext,
|
||||
move || {
|
||||
RuntimeInstanceSpawn::register_on_externalities(module.clone());
|
||||
instance.call_export(method, call_data)
|
||||
}
|
||||
)
|
||||
}).map_err(|e| e.to_string())
|
||||
} else {
|
||||
let module = crate::wasm_runtime::create_wasm_runtime_with_code(
|
||||
self.method,
|
||||
self.default_heap_pages,
|
||||
&wasm_code,
|
||||
self.host_functions.to_vec(),
|
||||
allow_missing_host_functions,
|
||||
self.cache_path.as_deref(),
|
||||
)
|
||||
.map_err(|e| format!("Failed to create module: {:?}", e))?;
|
||||
|
||||
let instance = module.new_instance()
|
||||
.map_err(|e| format!("Failed to create instance: {:?}", e))?;
|
||||
|
||||
let instance = AssertUnwindSafe(instance);
|
||||
let mut ext = AssertUnwindSafe(ext);
|
||||
let module = AssertUnwindSafe(module);
|
||||
|
||||
with_externalities_safe(
|
||||
&mut **ext,
|
||||
move || {
|
||||
RuntimeInstanceSpawn::register_on_externalities(module.clone());
|
||||
instance.call_export(method, call_data)
|
||||
}
|
||||
)
|
||||
.and_then(|r| r)
|
||||
.map_err(|e| e.to_string())
|
||||
if let Some(version) = crate::wasm_runtime::read_embedded_version(&runtime_blob)
|
||||
.map_err(|e| format!("Failed to read the static section: {:?}", e))
|
||||
.map(|v| v.map(|v| v.encode()))?
|
||||
{
|
||||
return Ok(version);
|
||||
}
|
||||
|
||||
// If the blob didn't have embedded runtime version section, we fallback to the legacy
|
||||
// way of fetching the verison: i.e. instantiating the given instance and calling
|
||||
// `Core_version` on it.
|
||||
|
||||
self.uncached_call(
|
||||
runtime_blob,
|
||||
ext,
|
||||
// If a runtime upgrade introduces new host functions that are not provided by
|
||||
// the node, we should not fail at instantiation. Otherwise nodes that are
|
||||
// updated could run this successfully and it could lead to a storage root
|
||||
// mismatch when importing this block.
|
||||
true,
|
||||
"Core_version",
|
||||
&[],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,29 +453,25 @@ impl RuntimeInstanceSpawn {
|
||||
ext.extension::<sp_core::traits::TaskExecutorExt>()
|
||||
.map(move |task_ext| Self::new(module, task_ext.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Register new `RuntimeSpawnExt` on current externalities.
|
||||
///
|
||||
/// This extensions will spawn instances from provided `module`.
|
||||
pub fn register_on_externalities(module: Arc<dyn WasmModule>) {
|
||||
sp_externalities::with_externalities(
|
||||
move |mut ext| {
|
||||
if let Some(runtime_spawn) =
|
||||
Self::with_externalities_and_module(module.clone(), ext)
|
||||
{
|
||||
if let Err(e) = ext.register_extension(
|
||||
RuntimeSpawnExt(Box::new(runtime_spawn))
|
||||
) {
|
||||
trace!(
|
||||
target: "executor",
|
||||
"Failed to register `RuntimeSpawnExt` instance on externalities: {:?}",
|
||||
e,
|
||||
)
|
||||
}
|
||||
}
|
||||
/// Pre-registers the built-in extensions to the currently effective externalities.
|
||||
///
|
||||
/// Meant to be called each time before calling into the runtime.
|
||||
fn preregister_builtin_ext(module: Arc<dyn WasmModule>) {
|
||||
sp_externalities::with_externalities(move |mut ext| {
|
||||
if let Some(runtime_spawn) =
|
||||
RuntimeInstanceSpawn::with_externalities_and_module(module, ext)
|
||||
{
|
||||
if let Err(e) = ext.register_extension(RuntimeSpawnExt(Box::new(runtime_spawn))) {
|
||||
trace!(
|
||||
target: "executor",
|
||||
"Failed to register `RuntimeSpawnExt` instance on externalities: {:?}",
|
||||
e,
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl<D: NativeExecutionDispatch + 'static> CodeExecutor for NativeExecutor<D> {
|
||||
@@ -506,7 +519,7 @@ impl<D: NativeExecutionDispatch + 'static> CodeExecutor for NativeExecutor<D> {
|
||||
with_externalities_safe(
|
||||
&mut **ext,
|
||||
move || {
|
||||
RuntimeInstanceSpawn::register_on_externalities(module.clone());
|
||||
preregister_builtin_ext(module.clone());
|
||||
instance.call_export(method, data).map(NativeOrEncoded::Encoded)
|
||||
}
|
||||
)
|
||||
@@ -557,17 +570,13 @@ impl<D: NativeExecutionDispatch> Clone for NativeExecutor<D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: NativeExecutionDispatch> sp_core::traits::CallInWasm for NativeExecutor<D> {
|
||||
fn call_in_wasm(
|
||||
impl<D: NativeExecutionDispatch> sp_core::traits::ReadRuntimeVersion for NativeExecutor<D> {
|
||||
fn read_runtime_version(
|
||||
&self,
|
||||
wasm_blob: &[u8],
|
||||
code_hash: Option<Vec<u8>>,
|
||||
method: &str,
|
||||
call_data: &[u8],
|
||||
wasm_code: &[u8],
|
||||
ext: &mut dyn Externalities,
|
||||
missing_host_functions: MissingHostFunctions,
|
||||
) -> std::result::Result<Vec<u8>, String> {
|
||||
self.wasm.call_in_wasm(wasm_blob, code_hash, method, call_data, ext, missing_host_functions)
|
||||
self.wasm.read_runtime_version(wasm_code, ext)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user