Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Embed runtime version as a custom section #8688

Merged
17 commits merged into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ members = [
"primitives/trie",
"primitives/utils",
"primitives/version",
"primitives/version/proc-macro",
"primitives/wasm-interface",
"test-utils/client",
"test-utils/derive",
Expand Down
1 change: 1 addition & 0 deletions bin/node-template/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ sp-session = { version = "3.0.0", default-features = false, path = "../../../pri
sp-std = { version = "3.0.0", default-features = false, path = "../../../primitives/std" }
sp-transaction-pool = { version = "3.0.0", default-features = false, path = "../../../primitives/transaction-pool" }
sp-version = { version = "3.0.0", default-features = false, path = "../../../primitives/version" }
sp-version-proc-macro = { version = "3.0.0", path = "../../../primitives/version/proc-macro" }
pepyakin marked this conversation as resolved.
Show resolved Hide resolved

# Used for the node template's RPCs
frame-system-rpc-runtime-api = { version = "3.0.0", default-features = false, path = "../../../frame/system/rpc/runtime-api/" }
Expand Down
1 change: 1 addition & 0 deletions bin/node-template/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ pub mod opaque {

// To learn more about runtime versioning and what each of the following value means:
// https://substrate.dev/docs/en/knowledgebase/runtime/upgrades#runtime-versioning
#[sp_version_proc_macro::runtime_version]
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node-template"),
impl_name: create_runtime_str!("node-template"),
Expand Down
1 change: 1 addition & 0 deletions bin/node/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ sp-keyring = { version = "3.0.0", optional = true, path = "../../../primitives/k
sp-session = { version = "3.0.0", default-features = false, path = "../../../primitives/session" }
sp-transaction-pool = { version = "3.0.0", default-features = false, path = "../../../primitives/transaction-pool" }
sp-version = { version = "3.0.0", default-features = false, path = "../../../primitives/version" }
sp-version-proc-macro = { version = "3.0.0", path = "../../../primitives/version/proc-macro" }
sp-npos-elections = { version = "3.0.0", default-features = false, path = "../../../primitives/npos-elections" }

# frame dependencies
Expand Down
1 change: 1 addition & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ pub fn wasm_binary_unwrap() -> &'static [u8] {
}

/// Runtime version.
#[sp_version_proc_macro::runtime_version]
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node"),
impl_name: create_runtime_str!("substrate-node"),
Expand Down
15 changes: 15 additions & 0 deletions client/executor/common/src/runtime_blob/runtime_blob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,24 @@ impl RuntimeBlob {
})
}

/// Scans the wasm blob for the first section with the name that matches the given. Returns the
/// contents of the custom section if found or `None` otherwise.
pub fn custom_section_contents(&self, section_name: &str) -> Option<&[u8]> {
self.raw_module
.custom_sections()
.filter(|cs| cs.name() == section_name)
.next()
.map(|cs| cs.payload())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.filter(|cs| cs.name() == section_name)
.next()
.map(|cs| cs.payload())
.filter_map(|cs| (cs.name() == section_name).then(|| cs.payload()))

Copy link
Contributor Author

@pepyakin pepyakin May 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not entirely sure how would that work. Currently, this code takes an iterator of custom sections, leaves only sections that we are interested in, takes the first one which gives us an option and then we narrow the contents of this section to only its payload. But in the proposed version the result seems to be not an option

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL. This however misses the point, if I understand you correctly. The problem is the expression in the proposed code is typed as Iterator whereas the function returns an Option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filter + next is a find though.

We can simplify the code to Iterator::find + Option::map. That will reduce the number of combinators by one.

We could use a Iterator::find_map, but that would use nested bool::then. But I consider nested combis harder to reason about and I would guess bool::then also is not among commonly used functions out there.

}

/// Consumes this runtime blob and serializes it.
pub fn serialize(self) -> Vec<u8> {
serialize(self.raw_module)
.expect("serializing into a vec should succeed; qed")
}

/// Destructure this structure into the underlying parity-wasm Module.
pub fn into_inner(self) -> RawModule {
self.raw_module
}
}
53 changes: 28 additions & 25 deletions client/executor/src/integration_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
mod sandbox;

use std::sync::Arc;
use codec::{Encode, Decode};
use hex_literal::hex;
use sp_core::{
Expand All @@ -29,6 +30,10 @@ use sp_state_machine::TestExternalities as CoreTestExternalities;
use sp_trie::{TrieConfiguration, trie_types::Layout};
use sp_wasm_interface::HostFunctions as _;
use sp_runtime::traits::BlakeTwo256;
use sc_executor_common::{
wasm_runtime::WasmModule,
runtime_blob::RuntimeBlob,
};
use tracing_subscriber::layer::SubscriberExt;

use crate::WasmExecutionMethod;
Expand Down Expand Up @@ -553,16 +558,30 @@ fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) {
assert!(err.contains("Allocator ran out of space"));
}

test_wasm_execution!(returns_mutable_static);
fn returns_mutable_static(wasm_method: WasmExecutionMethod) {
let runtime = crate::wasm_runtime::create_wasm_runtime_with_code(
fn mk_test_runtime(wasm_method: WasmExecutionMethod, pages: u64) -> Arc<dyn WasmModule> {
let raw =
sp_maybe_compressed_blob::decompress(
&wasm_binary_unwrap()[..],
sp_maybe_compressed_blob::CODE_BLOB_BOMB_LIMIT,
)
.expect("failed to uncompress the test runtime");
let blob = RuntimeBlob::new(&raw)
.expect("failed to create a runtime blob out of test runtime");

crate::wasm_runtime::create_wasm_runtime_with_code(
wasm_method,
1024,
&wasm_binary_unwrap()[..],
pages,
blob,
HostFunctions::host_functions(),
true,
None,
).expect("Creates runtime");
)
.expect("failed to instantiate wasm runtime")
}

test_wasm_execution!(returns_mutable_static);
fn returns_mutable_static(wasm_method: WasmExecutionMethod) {
let runtime = mk_test_runtime(wasm_method, 1024);

let instance = runtime.new_instance().unwrap();
let res = instance.call_export("returns_mutable_static", &[0]).unwrap();
Expand All @@ -589,14 +608,7 @@ fn restoration_of_globals(wasm_method: WasmExecutionMethod) {
// to our allocator algorithm there are inefficiencies.
const REQUIRED_MEMORY_PAGES: u64 = 32;

let runtime = crate::wasm_runtime::create_wasm_runtime_with_code(
wasm_method,
REQUIRED_MEMORY_PAGES,
&wasm_binary_unwrap()[..],
HostFunctions::host_functions(),
true,
None,
).expect("Creates runtime");
let runtime = mk_test_runtime(wasm_method, REQUIRED_MEMORY_PAGES);
let instance = runtime.new_instance().unwrap();

// On the first invocation we allocate approx. 768KB (75%) of stack and then trap.
Expand All @@ -610,14 +622,7 @@ fn restoration_of_globals(wasm_method: WasmExecutionMethod) {

test_wasm_execution!(interpreted_only heap_is_reset_between_calls);
fn heap_is_reset_between_calls(wasm_method: WasmExecutionMethod) {
let runtime = crate::wasm_runtime::create_wasm_runtime_with_code(
wasm_method,
1024,
&wasm_binary_unwrap()[..],
HostFunctions::host_functions(),
true,
None,
).expect("Creates runtime");
let runtime = mk_test_runtime(wasm_method, 1024);
let instance = runtime.new_instance().unwrap();

let heap_base = instance.get_global_const("__heap_base")
Expand Down Expand Up @@ -671,9 +676,7 @@ fn parallel_execution(wasm_method: WasmExecutionMethod) {

test_wasm_execution!(wasm_tracing_should_work);
fn wasm_tracing_should_work(wasm_method: WasmExecutionMethod) {

use std::sync::{Arc, Mutex};

use std::sync::Mutex;
use sc_tracing::{SpanDatum, TraceEvent};

struct TestTraceHandler(Arc<Mutex<Vec<SpanDatum>>>);
Expand Down
83 changes: 56 additions & 27 deletions client/executor/src/native_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ use sp_core::{
NativeOrEncoded,
traits::{
CodeExecutor, Externalities, RuntimeCode, MissingHostFunctions,
RuntimeSpawnExt, RuntimeSpawn,
RuntimeSpawnExt, RuntimeSpawn, ReadRuntimeVersionExt,
},
};
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;

Expand Down Expand Up @@ -213,16 +216,22 @@ impl sp_core::traits::CallInWasm for WasmExecutor {
with_externalities_safe(
&mut **ext,
move || {
RuntimeInstanceSpawn::register_on_externalities(module.clone());
preregister_builtin_ext(module.clone());
instance.call_export(method, call_data)
}
)
}).map_err(|e| e.to_string())
} else {
use sp_maybe_compressed_blob::CODE_BLOB_BOMB_LIMIT;
let wasm_code = sp_maybe_compressed_blob::decompress(wasm_code, CODE_BLOB_BOMB_LIMIT)
.map_err(|e| format!("Decompression error: {:?}", e))?;
let runtime_blob = RuntimeBlob::new(&wasm_code)
.map_err(|e| format!("Failed to create runtime blob: {:?}", e))?;

let module = crate::wasm_runtime::create_wasm_runtime_with_code(
self.method,
self.default_heap_pages,
&wasm_code,
runtime_blob,
self.host_functions.to_vec(),
allow_missing_host_functions,
self.cache_path.as_deref(),
Expand All @@ -239,7 +248,7 @@ impl sp_core::traits::CallInWasm for WasmExecutor {
with_externalities_safe(
&mut **ext,
move || {
RuntimeInstanceSpawn::register_on_externalities(module.clone());
preregister_builtin_ext(module.clone());
instance.call_export(method, call_data)
}
)
Expand Down Expand Up @@ -436,31 +445,51 @@ 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,
)
}
}
}
);
struct ReadRuntimeVersion;

impl sp_core::traits::ReadRuntimeVersion for ReadRuntimeVersion {
fn read_runtime_version(
&self,
wasm_code: &[u8],
) -> std::result::Result<Option<Vec<u8>>, String> {
let runtime_blob = RuntimeBlob::new(wasm_code)
.map_err(|e| format!("Failed to create runtime blob: {:?}", e))?;

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()))
}
}

/// 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,
)
}
}

if let Err(e) = ext.register_extension(ReadRuntimeVersionExt::new(ReadRuntimeVersion)) {
trace!(
target: "executor",
"Failed to register `ReadRuntimeVersionExt` instance on externalities: {:?}",
e,
)
}
});
}

impl<D: NativeExecutionDispatch + 'static> CodeExecutor for NativeExecutor<D> {
type Error = Error;

Expand Down Expand Up @@ -506,7 +535,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)
}
)
Expand Down
Loading