diff --git a/substrate/client/executor/runtime-test/src/lib.rs b/substrate/client/executor/runtime-test/src/lib.rs
index c49b9e70b4f85711830f3e6ecec839e67949efb7..a8d329cdd9607a05a0d00cc3da2685970a0115f5 100644
--- a/substrate/client/executor/runtime-test/src/lib.rs
+++ b/substrate/client/executor/runtime-test/src/lib.rs
@@ -18,7 +18,23 @@ use sp_runtime::{print, traits::{BlakeTwo256, Hash}};
 #[cfg(not(feature = "std"))]
 use sp_core::{ed25519, sr25519};
 
+extern "C" {
+	#[allow(dead_code)]
+	fn missing_external();
+
+	#[allow(dead_code)]
+	fn yet_another_missing_external();
+}
+
 sp_core::wasm_export_functions! {
+	fn test_calling_missing_external() {
+		unsafe { missing_external() }
+	}
+
+	fn test_calling_yet_another_missing_external() {
+		unsafe { yet_another_missing_external() }
+	}
+
 	fn test_data_in(input: Vec<u8>) -> Vec<u8> {
 		print("set_storage");
 		storage::set(b"input", &input);
diff --git a/substrate/client/executor/src/integration_tests/mod.rs b/substrate/client/executor/src/integration_tests/mod.rs
index aefb52c7411bdaf77c7c5f38d8a83a1d7a1d5b11..2d39cac4145450425b172b7a6508d97fd128699b 100644
--- a/substrate/client/executor/src/integration_tests/mod.rs
+++ b/substrate/client/executor/src/integration_tests/mod.rs
@@ -32,6 +32,49 @@ use crate::WasmExecutionMethod;
 
 pub type TestExternalities = CoreTestExternalities<Blake2Hasher, u64>;
 
+#[cfg(feature = "wasmtime")]
+mod wasmtime_missing_externals {
+	use sp_wasm_interface::{Function, FunctionContext, HostFunctions, Result, Signature, Value};
+
+	pub struct WasmtimeHostFunctions;
+
+	impl HostFunctions for WasmtimeHostFunctions {
+		fn host_functions() -> Vec<&'static dyn Function> {
+			vec![MISSING_EXTERNAL_FUNCTION, YET_ANOTHER_MISSING_EXTERNAL_FUNCTION]
+		}
+	}
+
+	struct MissingExternalFunction(&'static str);
+
+	impl Function for MissingExternalFunction {
+		fn name(&self) -> &str { self.0 }
+
+		fn signature(&self) -> Signature {
+			Signature::new(vec![], None)
+		}
+
+		fn execute(
+			&self,
+			_context: &mut dyn FunctionContext,
+			_args: &mut dyn Iterator<Item = Value>,
+		) -> Result<Option<Value>> {
+			panic!("should not be called");
+		}
+	}
+
+	static MISSING_EXTERNAL_FUNCTION: &'static MissingExternalFunction =
+		&MissingExternalFunction("missing_external");
+	static YET_ANOTHER_MISSING_EXTERNAL_FUNCTION: &'static MissingExternalFunction =
+		&MissingExternalFunction("yet_another_missing_external");
+}
+
+#[cfg(feature = "wasmtime")]
+type HostFunctions =
+	(wasmtime_missing_externals::WasmtimeHostFunctions, sp_io::SubstrateHostFunctions);
+
+#[cfg(not(feature = "wasmtime"))]
+type HostFunctions = sp_io::SubstrateHostFunctions;
+
 fn call_in_wasm<E: Externalities>(
 	function: &str,
 	call_data: &[u8],
@@ -40,13 +83,14 @@ fn call_in_wasm<E: Externalities>(
 	code: &[u8],
 	heap_pages: u64,
 ) -> crate::error::Result<Vec<u8>> {
-	crate::call_in_wasm::<E, sp_io::SubstrateHostFunctions>(
+	crate::call_in_wasm::<E, HostFunctions>(
 		function,
 		call_data,
 		execution_method,
 		ext,
 		code,
 		heap_pages,
+		true,
 	)
 }
 
@@ -68,6 +112,44 @@ fn returning_should_work(wasm_method: WasmExecutionMethod) {
 	assert_eq!(output, vec![0u8; 0]);
 }
 
+#[test_case(WasmExecutionMethod::Interpreted)]
+#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
+#[should_panic(expected = "Function `missing_external` is only a stub. Calling a stub is not allowed.")]
+#[cfg(not(feature = "wasmtime"))]
+fn call_not_existing_function(wasm_method: WasmExecutionMethod) {
+	let mut ext = TestExternalities::default();
+	let mut ext = ext.ext();
+	let test_code = WASM_BINARY;
+
+	call_in_wasm(
+		"test_calling_missing_external",
+		&[],
+		wasm_method,
+		&mut ext,
+		&test_code[..],
+		8,
+	).unwrap();
+}
+
+#[test_case(WasmExecutionMethod::Interpreted)]
+#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
+#[should_panic(expected = "Function `yet_another_missing_external` is only a stub. Calling a stub is not allowed.")]
+#[cfg(not(feature = "wasmtime"))]
+fn call_yet_another_not_existing_function(wasm_method: WasmExecutionMethod) {
+	let mut ext = TestExternalities::default();
+	let mut ext = ext.ext();
+	let test_code = WASM_BINARY;
+
+	call_in_wasm(
+		"test_calling_yet_another_missing_external",
+		&[],
+		wasm_method,
+		&mut ext,
+		&test_code[..],
+		8,
+	).unwrap();
+}
+
 #[test_case(WasmExecutionMethod::Interpreted)]
 #[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
 fn panicking_should_work(wasm_method: WasmExecutionMethod) {
diff --git a/substrate/client/executor/src/lib.rs b/substrate/client/executor/src/lib.rs
index 78586e0fdc5cf2ed9126640ce9b56762c37e2427..1908eb3688ea357630740058ba4b574988b1aaec 100644
--- a/substrate/client/executor/src/lib.rs
+++ b/substrate/client/executor/src/lib.rs
@@ -67,12 +67,14 @@ pub fn call_in_wasm<E: Externalities, HF: sp_wasm_interface::HostFunctions>(
 	ext: &mut E,
 	code: &[u8],
 	heap_pages: u64,
+	allow_missing_imports: bool,
 ) -> error::Result<Vec<u8>> {
 	let mut instance = wasm_runtime::create_wasm_runtime_with_code(
 		execution_method,
 		heap_pages,
 		code,
 		HF::host_functions(),
+		allow_missing_imports,
 	)?;
 	instance.call(ext, function, call_data)
 }
@@ -103,6 +105,7 @@ mod tests {
 			&mut ext,
 			&WASM_BINARY,
 			8,
+			true,
 		).unwrap();
 		assert_eq!(res, vec![0u8; 0]);
 	}
diff --git a/substrate/client/executor/src/wasm_runtime.rs b/substrate/client/executor/src/wasm_runtime.rs
index cec7672b0286ef0d50bf8c62899c8b88f231c1b3..eef73097f6e7bbcfb6716175f07d16893fe706b6 100644
--- a/substrate/client/executor/src/wasm_runtime.rs
+++ b/substrate/client/executor/src/wasm_runtime.rs
@@ -191,10 +191,11 @@ pub fn create_wasm_runtime_with_code(
 	heap_pages: u64,
 	code: &[u8],
 	host_functions: Vec<&'static dyn Function>,
+	allow_missing_imports: bool,
 ) -> Result<Box<dyn WasmRuntime>, WasmError> {
 	match wasm_method {
 		WasmExecutionMethod::Interpreted =>
-			sc_executor_wasmi::create_instance(code, heap_pages, host_functions)
+			sc_executor_wasmi::create_instance(code, heap_pages, host_functions, allow_missing_imports)
 				.map(|runtime| -> Box<dyn WasmRuntime> { Box::new(runtime) }),
 		#[cfg(feature = "wasmtime")]
 		WasmExecutionMethod::Compiled =>
@@ -212,7 +213,7 @@ fn create_versioned_wasm_runtime<E: Externalities>(
 	let code = ext
 		.original_storage(well_known_keys::CODE)
 		.ok_or(WasmError::CodeNotFound)?;
-	let mut runtime = create_wasm_runtime_with_code(wasm_method, heap_pages, &code, host_functions)?;
+	let mut runtime = create_wasm_runtime_with_code(wasm_method, heap_pages, &code, host_functions, false)?;
 
 	// Call to determine runtime version.
 	let version_result = {
diff --git a/substrate/client/executor/wasmi/src/lib.rs b/substrate/client/executor/wasmi/src/lib.rs
index eebc49f75b29bb54cf4465bd29770bc705354b27..7c6141d6c835f9f8b3378cbab502e73e206a9b82 100644
--- a/substrate/client/executor/wasmi/src/lib.rs
+++ b/substrate/client/executor/wasmi/src/lib.rs
@@ -21,7 +21,7 @@ use sc_executor_common::{
 	sandbox,
 	allocator,
 };
-use std::{str, mem};
+use std::{str, mem, cell::RefCell};
 use wasmi::{
 	Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder, ModuleRef,
 	memory_units::Pages, RuntimeValue::{I32, I64, self},
@@ -42,6 +42,8 @@ struct FunctionExecutor<'a> {
 	memory: MemoryRef,
 	table: Option<TableRef>,
 	host_functions: &'a [&'static dyn Function],
+	allow_missing_imports: bool,
+	missing_functions: &'a [String],
 }
 
 impl<'a> FunctionExecutor<'a> {
@@ -50,6 +52,8 @@ impl<'a> FunctionExecutor<'a> {
 		heap_base: u32,
 		t: Option<TableRef>,
 		host_functions: &'a [&'static dyn Function],
+		allow_missing_imports: bool,
+		missing_functions: &'a [String],
 	) -> Result<Self, Error> {
 		Ok(FunctionExecutor {
 			sandbox_store: sandbox::Store::new(),
@@ -57,6 +61,8 @@ impl<'a> FunctionExecutor<'a> {
 			memory: m,
 			table: t,
 			host_functions,
+			allow_missing_imports,
+			missing_functions,
 		})
 	}
 }
@@ -269,14 +275,28 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
 	}
 }
 
-struct Resolver<'a>(&'a[&'static dyn Function]);
+struct Resolver<'a> {
+	host_functions: &'a[&'static dyn Function],
+	allow_missing_imports: bool,
+	missing_functions: RefCell<Vec<String>>,
+}
+
+impl<'a> Resolver<'a> {
+	fn new(host_functions: &'a[&'static dyn Function], allow_missing_imports: bool) -> Resolver<'a> {
+		Resolver {
+			host_functions,
+			allow_missing_imports,
+			missing_functions: RefCell::new(Vec::new()),
+		}
+	}
+}
 
 impl<'a> wasmi::ModuleImportResolver for Resolver<'a> {
 	fn resolve_func(&self, name: &str, signature: &wasmi::Signature)
 		-> std::result::Result<wasmi::FuncRef, wasmi::Error>
 	{
 		let signature = sp_wasm_interface::Signature::from(signature);
-		for (function_index, function) in self.0.iter().enumerate() {
+		for (function_index, function) in self.host_functions.iter().enumerate() {
 			if name == function.name() {
 				if signature == function.signature() {
 					return Ok(
@@ -295,9 +315,17 @@ impl<'a> wasmi::ModuleImportResolver for Resolver<'a> {
 			}
 		}
 
-		Err(wasmi::Error::Instantiation(
-			format!("Export {} not found", name),
-		))
+		if self.allow_missing_imports {
+			trace!(target: "wasm-executor", "Could not find function `{}`, a stub will be provided instead.", name);
+			let id = self.missing_functions.borrow().len() + self.host_functions.len();
+			self.missing_functions.borrow_mut().push(name.to_string());
+
+			Ok(wasmi::FuncInstance::alloc_host(signature.into(), id))
+		} else {
+			Err(wasmi::Error::Instantiation(
+				format!("Export {} not found", name),
+			))
+		}
 	}
 }
 
@@ -306,16 +334,23 @@ impl<'a> wasmi::Externals for FunctionExecutor<'a> {
 		-> Result<Option<wasmi::RuntimeValue>, wasmi::Trap>
 	{
 		let mut args = args.as_ref().iter().copied().map(Into::into);
-		let function = self.host_functions.get(index).ok_or_else(||
-			Error::from(
-				format!("Could not find host function with index: {}", index),
-			)
-		)?;
-
-		function.execute(self, &mut args)
-			.map_err(|msg| Error::FunctionExecution(function.name().to_string(), msg))
-			.map_err(wasmi::Trap::from)
-			.map(|v| v.map(Into::into))
+
+		if let Some(function) = self.host_functions.get(index) {
+			function.execute(self, &mut args)
+				.map_err(|msg| Error::FunctionExecution(function.name().to_string(), msg))
+				.map_err(wasmi::Trap::from)
+				.map(|v| v.map(Into::into))
+		} else if self.allow_missing_imports
+			&& index >= self.host_functions.len()
+			&& index < self.host_functions.len() + self.missing_functions.len()
+		{
+			Err(Error::from(format!(
+				"Function `{}` is only a stub. Calling a stub is not allowed.",
+				self.missing_functions[index - self.host_functions.len()],
+			)).into())
+		} else {
+			Err(Error::from(format!("Could not find host function with index: {}", index)).into())
+		}
 	}
 }
 
@@ -351,6 +386,8 @@ fn call_in_wasm_module(
 	method: &str,
 	data: &[u8],
 	host_functions: &[&'static dyn Function],
+	allow_missing_imports: bool,
+	missing_functions: &Vec<String>,
 ) -> Result<Vec<u8>, Error> {
 	// extract a reference to a linear memory, optional reference to a table
 	// and then initialize FunctionExecutor.
@@ -360,7 +397,14 @@ fn call_in_wasm_module(
 		.and_then(|e| e.as_table().cloned());
 	let heap_base = get_heap_base(module_instance)?;
 
-	let mut fec = FunctionExecutor::new(memory.clone(), heap_base, table, host_functions)?;
+	let mut fec = FunctionExecutor::new(
+		memory.clone(),
+		heap_base,
+		table,
+		host_functions,
+		allow_missing_imports,
+		missing_functions,
+	)?;
 
 	// Write the call data
 	let offset = fec.allocate_memory(data.len() as u32)?;
@@ -397,8 +441,9 @@ fn instantiate_module(
 	heap_pages: usize,
 	module: &Module,
 	host_functions: &[&'static dyn Function],
-) -> Result<ModuleRef, Error> {
-	let resolver = Resolver(host_functions);
+	allow_missing_imports: bool,
+) -> Result<(ModuleRef, Vec<String>), Error> {
+	let resolver = Resolver::new(host_functions, allow_missing_imports);
 	// start module instantiation. Don't run 'start' function yet.
 	let intermediate_instance = ModuleInstance::new(
 		module,
@@ -416,7 +461,7 @@ fn instantiate_module(
 		// Runtime is not allowed to have the `start` function.
 		Err(Error::RuntimeHasStartFn)
 	} else {
-		Ok(intermediate_instance.assert_no_start())
+		Ok((intermediate_instance.assert_no_start(), resolver.missing_functions.into_inner()))
 	}
 }
 
@@ -536,6 +581,11 @@ pub struct WasmiRuntime {
 	state_snapshot: StateSnapshot,
 	/// The host functions registered for this instance.
 	host_functions: Vec<&'static dyn Function>,
+	/// Enable stub generation for functions that are not available in `host_functions`.
+	/// These stubs will error when the wasm blob tries to call them.
+	allow_missing_imports: bool,
+	/// List of missing functions detected during function resolution
+	missing_functions: Vec<String>,
 }
 
 impl WasmRuntime for WasmiRuntime {
@@ -561,7 +611,15 @@ impl WasmRuntime for WasmiRuntime {
 				error!(target: "wasm-executor", "snapshot restoration failed: {}", e);
 				e
 			})?;
-		call_in_wasm_module(ext, &self.instance, method, data, &self.host_functions)
+		call_in_wasm_module(
+			ext,
+			&self.instance,
+			method,
+			data,
+			&self.host_functions,
+			self.allow_missing_imports,
+			&self.missing_functions,
+		)
 	}
 }
 
@@ -569,6 +627,7 @@ pub fn create_instance(
 	code: &[u8],
 	heap_pages: u64,
 	host_functions: Vec<&'static dyn Function>,
+	allow_missing_imports: bool,
 ) -> Result<WasmiRuntime, WasmError> {
 	let module = Module::from_buffer(&code).map_err(|_| WasmError::InvalidModule)?;
 
@@ -579,8 +638,12 @@ pub fn create_instance(
 	let data_segments = extract_data_segments(&code)?;
 
 	// Instantiate this module.
-	let instance = instantiate_module(heap_pages as usize, &module, &host_functions)
-		.map_err(|e| WasmError::Instantiation(e.to_string()))?;
+	let (instance, missing_functions) = instantiate_module(
+		heap_pages as usize,
+		&module,
+		&host_functions,
+		allow_missing_imports,
+	).map_err(|e| WasmError::Instantiation(e.to_string()))?;
 
 	// Take state snapshot before executing anything.
 	let state_snapshot = StateSnapshot::take(&instance, data_segments, heap_pages)
@@ -595,6 +658,8 @@ pub fn create_instance(
 		instance,
 		state_snapshot,
 		host_functions,
+		allow_missing_imports,
+		missing_functions,
 	})
 }
 
diff --git a/substrate/primitives/runtime-interface/test/src/lib.rs b/substrate/primitives/runtime-interface/test/src/lib.rs
index 683a7af2976f1cdd63125b29704b44c4e6cf3a93..35a93e21365ebf6aa271d7baf0fa07213b10f9f2 100644
--- a/substrate/primitives/runtime-interface/test/src/lib.rs
+++ b/substrate/primitives/runtime-interface/test/src/lib.rs
@@ -41,6 +41,7 @@ fn call_wasm_method<HF: HostFunctionsT>(method: &str) -> TestExternalities {
 		&mut ext_ext,
 		&WASM_BINARY[..],
 		8,
+		false,
 	).expect(&format!("Executes `{}`", method));
 
 	ext