diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 6d8651dabd..82fabb30cf 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -1529,6 +1529,7 @@ dependencies = [ "frame-support", "parity-scale-codec", "pretty_assertions", + "rustversion", "serde", "sp-core", "sp-inherents", @@ -7643,6 +7644,7 @@ name = "sp-runtime-interface-test" version = "2.0.0-dev" dependencies = [ "sc-executor", + "sp-core", "sp-io", "sp-runtime", "sp-runtime-interface", diff --git a/substrate/client/executor/src/integration_tests/mod.rs b/substrate/client/executor/src/integration_tests/mod.rs index e1ef18a085..44e021729a 100644 --- a/substrate/client/executor/src/integration_tests/mod.rs +++ b/substrate/client/executor/src/integration_tests/mod.rs @@ -45,7 +45,6 @@ fn call_in_wasm( execution_method, Some(1024), HostFunctions::host_functions(), - true, 8, ); executor.call_in_wasm( @@ -54,6 +53,7 @@ fn call_in_wasm( function, call_data, ext, + sp_core::traits::MissingHostFunctions::Allow, ) } @@ -511,7 +511,6 @@ fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { wasm_method, Some(17), // `17` is the initial number of pages compiled into the binary. HostFunctions::host_functions(), - true, 8, ); executor.call_in_wasm( @@ -520,6 +519,7 @@ fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { "test_exhaust_heap", &[0], &mut ext.ext(), + sp_core::traits::MissingHostFunctions::Allow, ).unwrap(); } diff --git a/substrate/client/executor/src/lib.rs b/substrate/client/executor/src/lib.rs index c3b41bd199..3d7db630f0 100644 --- a/substrate/client/executor/src/lib.rs +++ b/substrate/client/executor/src/lib.rs @@ -77,7 +77,6 @@ mod tests { WasmExecutionMethod::Interpreted, Some(8), sp_io::SubstrateHostFunctions::host_functions(), - true, 8, ); let res = executor.call_in_wasm( @@ -86,6 +85,7 @@ mod tests { "test_empty_return", &[], &mut ext, + sp_core::traits::MissingHostFunctions::Allow, ).unwrap(); assert_eq!(res, vec![0u8; 0]); } diff --git a/substrate/client/executor/src/native_executor.rs b/substrate/client/executor/src/native_executor.rs index 778bc80800..b859b544a3 100644 --- a/substrate/client/executor/src/native_executor.rs +++ b/substrate/client/executor/src/native_executor.rs @@ -20,7 +20,9 @@ use crate::{ }; use sp_version::{NativeVersion, RuntimeVersion}; use codec::{Decode, Encode}; -use sp_core::{NativeOrEncoded, traits::{CodeExecutor, Externalities, RuntimeCode}}; +use sp_core::{ + NativeOrEncoded, traits::{CodeExecutor, Externalities, RuntimeCode, MissingHostFunctions}, +}; use log::trace; use std::{result, panic::{UnwindSafe, AssertUnwindSafe}, sync::Arc}; use sp_wasm_interface::{HostFunctions, Function}; @@ -83,8 +85,6 @@ pub struct WasmExecutor { host_functions: Arc>, /// WASM runtime cache. cache: Arc, - /// Allow missing function imports. - allow_missing_func_imports: bool, /// The size of the instances cache. max_runtime_instances: usize, } @@ -102,7 +102,6 @@ impl WasmExecutor { method: WasmExecutionMethod, default_heap_pages: Option, host_functions: Vec<&'static dyn Function>, - allow_missing_func_imports: bool, max_runtime_instances: usize, ) -> Self { WasmExecutor { @@ -110,7 +109,6 @@ impl WasmExecutor { default_heap_pages: default_heap_pages.unwrap_or(DEFAULT_HEAP_PAGES), host_functions: Arc::new(host_functions), cache: Arc::new(RuntimeCache::new(max_runtime_instances)), - allow_missing_func_imports, max_runtime_instances, } } @@ -132,6 +130,7 @@ impl WasmExecutor { &self, runtime_code: &RuntimeCode, ext: &mut dyn Externalities, + allow_missing_host_functions: bool, f: F, ) -> Result where F: FnOnce( @@ -146,7 +145,7 @@ impl WasmExecutor { self.method, self.default_heap_pages, &*self.host_functions, - self.allow_missing_func_imports, + allow_missing_host_functions, |instance, version, ext| { let instance = AssertUnwindSafe(instance); let ext = AssertUnwindSafe(ext); @@ -167,7 +166,10 @@ impl sp_core::traits::CallInWasm for WasmExecutor { method: &str, call_data: &[u8], ext: &mut dyn Externalities, + missing_host_functions: MissingHostFunctions, ) -> std::result::Result, String> { + let allow_missing_host_functions = missing_host_functions.allowed(); + if let Some(hash) = code_hash { let code = RuntimeCode { code_fetcher: &sp_core::traits::WrappedRuntimeCode(wasm_code.into()), @@ -175,7 +177,7 @@ impl sp_core::traits::CallInWasm for WasmExecutor { heap_pages: None, }; - self.with_instance(&code, ext, |instance, _, mut ext| { + self.with_instance(&code, ext, allow_missing_host_functions, |instance, _, mut ext| { with_externalities_safe( &mut **ext, move || instance.call(method, call_data), @@ -187,7 +189,7 @@ impl sp_core::traits::CallInWasm for WasmExecutor { self.default_heap_pages, &wasm_code, self.host_functions.to_vec(), - self.allow_missing_func_imports, + allow_missing_host_functions, ) .map_err(|e| format!("Failed to create module: {:?}", e))?; @@ -240,7 +242,6 @@ impl NativeExecutor { fallback_method, default_heap_pages, host_functions, - false, max_runtime_instances, ); @@ -265,8 +266,9 @@ impl RuntimeInfo for NativeExecutor { self.wasm.with_instance( runtime_code, ext, + false, |_instance, version, _ext| - Ok(version.cloned().ok_or_else(|| Error::ApiError("Unknown version".into()))) + Ok(version.cloned().ok_or_else(|| Error::ApiError("Unknown version".into()))), ) } } @@ -290,6 +292,7 @@ impl CodeExecutor for NativeExecutor { let result = self.wasm.with_instance( runtime_code, ext, + false, |instance, onchain_version, mut ext| { let onchain_version = onchain_version.ok_or_else( || Error::ApiError("Unknown version".into()) @@ -372,8 +375,9 @@ impl sp_core::traits::CallInWasm for NativeExecutor< method: &str, call_data: &[u8], ext: &mut dyn Externalities, + missing_host_functions: MissingHostFunctions, ) -> std::result::Result, String> { - self.wasm.call_in_wasm(wasm_blob, code_hash, method, call_data, ext) + self.wasm.call_in_wasm(wasm_blob, code_hash, method, call_data, ext, missing_host_functions) } } diff --git a/substrate/frame/support/test/Cargo.toml b/substrate/frame/support/test/Cargo.toml index 3d2fa71a94..77899788d2 100644 --- a/substrate/frame/support/test/Cargo.toml +++ b/substrate/frame/support/test/Cargo.toml @@ -22,6 +22,7 @@ sp-runtime = { version = "2.0.0-dev", default-features = false, path = "../../.. sp-core = { version = "2.0.0-dev", default-features = false, path = "../../../primitives/core" } trybuild = "1.0.17" pretty_assertions = "0.6.1" +rustversion = "1.0.0" [features] default = ["std"] diff --git a/substrate/frame/support/test/tests/construct_runtime_ui.rs b/substrate/frame/support/test/tests/construct_runtime_ui.rs index acddf01f03..d094d73ba5 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui.rs +++ b/substrate/frame/support/test/tests/construct_runtime_ui.rs @@ -1,5 +1,22 @@ +// Copyright 2019-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + use std::env; +#[rustversion::attr(not(stable), ignore)] #[test] fn ui() { // As trybuild is using `cargo check`, we don't need the real WASM binaries. diff --git a/substrate/frame/support/test/tests/reserved_keyword.rs b/substrate/frame/support/test/tests/reserved_keyword.rs index d6cc4bba3b..6f2bbb4747 100644 --- a/substrate/frame/support/test/tests/reserved_keyword.rs +++ b/substrate/frame/support/test/tests/reserved_keyword.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . +#[rustversion::attr(not(stable), ignore)] #[test] fn reserved_keyword() { // As trybuild is using `cargo check`, we don't need the real WASM binaries. diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs index d9b8b68108..83733bb6c9 100644 --- a/substrate/frame/system/src/lib.rs +++ b/substrate/frame/system/src/lib.rs @@ -2108,6 +2108,7 @@ pub(crate) mod tests { _: &str, _: &[u8], _: &mut dyn sp_externalities::Externalities, + _: sp_core::traits::MissingHostFunctions, ) -> Result, String> { Ok(self.0.clone()) } diff --git a/substrate/primitives/core/src/traits.rs b/substrate/primitives/core/src/traits.rs index 14839fb585..1724f5e987 100644 --- a/substrate/primitives/core/src/traits.rs +++ b/substrate/primitives/core/src/traits.rs @@ -262,6 +262,23 @@ impl std::fmt::Display for CodeNotFound { } } +/// `Allow` or `Disallow` missing host functions when instantiating a WASM blob. +#[derive(Clone, Copy, Debug)] +pub enum MissingHostFunctions { + /// Any missing host function will be replaced by a stub that returns an error when + /// being called. + Allow, + /// Any missing host function will result in an error while instantiating the WASM blob, + Disallow, +} + +impl MissingHostFunctions { + /// Are missing host functions allowed? + pub fn allowed(self) -> bool { + matches!(self, Self::Allow) + } +} + /// Something that can call a method in a WASM blob. pub trait CallInWasm: Send + Sync { /// Call the given `method` in the given `wasm_blob` using `call_data` (SCALE encoded arguments) @@ -280,6 +297,7 @@ pub trait CallInWasm: Send + Sync { method: &str, call_data: &[u8], ext: &mut dyn Externalities, + missing_host_functions: MissingHostFunctions, ) -> Result, String>; } diff --git a/substrate/primitives/io/src/lib.rs b/substrate/primitives/io/src/lib.rs index b582720233..344ce1a79a 100644 --- a/substrate/primitives/io/src/lib.rs +++ b/substrate/primitives/io/src/lib.rs @@ -329,7 +329,18 @@ pub trait Misc { self.extension::() .expect("No `CallInWasmExt` associated for the current context!") - .call_in_wasm(wasm, None, "Core_version", &[], &mut ext) + .call_in_wasm( + wasm, + None, + "Core_version", + &[], + &mut 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. + sp_core::traits::MissingHostFunctions::Allow, + ) .ok() } } diff --git a/substrate/primitives/runtime-interface/test/Cargo.toml b/substrate/primitives/runtime-interface/test/Cargo.toml index 0809eeb8a1..03f0122b22 100644 --- a/substrate/primitives/runtime-interface/test/Cargo.toml +++ b/substrate/primitives/runtime-interface/test/Cargo.toml @@ -18,5 +18,6 @@ sp-runtime-interface-test-wasm = { version = "2.0.0-dev", path = "../test-wasm" sp-runtime-interface-test-wasm-deprecated = { version = "2.0.0-dev", path = "../test-wasm-deprecated" } sp-state-machine = { version = "0.8.0-dev", path = "../../../primitives/state-machine" } sp-runtime = { version = "2.0.0-dev", path = "../../runtime" } +sp-core = { version = "2.0.0-dev", path = "../../core" } sp-io = { version = "2.0.0-dev", path = "../../io" } tracing = "0.1.13" diff --git a/substrate/primitives/runtime-interface/test/src/lib.rs b/substrate/primitives/runtime-interface/test/src/lib.rs index 8815a17a08..e7ef1934e2 100644 --- a/substrate/primitives/runtime-interface/test/src/lib.rs +++ b/substrate/primitives/runtime-interface/test/src/lib.rs @@ -41,7 +41,6 @@ fn call_wasm_method(binary: &[u8], method: &str) -> TestExte sc_executor::WasmExecutionMethod::Interpreted, Some(8), host_functions, - false, 8, ); executor.call_in_wasm( @@ -50,6 +49,7 @@ fn call_wasm_method(binary: &[u8], method: &str) -> TestExte method, &[], &mut ext_ext, + sp_core::traits::MissingHostFunctions::Disallow, ).expect(&format!("Executes `{}`", method)); ext diff --git a/substrate/primitives/state-machine/src/lib.rs b/substrate/primitives/state-machine/src/lib.rs index 1c0007c5f9..83bc812b15 100644 --- a/substrate/primitives/state-machine/src/lib.rs +++ b/substrate/primitives/state-machine/src/lib.rs @@ -811,6 +811,7 @@ mod tests { _: &str, _: &[u8], _: &mut dyn Externalities, + _: sp_core::traits::MissingHostFunctions, ) -> std::result::Result, String> { unimplemented!("Not required in tests.") }