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:
Sergei Shulepov
2021-05-12 15:39:08 +02:00
committed by GitHub
parent 4f7c1df31e
commit 0849bcce0e
27 changed files with 833 additions and 289 deletions
@@ -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)
}
}