From e2b835280734ef477f5602c207da6834372b8d1c Mon Sep 17 00:00:00 2001
From: Pierre Krieger <pierre.krieger1708@gmail.com>
Date: Tue, 14 Feb 2023 17:44:32 +0100
Subject: [PATCH] Check signatures of host functions at initialization and sync
 functions list with spec

---
 Cargo.toml                 |   2 +-
 bin/wasm-node/CHANGELOG.md |   1 +
 src/executor/host.rs       | 456 ++++++++++++++++++++++++++-----------
 src/executor/vm.rs         |  20 ++
 4 files changed, 347 insertions(+), 132 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 8dceabc160..d5f112eaac 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -78,7 +78,7 @@ serde_json = { version = "1.0.93", default-features = false, features = ["alloc"
 sha2 = { version = "0.10.6", default-features = false }
 siphasher = { version = "0.3.10", default-features = false }
 slab = { version = "0.4.7", default-features = false }
-smallvec = "1.10.0"
+smallvec = { version = "1.10.0", default-features = false }
 snow = { version = "0.9.1", default-features = false, features = ["default-resolver"] }
 tiny-keccak = { version = "2.0", features = ["keccak"] }
 twox-hash = { version = "1.6.3", default-features = false }
diff --git a/bin/wasm-node/CHANGELOG.md b/bin/wasm-node/CHANGELOG.md
index dcb7809189..4433743566 100644
--- a/bin/wasm-node/CHANGELOG.md
+++ b/bin/wasm-node/CHANGELOG.md
@@ -5,6 +5,7 @@
 ### Changed
 
 - The Wasm virtual machine no longer tries to grab a table symbol named `__indirect_function_table`. This removes support for an old Substrate feature that no longer exists.
+- The signature of host functions called by the Wasm runtime is now checked when the Wasm code is compiled rather than when the functions are called.
 
 ## 0.7.11 - 2022-02-13
 
diff --git a/src/executor/host.rs b/src/executor/host.rs
index 160f5c210c..f62c26d67b 100644
--- a/src/executor/host.rs
+++ b/src/executor/host.rs
@@ -309,16 +309,19 @@ impl HostVmPrototype {
             let vm_proto = vm::VirtualMachinePrototype::new(
                 &module,
                 // This closure is called back for each function that the runtime imports.
-                |mod_name, f_name, _signature| {
+                |mod_name, f_name, signature| {
                     if mod_name != "env" {
                         return Err(());
                     }
 
                     let id = registered_functions.len();
                     registered_functions.push(match HostFunction::by_name(f_name) {
-                        Some(f) => FunctionImport::Resolved(f),
-                        None if !allow_unresolved_imports => return Err(()),
-                        None => FunctionImport::Unresolved {
+                        Some(f) if f.signature() == *signature => FunctionImport::Resolved(f),
+                        Some(_) | None if !allow_unresolved_imports => {
+                            // TODO: return a better error if there is a signature mismatch
+                            return Err(());
+                        }
+                        Some(_) | None => FunctionImport::Unresolved {
                             name: f_name.to_owned(),
                             module: mod_name.to_owned(),
                         },
@@ -703,12 +706,9 @@ impl ReadyToRun {
 
             Err(vm::RunErr::BadValueTy { .. }) => {
                 // Tried to inject back the value returned by a host function, but it doesn't
-                // match what the Wasm code expects.
-                // TODO: check signatures at initialization instead?
-                return HostVm::Error {
-                    prototype: self.inner.into_prototype(),
-                    error: Error::ReturnValueTypeMismatch,
-                };
+                // match what the Wasm code expects. Given that we check the host function
+                // signatures at initialization, this indicates a bug in this implementation.
+                unreachable!()
             }
 
             Err(vm::RunErr::Poisoned) => {
@@ -734,20 +734,6 @@ impl ReadyToRun {
             None => unreachable!(),
         };
 
-        // Check that the actual number of parameters matches the expected number.
-        // This is done ahead of time in order to not forget.
-        let expected_params_num = host_fn.num_parameters();
-        if params.len() != expected_params_num {
-            return HostVm::Error {
-                error: Error::ParamsCountMismatch {
-                    function: host_fn.name(),
-                    expected: expected_params_num,
-                    actual: params.len(),
-                },
-                prototype: self.inner.into_prototype(),
-            };
-        }
-
         // Passed a parameter index. Produces an `impl AsRef<[u8]>`.
         macro_rules! expect_pointer_size {
             ($num:expr) => {{
@@ -1550,6 +1536,7 @@ impl ReadyToRun {
                     inner: self.inner,
                 })
             }
+            HostFunction::ext_crypto_ed25519_batch_verify_version_1 => host_fn_not_implemented!(),
             HostFunction::ext_crypto_sr25519_public_keys_version_1 => host_fn_not_implemented!(),
             HostFunction::ext_crypto_sr25519_generate_version_1 => host_fn_not_implemented!(),
             HostFunction::ext_crypto_sr25519_sign_version_1 => host_fn_not_implemented!(),
@@ -1575,6 +1562,7 @@ impl ReadyToRun {
                     inner: self.inner,
                 })
             }
+            HostFunction::ext_crypto_sr25519_batch_verify_version_1 => host_fn_not_implemented!(),
             HostFunction::ext_crypto_ecdsa_generate_version_1 => host_fn_not_implemented!(),
             HostFunction::ext_crypto_ecdsa_sign_version_1 => {
                 // NOTE: safe to unwrap here because we supply the nn to blake2b fn
@@ -1614,7 +1602,9 @@ impl ReadyToRun {
                     inner: self.inner,
                 })
             }
+            HostFunction::ext_crypto_ecdsa_verify_version_2 => host_fn_not_implemented!(),
             HostFunction::ext_crypto_ecdsa_sign_prehashed_version_1 => {
+                // TODO: seems misimplemented, see https://spec.polkadot.network/#id-ext_crypto_ecdsa_sign_prehashed
                 let message = libsecp256k1::Message::parse(&expect_pointer_constant_size!(0, 32));
 
                 if let Ok(sc) =
@@ -1644,6 +1634,7 @@ impl ReadyToRun {
                     inner: self.inner,
                 })
             }
+            HostFunction::ext_crypto_ecdsa_batch_verify_version_1 => host_fn_not_implemented!(),
 
             HostFunction::ext_crypto_secp256k1_ecdsa_recover_version_1
             | HostFunction::ext_crypto_secp256k1_ecdsa_recover_version_2 => {
@@ -1776,6 +1767,7 @@ impl ReadyToRun {
                 self.inner
                     .alloc_write_and_return_pointer(host_fn.name(), iter::once(&out))
             }
+            HostFunction::ext_hashing_keccak_512_version_1 => host_fn_not_implemented!(),
             HostFunction::ext_hashing_sha2_256_version_1 => {
                 let mut hasher = sha2::Sha256::new();
                 hasher.update(expect_pointer_size!(0));
@@ -1996,8 +1988,14 @@ impl ReadyToRun {
                     },
                 }
             }
+            HostFunction::ext_trie_keccak_256_root_version_1 => host_fn_not_implemented!(),
+            HostFunction::ext_trie_keccak_256_root_version_2 => host_fn_not_implemented!(),
             HostFunction::ext_trie_keccak_256_ordered_root_version_1 => host_fn_not_implemented!(),
             HostFunction::ext_trie_keccak_256_ordered_root_version_2 => host_fn_not_implemented!(),
+            HostFunction::ext_trie_blake2_256_verify_proof_version_1 => host_fn_not_implemented!(),
+            HostFunction::ext_trie_blake2_256_verify_proof_version_2 => host_fn_not_implemented!(),
+            HostFunction::ext_trie_keccak_256_verify_proof_version_1 => host_fn_not_implemented!(),
+            HostFunction::ext_trie_keccak_256_verify_proof_version_2 => host_fn_not_implemented!(),
             HostFunction::ext_misc_print_num_version_1 => {
                 let num = match params[0] {
                     vm::WasmValue::I64(v) => u64::from_ne_bytes(v.to_ne_bytes()),
@@ -3700,9 +3698,6 @@ pub enum Error {
         /// Size of the virtual memory.
         memory_size: u32,
     },
-    /// An `host_fn` wants to returns a certain value, but the Wasm code expects a different one.
-    // TODO: indicate function and actual/expected types
-    ReturnValueTypeMismatch,
     /// Called a function that is unknown to the host.
     ///
     /// > **Note**: Can only happen if `allow_unresolved_imports` was `true`.
@@ -3713,21 +3708,6 @@ pub enum Error {
         /// Name of module associated with the unresolved function.
         module_name: String,
     },
-    /// Mismatch between the number of parameters expected and the actual number.
-    #[display(
-        fmt = "Mismatch in parameters count: {}, expected = {}, actual = {}",
-        function,
-        expected,
-        actual
-    )]
-    ParamsCountMismatch {
-        /// Name of the function being called whose number of parameters mismatches.
-        function: &'static str,
-        /// Expected number of parameters.
-        expected: usize,
-        /// Number of parameters that have been passed.
-        actual: usize,
-    },
     /// Failed to decode a SCALE-encoded parameter.
     ParamDecodeError,
     /// The type of one of the parameters is wrong.
@@ -3894,17 +3874,21 @@ externalities! {
     ext_crypto_ed25519_generate_version_1,
     ext_crypto_ed25519_sign_version_1,
     ext_crypto_ed25519_verify_version_1,
+    ext_crypto_ed25519_batch_verify_version_1,
     ext_crypto_sr25519_public_keys_version_1,
     ext_crypto_sr25519_generate_version_1,
     ext_crypto_sr25519_sign_version_1,
     ext_crypto_sr25519_verify_version_1,
     ext_crypto_sr25519_verify_version_2,
+    ext_crypto_sr25519_batch_verify_version_1,
     ext_crypto_ecdsa_generate_version_1,
     ext_crypto_ecdsa_sign_version_1,
     ext_crypto_ecdsa_public_keys_version_1,
     ext_crypto_ecdsa_verify_version_1,
+    ext_crypto_ecdsa_verify_version_2,
     ext_crypto_ecdsa_sign_prehashed_version_1,
     ext_crypto_ecdsa_verify_prehashed_version_1,
+    ext_crypto_ecdsa_batch_verify_version_1,
     ext_crypto_secp256k1_ecdsa_recover_version_1,
     ext_crypto_secp256k1_ecdsa_recover_version_2,
     ext_crypto_secp256k1_ecdsa_recover_compressed_version_1,
@@ -3912,6 +3896,7 @@ externalities! {
     ext_crypto_start_batch_verify_version_1,
     ext_crypto_finish_batch_verify_version_1,
     ext_hashing_keccak_256_version_1,
+    ext_hashing_keccak_512_version_1,
     ext_hashing_sha2_256_version_1,
     ext_hashing_blake2_128_version_1,
     ext_hashing_blake2_256_version_1,
@@ -3940,8 +3925,14 @@ externalities! {
     ext_trie_blake2_256_root_version_2,
     ext_trie_blake2_256_ordered_root_version_1,
     ext_trie_blake2_256_ordered_root_version_2,
+    ext_trie_keccak_256_root_version_1,
+    ext_trie_keccak_256_root_version_2,
     ext_trie_keccak_256_ordered_root_version_1,
     ext_trie_keccak_256_ordered_root_version_2,
+    ext_trie_blake2_256_verify_proof_version_1,
+    ext_trie_blake2_256_verify_proof_version_2,
+    ext_trie_keccak_256_verify_proof_version_1,
+    ext_trie_keccak_256_verify_proof_version_2,
     ext_misc_print_num_version_1,
     ext_misc_print_utf8_version_1,
     ext_misc_print_hex_version_1,
@@ -3953,96 +3944,299 @@ externalities! {
 }
 
 impl HostFunction {
-    fn num_parameters(&self) -> usize {
+    // TODO: make this a `const fn` function
+    fn signature(&self) -> vm::Signature {
         match *self {
-            HostFunction::ext_storage_set_version_1 => 2,
-            HostFunction::ext_storage_get_version_1 => 1,
-            HostFunction::ext_storage_read_version_1 => 3,
-            HostFunction::ext_storage_clear_version_1 => 1,
-            HostFunction::ext_storage_exists_version_1 => 1,
-            HostFunction::ext_storage_clear_prefix_version_1 => 1,
-            HostFunction::ext_storage_clear_prefix_version_2 => 2,
-            HostFunction::ext_storage_root_version_1 => 0,
-            HostFunction::ext_storage_root_version_2 => 1,
-            HostFunction::ext_storage_changes_root_version_1 => 1,
-            HostFunction::ext_storage_next_key_version_1 => 1,
-            HostFunction::ext_storage_append_version_1 => 2,
-            HostFunction::ext_storage_start_transaction_version_1 => 0,
-            HostFunction::ext_storage_rollback_transaction_version_1 => 0,
-            HostFunction::ext_storage_commit_transaction_version_1 => 0,
-            HostFunction::ext_default_child_storage_get_version_1 => 2,
-            HostFunction::ext_default_child_storage_read_version_1 => 4,
-            HostFunction::ext_default_child_storage_storage_kill_version_1 => 1,
-            HostFunction::ext_default_child_storage_storage_kill_version_2 => 2,
-            HostFunction::ext_default_child_storage_storage_kill_version_3 => 2,
-            HostFunction::ext_default_child_storage_clear_prefix_version_1 => 2,
-            HostFunction::ext_default_child_storage_clear_prefix_version_2 => 3,
-            HostFunction::ext_default_child_storage_set_version_1 => 3,
-            HostFunction::ext_default_child_storage_clear_version_1 => 2,
-            HostFunction::ext_default_child_storage_exists_version_1 => 2,
-            HostFunction::ext_default_child_storage_next_key_version_1 => 2,
-            HostFunction::ext_default_child_storage_root_version_1 => 1,
-            HostFunction::ext_default_child_storage_root_version_2 => 2,
-            HostFunction::ext_crypto_ed25519_public_keys_version_1 => todo!(),
-            HostFunction::ext_crypto_ed25519_generate_version_1 => todo!(),
-            HostFunction::ext_crypto_ed25519_sign_version_1 => todo!(),
-            HostFunction::ext_crypto_ed25519_verify_version_1 => 3,
-            HostFunction::ext_crypto_sr25519_public_keys_version_1 => todo!(),
-            HostFunction::ext_crypto_sr25519_generate_version_1 => todo!(),
-            HostFunction::ext_crypto_sr25519_sign_version_1 => todo!(),
-            HostFunction::ext_crypto_sr25519_verify_version_1 => 3,
-            HostFunction::ext_crypto_sr25519_verify_version_2 => 3,
-            HostFunction::ext_crypto_ecdsa_generate_version_1 => todo!(),
-            HostFunction::ext_crypto_ecdsa_sign_version_1 => 2,
-            HostFunction::ext_crypto_ecdsa_public_keys_version_1 => todo!(),
-            HostFunction::ext_crypto_ecdsa_verify_version_1 => 3,
-            HostFunction::ext_crypto_ecdsa_sign_prehashed_version_1 => 2,
-            HostFunction::ext_crypto_ecdsa_verify_prehashed_version_1 => 3,
-            HostFunction::ext_crypto_secp256k1_ecdsa_recover_version_1 => 2,
-            HostFunction::ext_crypto_secp256k1_ecdsa_recover_version_2 => 2,
-            HostFunction::ext_crypto_secp256k1_ecdsa_recover_compressed_version_1 => 2,
-            HostFunction::ext_crypto_secp256k1_ecdsa_recover_compressed_version_2 => 2,
-            HostFunction::ext_crypto_start_batch_verify_version_1 => 0,
-            HostFunction::ext_crypto_finish_batch_verify_version_1 => 0,
-            HostFunction::ext_hashing_keccak_256_version_1 => 1,
-            HostFunction::ext_hashing_sha2_256_version_1 => todo!(),
-            HostFunction::ext_hashing_blake2_128_version_1 => 1,
-            HostFunction::ext_hashing_blake2_256_version_1 => 1,
-            HostFunction::ext_hashing_twox_64_version_1 => 1,
-            HostFunction::ext_hashing_twox_128_version_1 => 1,
-            HostFunction::ext_hashing_twox_256_version_1 => 1,
-            HostFunction::ext_offchain_index_set_version_1 => 2,
-            HostFunction::ext_offchain_index_clear_version_1 => 1,
-            HostFunction::ext_offchain_is_validator_version_1 => todo!(),
-            HostFunction::ext_offchain_submit_transaction_version_1 => todo!(),
-            HostFunction::ext_offchain_network_state_version_1 => todo!(),
-            HostFunction::ext_offchain_timestamp_version_1 => todo!(),
-            HostFunction::ext_offchain_sleep_until_version_1 => todo!(),
-            HostFunction::ext_offchain_random_seed_version_1 => todo!(),
-            HostFunction::ext_offchain_local_storage_set_version_1 => todo!(),
-            HostFunction::ext_offchain_local_storage_compare_and_set_version_1 => todo!(),
-            HostFunction::ext_offchain_local_storage_get_version_1 => todo!(),
-            HostFunction::ext_offchain_local_storage_clear_version_1 => todo!(),
-            HostFunction::ext_offchain_http_request_start_version_1 => todo!(),
-            HostFunction::ext_offchain_http_request_add_header_version_1 => todo!(),
-            HostFunction::ext_offchain_http_request_write_body_version_1 => todo!(),
-            HostFunction::ext_offchain_http_response_wait_version_1 => todo!(),
-            HostFunction::ext_offchain_http_response_headers_version_1 => todo!(),
-            HostFunction::ext_offchain_http_response_read_body_version_1 => todo!(),
-            HostFunction::ext_trie_blake2_256_root_version_1 => 1,
-            HostFunction::ext_trie_blake2_256_root_version_2 => 2,
-            HostFunction::ext_trie_blake2_256_ordered_root_version_1 => 1,
-            HostFunction::ext_trie_blake2_256_ordered_root_version_2 => 2,
-            HostFunction::ext_trie_keccak_256_ordered_root_version_1 => todo!(),
-            HostFunction::ext_trie_keccak_256_ordered_root_version_2 => todo!(),
-            HostFunction::ext_misc_print_num_version_1 => 1,
-            HostFunction::ext_misc_print_utf8_version_1 => 1,
-            HostFunction::ext_misc_print_hex_version_1 => 1,
-            HostFunction::ext_misc_runtime_version_version_1 => 1,
-            HostFunction::ext_allocator_malloc_version_1 => 1,
-            HostFunction::ext_allocator_free_version_1 => 1,
-            HostFunction::ext_logging_log_version_1 => 3,
-            HostFunction::ext_logging_max_level_version_1 => 0,
+            HostFunction::ext_storage_set_version_1 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_storage_get_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_storage_read_version_1 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I64)
+            }
+            HostFunction::ext_storage_clear_version_1 => {
+                crate::signature!((vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_storage_exists_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_storage_clear_prefix_version_1 => {
+                crate::signature!((vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_storage_clear_prefix_version_2 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_storage_root_version_1 => crate::signature!(() => vm::ValueType::I64),
+            HostFunction::ext_storage_root_version_2 => {
+                crate::signature!((vm::ValueType::I32) => vm::ValueType::I64)
+            }
+            HostFunction::ext_storage_changes_root_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_storage_next_key_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_storage_append_version_1 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_storage_start_transaction_version_1 => crate::signature!(() => ()),
+            HostFunction::ext_storage_rollback_transaction_version_1 => crate::signature!(() => ()),
+            HostFunction::ext_storage_commit_transaction_version_1 => crate::signature!(() => ()),
+            HostFunction::ext_default_child_storage_get_version_1 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_default_child_storage_read_version_1 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64, vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_default_child_storage_storage_kill_version_1 => {
+                crate::signature!((vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_default_child_storage_storage_kill_version_2 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_default_child_storage_storage_kill_version_3 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_default_child_storage_clear_prefix_version_1 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_default_child_storage_clear_prefix_version_2 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_default_child_storage_set_version_1 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64, vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_default_child_storage_clear_version_1 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_default_child_storage_exists_version_1 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_default_child_storage_next_key_version_1 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_default_child_storage_root_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_default_child_storage_root_version_2 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_crypto_ed25519_public_keys_version_1 => {
+                crate::signature!((vm::ValueType::I32) => vm::ValueType::I64)
+            }
+            HostFunction::ext_crypto_ed25519_generate_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_crypto_ed25519_sign_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I32, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_crypto_ed25519_verify_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_crypto_ed25519_batch_verify_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_crypto_sr25519_public_keys_version_1 => {
+                crate::signature!((vm::ValueType::I32) => vm::ValueType::I64)
+            }
+            HostFunction::ext_crypto_sr25519_generate_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_crypto_sr25519_sign_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I32, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_crypto_sr25519_verify_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_crypto_sr25519_verify_version_2 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_crypto_sr25519_batch_verify_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_crypto_ecdsa_generate_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_crypto_ecdsa_sign_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I32, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_crypto_ecdsa_public_keys_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_crypto_ecdsa_verify_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_crypto_ecdsa_verify_version_2 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_crypto_ecdsa_sign_prehashed_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I32, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_crypto_ecdsa_verify_prehashed_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I32, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_crypto_ecdsa_batch_verify_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_crypto_secp256k1_ecdsa_recover_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I32) => vm::ValueType::I64)
+            }
+            HostFunction::ext_crypto_secp256k1_ecdsa_recover_version_2 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I32) => vm::ValueType::I64)
+            }
+            HostFunction::ext_crypto_secp256k1_ecdsa_recover_compressed_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I32) => vm::ValueType::I64)
+            }
+            HostFunction::ext_crypto_secp256k1_ecdsa_recover_compressed_version_2 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I32) => vm::ValueType::I64)
+            }
+            HostFunction::ext_crypto_start_batch_verify_version_1 => crate::signature!(() => ()),
+            HostFunction::ext_crypto_finish_batch_verify_version_1 => {
+                crate::signature!(() => vm::ValueType::I32)
+            }
+            HostFunction::ext_hashing_keccak_256_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_hashing_keccak_512_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_hashing_sha2_256_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_hashing_blake2_128_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_hashing_blake2_256_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_hashing_twox_64_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_hashing_twox_128_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_hashing_twox_256_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_offchain_index_set_version_1 => {
+                // TODO: not documented on https://spec.polkadot.network/
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_offchain_index_clear_version_1 => {
+                // TODO: not documented on https://spec.polkadot.network/
+                crate::signature!((vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_offchain_is_validator_version_1 => {
+                crate::signature!(() => vm::ValueType::I32)
+            }
+            HostFunction::ext_offchain_submit_transaction_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_offchain_network_state_version_1 => {
+                crate::signature!(() => vm::ValueType::I64)
+            }
+            HostFunction::ext_offchain_timestamp_version_1 => {
+                crate::signature!(() => vm::ValueType::I64)
+            }
+            HostFunction::ext_offchain_sleep_until_version_1 => {
+                crate::signature!((vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_offchain_random_seed_version_1 => {
+                crate::signature!(() => vm::ValueType::I32)
+            }
+            HostFunction::ext_offchain_local_storage_set_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_offchain_local_storage_compare_and_set_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_offchain_local_storage_get_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_offchain_local_storage_clear_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_offchain_http_request_start_version_1 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_offchain_http_request_add_header_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_offchain_http_request_write_body_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_offchain_http_response_wait_version_1 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_offchain_http_response_headers_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_offchain_http_response_read_body_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_trie_blake2_256_root_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_trie_blake2_256_root_version_2 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_trie_blake2_256_ordered_root_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_trie_blake2_256_ordered_root_version_2 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_trie_keccak_256_root_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_trie_keccak_256_root_version_2 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_trie_keccak_256_ordered_root_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_trie_keccak_256_ordered_root_version_2 => {
+                crate::signature!((vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_trie_blake2_256_verify_proof_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_trie_blake2_256_verify_proof_version_2 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I64, vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_trie_keccak_256_verify_proof_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I64, vm::ValueType::I64) => vm::ValueType::I32)
+            }
+            HostFunction::ext_trie_keccak_256_verify_proof_version_2 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I64, vm::ValueType::I64, vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_misc_print_num_version_1 => {
+                crate::signature!((vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_misc_print_utf8_version_1 => {
+                crate::signature!((vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_misc_print_hex_version_1 => {
+                crate::signature!((vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_misc_runtime_version_version_1 => {
+                crate::signature!((vm::ValueType::I64) => vm::ValueType::I64)
+            }
+            HostFunction::ext_allocator_malloc_version_1 => {
+                crate::signature!((vm::ValueType::I32) => vm::ValueType::I32)
+            }
+            HostFunction::ext_allocator_free_version_1 => {
+                crate::signature!((vm::ValueType::I32) => ())
+            }
+            HostFunction::ext_logging_log_version_1 => {
+                crate::signature!((vm::ValueType::I32, vm::ValueType::I64, vm::ValueType::I64) => ())
+            }
+            HostFunction::ext_logging_max_level_version_1 => {
+                // TODO: not documented on https://spec.polkadot.network/
+                crate::signature!(() => vm::ValueType::I32)
+            }
         }
     }
 }
diff --git a/src/executor/vm.rs b/src/executor/vm.rs
index 0c7182c8d8..83cdd5ff7d 100644
--- a/src/executor/vm.rs
+++ b/src/executor/vm.rs
@@ -394,6 +394,17 @@ pub struct Signature {
     ret_ty: Option<ValueType>,
 }
 
+// TODO: figure out how to optimize so that we can use this macro in a const context
+#[macro_export]
+macro_rules! signature {
+    (($($param:expr),* $(,)?) => ()) => {
+        $crate::executor::vm::Signature::from_components(smallvec::smallvec!($($param),*), None)
+    };
+    (($($param:expr),* $(,)?) => $ret:expr) => {
+        $crate::executor::vm::Signature::from_components(smallvec::smallvec!($($param),*), Some($ret))
+    };
+}
+
 impl Signature {
     /// Creates a [`Signature`] from the given parameter types and return type.
     pub fn new(
@@ -406,6 +417,15 @@ impl Signature {
         }
     }
 
+    // TODO: find a way to remove? it is used only by the signature! macro
+    #[doc(hidden)]
+    pub(crate) fn from_components(
+        params: SmallVec<[ValueType; 8]>,
+        ret_ty: Option<ValueType>,
+    ) -> Self {
+        Signature { params, ret_ty }
+    }
+
     /// Returns a list of all the types of the parameters.
     pub fn parameters(&self) -> impl ExactSizeIterator<Item = &ValueType> {
         self.params.iter()