diff --git a/Cargo.lock b/Cargo.lock index 8c215ddd12611..9f4ec25af2e1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,14 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "adler32" version = "1.0.4" @@ -993,7 +1002,7 @@ dependencies = [ "evm-core 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "evm-gasometer 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "evm-runtime 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1003,7 +1012,7 @@ name = "evm-core" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1013,7 +1022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "evm-core 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "evm-runtime 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1022,7 +1031,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "evm-core 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3324,7 +3333,7 @@ dependencies = [ [[package]] name = "primitive-types" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fixed-hash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4234,7 +4243,7 @@ dependencies = [ "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", "sr-std 2.0.0", @@ -4249,10 +4258,10 @@ dependencies = [ "libsecp256k1 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "sr-std 2.0.0", "substrate-externalities 2.0.0", "substrate-primitives 2.0.0", + "substrate-runtime-interface 2.0.0", "substrate-state-machine 2.0.0", "substrate-trie 2.0.0", "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4283,7 +4292,7 @@ version = "2.0.0" dependencies = [ "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 2.0.0", "sr-std 2.0.0", "substrate-primitives 2.0.0", "wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4302,9 +4311,6 @@ dependencies = [ [[package]] name = "sr-std" version = "2.0.0" -dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "sr-version" @@ -4551,7 +4557,7 @@ version = "2.0.0" dependencies = [ "evm 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4855,6 +4861,7 @@ dependencies = [ "srml-system 2.0.0", "substrate-inherents 2.0.0", "substrate-primitives 2.0.0", + "substrate-state-machine 2.0.0", ] [[package]] @@ -4900,6 +4907,7 @@ dependencies = [ "srml-support 2.0.0", "substrate-inherents 2.0.0", "substrate-primitives 2.0.0", + "substrate-state-machine 2.0.0", "trybuild 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5046,6 +5054,11 @@ name = "static_assertions" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "static_assertions" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "stream-cipher" version = "0.3.2" @@ -5573,6 +5586,7 @@ dependencies = [ "substrate-offchain 2.0.0", "substrate-panic-handler 2.0.0", "substrate-primitives 2.0.0", + "substrate-runtime-interface 2.0.0", "substrate-runtime-test 2.0.0", "substrate-serializer 2.0.0", "substrate-state-machine 2.0.0", @@ -5592,7 +5606,7 @@ name = "substrate-externalities" version = "2.0.0" dependencies = [ "environmental 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-std 2.0.0", "substrate-primitives-storage 2.0.0", ] @@ -5830,7 +5844,7 @@ dependencies = [ "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5842,6 +5856,7 @@ dependencies = [ "substrate-debug-derive 2.0.0", "substrate-externalities 2.0.0", "substrate-primitives-storage 2.0.0", + "substrate-runtime-interface 2.0.0", "substrate-serializer 2.0.0", "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5935,6 +5950,52 @@ dependencies = [ "sr-primitives 2.0.0", ] +[[package]] +name = "substrate-runtime-interface" +version = "2.0.0" +dependencies = [ + "environmental 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 2.0.0", + "sr-std 2.0.0", + "static_assertions 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-executor 2.0.0", + "substrate-externalities 2.0.0", + "substrate-primitives 2.0.0", + "substrate-runtime-interface-proc-macro 2.0.0", + "substrate-runtime-interface-test-wasm 2.0.0", + "substrate-state-machine 2.0.0", + "substrate-wasm-interface 2.0.0", +] + +[[package]] +name = "substrate-runtime-interface-proc-macro" +version = "2.0.0" +dependencies = [ + "Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustversion 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-externalities 2.0.0", + "substrate-runtime-interface 2.0.0", + "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "trybuild 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "substrate-runtime-interface-test-wasm" +version = "2.0.0" +dependencies = [ + "sr-io 2.0.0", + "sr-std 2.0.0", + "substrate-primitives 2.0.0", + "substrate-runtime-interface 2.0.0", + "substrate-wasm-builder-runner 1.0.4", +] + [[package]] name = "substrate-runtime-test" version = "2.0.0" @@ -6127,6 +6188,7 @@ dependencies = [ "substrate-keyring 2.0.0", "substrate-offchain-primitives 2.0.0", "substrate-primitives 2.0.0", + "substrate-runtime-interface 2.0.0", "substrate-session 2.0.0", "substrate-state-machine 2.0.0", "substrate-test-runtime-client 2.0.0", @@ -6226,6 +6288,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "substrate-wasm-interface" version = "2.0.0" dependencies = [ + "impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -7364,6 +7427,7 @@ dependencies = [ ] [metadata] +"checksum Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" "checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee" "checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" @@ -7680,7 +7744,7 @@ dependencies = [ "checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" -"checksum primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97b5a08dda18910f056e5c2060c034e77cab18e0bd7d895e44f03207af4c71d5" +"checksum primitive-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a0253db64c26d8b4e7896dd2063b516d2a1b9e0a5da26b5b78335f236d1e9522" "checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" "checksum proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097" "checksum proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "114cdf1f426eb7f550f01af5f53a33c0946156f6814aec939b3bd77e844f9a9d" @@ -7780,6 +7844,7 @@ dependencies = [ "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" +"checksum static_assertions 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fa13613355688665b68639b1c378a62dbedea78aff0fc59a4fa656cbbdec657" "checksum stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" "checksum string-interner 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd710eadff449a1531351b0e43eb81ea404336fa2f56c777427ab0e32a4cf183" diff --git a/Cargo.toml b/Cargo.toml index bdc0d8737518c..9f05104279359 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,9 @@ members = [ "core/rpc", "core/rpc/primitives", "core/rpc-servers", + "core/runtime-interface", + "core/runtime-interface/proc-macro", + "core/runtime-interface/test-wasm", "core/serializer", "core/service", "core/service/test", diff --git a/core/application-crypto/src/ed25519.rs b/core/application-crypto/src/ed25519.rs index bd785acadba46..468907a5c40a4 100644 --- a/core/application-crypto/src/ed25519.rs +++ b/core/application-crypto/src/ed25519.rs @@ -18,6 +18,8 @@ use crate::{RuntimePublic, KeyTypeId}; +use rstd::vec::Vec; + pub use primitives::ed25519::*; mod app { @@ -29,8 +31,7 @@ mod app { } } -pub use app::Public as AppPublic; -pub use app::Signature as AppSignature; +pub use app::{Public as AppPublic, Signature as AppSignature}; #[cfg(feature = "full_crypto")] pub use app::Pair as AppPair; @@ -38,19 +39,19 @@ impl RuntimePublic for Public { type Signature = Signature; fn all(key_type: KeyTypeId) -> crate::Vec { - runtime_io::ed25519_public_keys(key_type) + runtime_io::crypto::ed25519_public_keys(key_type) } - fn generate_pair(key_type: KeyTypeId, seed: Option<&str>) -> Self { - runtime_io::ed25519_generate(key_type, seed) + fn generate_pair(key_type: KeyTypeId, seed: Option>) -> Self { + runtime_io::crypto::ed25519_generate(key_type, seed) } fn sign>(&self, key_type: KeyTypeId, msg: &M) -> Option { - runtime_io::ed25519_sign(key_type, self, msg.as_ref()) + runtime_io::crypto::ed25519_sign(key_type, self, msg.as_ref()) } fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { - runtime_io::ed25519_verify(&signature, msg.as_ref(), self) + runtime_io::crypto::ed25519_verify(&signature, msg.as_ref(), self) } } diff --git a/core/application-crypto/src/lib.rs b/core/application-crypto/src/lib.rs index 66214ce444437..a02b1003b2ff4 100644 --- a/core/application-crypto/src/lib.rs +++ b/core/application-crypto/src/lib.rs @@ -286,7 +286,7 @@ macro_rules! app_crypto_public_common { <$public as $crate::RuntimePublic>::all($key_type).into_iter().map(Self).collect() } - fn generate_pair(seed: Option<&str>) -> Self { + fn generate_pair(seed: Option<$crate::Vec>) -> Self { Self(<$public as $crate::RuntimePublic>::generate_pair($key_type, seed)) } diff --git a/core/application-crypto/src/sr25519.rs b/core/application-crypto/src/sr25519.rs index f6c2388a9bbdf..2ad279a6bfd42 100644 --- a/core/application-crypto/src/sr25519.rs +++ b/core/application-crypto/src/sr25519.rs @@ -18,6 +18,8 @@ use crate::{RuntimePublic, KeyTypeId}; +use rstd::vec::Vec; + pub use primitives::sr25519::*; mod app { @@ -29,8 +31,7 @@ mod app { } } -pub use app::Public as AppPublic; -pub use app::Signature as AppSignature; +pub use app::{Public as AppPublic, Signature as AppSignature}; #[cfg(feature = "full_crypto")] pub use app::Pair as AppPair; @@ -38,19 +39,19 @@ impl RuntimePublic for Public { type Signature = Signature; fn all(key_type: KeyTypeId) -> crate::Vec { - runtime_io::sr25519_public_keys(key_type) + runtime_io::crypto::sr25519_public_keys(key_type) } - fn generate_pair(key_type: KeyTypeId, seed: Option<&str>) -> Self { - runtime_io::sr25519_generate(key_type, seed) + fn generate_pair(key_type: KeyTypeId, seed: Option>) -> Self { + runtime_io::crypto::sr25519_generate(key_type, seed) } fn sign>(&self, key_type: KeyTypeId, msg: &M) -> Option { - runtime_io::sr25519_sign(key_type, self, msg.as_ref()) + runtime_io::crypto::sr25519_sign(key_type, self, msg.as_ref()) } fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { - runtime_io::sr25519_verify(&signature, msg.as_ref(), self) + runtime_io::crypto::sr25519_verify(&signature, msg.as_ref(), self) } } diff --git a/core/application-crypto/src/traits.rs b/core/application-crypto/src/traits.rs index 28073be1cebca..0c99d49ce3bff 100644 --- a/core/application-crypto/src/traits.rs +++ b/core/application-crypto/src/traits.rs @@ -19,7 +19,7 @@ use primitives::crypto::Pair; use codec::Codec; use primitives::crypto::{KeyTypeId, CryptoType, IsWrappedBy, Public}; -use rstd::fmt::Debug; +use rstd::{fmt::Debug, vec::Vec}; /// An application-specific key. pub trait AppKey: 'static + Send + Sync + Sized + CryptoType + Clone { @@ -88,10 +88,13 @@ pub trait RuntimePublic: Sized { /// Returns all public keys for the given key type in the keystore. fn all(key_type: KeyTypeId) -> crate::Vec; - /// Generate a public/private pair for the given key type and store it in the keystore. + /// Generate a public/private pair for the given key type with an optional `seed` and + /// store it in the keystore. + /// + /// The `seed` needs to be valid utf8. /// /// Returns the generated public key. - fn generate_pair(key_type: KeyTypeId, seed: Option<&str>) -> Self; + fn generate_pair(key_type: KeyTypeId, seed: Option>) -> Self; /// Sign the given message with the corresponding private key of this public key. /// @@ -116,10 +119,12 @@ pub trait RuntimeAppPublic: Sized { /// Returns all public keys for this application in the keystore. fn all() -> crate::Vec; - /// Generate a public/private pair and store it in the keystore. + /// Generate a public/private pair with an optional `seed` and store it in the keystore. + /// + /// The `seed` needs to be valid utf8. /// /// Returns the generated public key. - fn generate_pair(seed: Option<&str>) -> Self; + fn generate_pair(seed: Option>) -> Self; /// Sign the given message with the corresponding private key of this public key. /// diff --git a/core/executor/Cargo.toml b/core/executor/Cargo.toml index c5604d50e4a0f..228ef5e0c311b 100644 --- a/core/executor/Cargo.toml +++ b/core/executor/Cargo.toml @@ -17,6 +17,7 @@ wasmi = "0.5.1" parity-wasm = "0.40.3" lazy_static = "1.4.0" wasm-interface = { package = "substrate-wasm-interface", path = "../wasm-interface" } +runtime-interface = { package = "substrate-runtime-interface", path = "../runtime-interface" } externalities = { package = "substrate-externalities", path = "../externalities" } parking_lot = "0.9.0" log = "0.4.8" @@ -46,12 +47,12 @@ test-case = "0.3.3" default = [] wasm-extern-trace = [] wasmtime = [ - "cranelift-codegen", - "cranelift-entity", - "cranelift-frontend", - "cranelift-native", - "cranelift-wasm", - "wasmtime-environ", - "wasmtime-jit", - "wasmtime-runtime", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", ] diff --git a/core/executor/runtime-test/src/lib.rs b/core/executor/runtime-test/src/lib.rs index 61eca8dd4e23f..16c6c353d9e37 100644 --- a/core/executor/runtime-test/src/lib.rs +++ b/core/executor/runtime-test/src/lib.rs @@ -10,8 +10,8 @@ use rstd::{vec::Vec, vec}; #[cfg(not(feature = "std"))] use runtime_io::{ - set_storage, storage, clear_prefix, blake2_128, blake2_256, - twox_128, twox_256, ed25519_verify, sr25519_verify, + storage, hashing::{blake2_128, blake2_256, twox_128, twox_256}, + crypto::{ed25519_verify, sr25519_verify}, }; #[cfg(not(feature = "std"))] use sr_primitives::{print, traits::{BlakeTwo256, Hash}}; @@ -21,20 +21,20 @@ use primitives::{ed25519, sr25519}; primitives::wasm_export_functions! { fn test_data_in(input: Vec) -> Vec { print("set_storage"); - set_storage(b"input", &input); + storage::set(b"input", &input); print("storage"); - let foo = storage(b"foo").unwrap(); + let foo = storage::get(b"foo").unwrap(); print("set_storage"); - set_storage(b"baz", &foo); + storage::set(b"baz", &foo); print("finished!"); b"all ok!".to_vec() } fn test_clear_prefix(input: Vec) -> Vec { - clear_prefix(&input); + storage::clear_prefix(&input); b"all ok!".to_vec() } @@ -142,40 +142,49 @@ primitives::wasm_export_functions! { fn test_offchain_local_storage() -> bool { let kind = primitives::offchain::StorageKind::PERSISTENT; - assert_eq!(runtime_io::local_storage_get(kind, b"test"), None); - runtime_io::local_storage_set(kind, b"test", b"asd"); - assert_eq!(runtime_io::local_storage_get(kind, b"test"), Some(b"asd".to_vec())); - - let res = runtime_io::local_storage_compare_and_set(kind, b"test", Some(b"asd"), b""); - assert_eq!(runtime_io::local_storage_get(kind, b"test"), Some(b"".to_vec())); + assert_eq!(runtime_io::offchain::local_storage_get(kind, b"test"), None); + runtime_io::offchain::local_storage_set(kind, b"test", b"asd"); + assert_eq!(runtime_io::offchain::local_storage_get(kind, b"test"), Some(b"asd".to_vec())); + + let res = runtime_io::offchain::local_storage_compare_and_set( + kind, + b"test", + Some(b"asd".to_vec()), + b"", + ); + assert_eq!(runtime_io::offchain::local_storage_get(kind, b"test"), Some(b"".to_vec())); res } fn test_offchain_local_storage_with_none() { let kind = primitives::offchain::StorageKind::PERSISTENT; - assert_eq!(runtime_io::local_storage_get(kind, b"test"), None); + assert_eq!(runtime_io::offchain::local_storage_get(kind, b"test"), None); - let res = runtime_io::local_storage_compare_and_set(kind, b"test", None, b"value"); + let res = runtime_io::offchain::local_storage_compare_and_set(kind, b"test", None, b"value"); assert_eq!(res, true); - assert_eq!(runtime_io::local_storage_get(kind, b"test"), Some(b"value".to_vec())); + assert_eq!(runtime_io::offchain::local_storage_get(kind, b"test"), Some(b"value".to_vec())); } fn test_offchain_http() -> bool { use primitives::offchain::HttpRequestStatus; let run = || -> Option<()> { - let id = runtime_io::http_request_start("POST", "http://localhost:12345", &[]).ok()?; - runtime_io::http_request_add_header(id, "X-Auth", "test").ok()?; - runtime_io::http_request_write_body(id, &[1, 2, 3, 4], None).ok()?; - runtime_io::http_request_write_body(id, &[], None).ok()?; - let status = runtime_io::http_response_wait(&[id], None); + let id = runtime_io::offchain::http_request_start( + "POST", + "http://localhost:12345", + &[], + ).ok()?; + runtime_io::offchain::http_request_add_header(id, "X-Auth", "test").ok()?; + runtime_io::offchain::http_request_write_body(id, &[1, 2, 3, 4], None).ok()?; + runtime_io::offchain::http_request_write_body(id, &[], None).ok()?; + let status = runtime_io::offchain::http_response_wait(&[id], None); assert!(status == vec![HttpRequestStatus::Finished(200)], "Expected Finished(200) status."); - let headers = runtime_io::http_response_headers(id); + let headers = runtime_io::offchain::http_response_headers(id); assert_eq!(headers, vec![(b"X-Auth".to_vec(), b"hello".to_vec())]); let mut buffer = vec![0; 64]; - let read = runtime_io::http_response_read_body(id, &mut buffer, None).ok()?; + let read = runtime_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?; assert_eq!(read, 3); - assert_eq!(&buffer[0..read], &[1, 2, 3]); - let read = runtime_io::http_response_read_body(id, &mut buffer, None).ok()?; + assert_eq!(&buffer[0..read as usize], &[1, 2, 3]); + let read = runtime_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?; assert_eq!(read, 0); Some(()) @@ -239,7 +248,7 @@ fn execute_sandboxed( }; let mut instance = sandbox::Instance::new(code, &env_builder, &mut state)?; - let result = instance.invoke(b"call", args, &mut state); + let result = instance.invoke("call", args, &mut state); result.map_err(|_| sandbox::HostError) } diff --git a/core/executor/src/host_interface.rs b/core/executor/src/deprecated_host_interface.rs similarity index 89% rename from core/executor/src/host_interface.rs rename to core/executor/src/deprecated_host_interface.rs index 4d1515d76999b..0499cad5663ce 100644 --- a/core/executor/src/host_interface.rs +++ b/core/executor/src/deprecated_host_interface.rs @@ -14,9 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Definition and implementation of the Substrate Wasm host interface. -//! -//! These are the host functions callable from within the Substrate runtime. +//! Definition and implementation of the old and deprecated Substrate runtime interface for the host. use codec::Encode; use std::{convert::TryFrom, str}; @@ -25,7 +23,9 @@ use primitives::{ crypto::KeyTypeId, offchain, }; use trie::{TrieConfiguration, trie_types::Layout}; -use wasm_interface::{FunctionContext, Pointer, PointerType, Result as WResult, WordSize}; +use wasm_interface::{ + Pointer, WordSize, WritePrimitive, ReadPrimitive, FunctionContext, Result as WResult, +}; #[cfg(feature="wasm-extern-trace")] macro_rules! debug_trace { @@ -37,6 +37,8 @@ macro_rules! debug_trace { ( $( $x:tt )* ) => () } +/// The old and deprecated Substrate externals. These are still required for backwards compatibility +/// reasons. pub struct SubstrateExternals; enum RecoverResult { @@ -166,20 +168,20 @@ impl_wasm_host_interface! { ext_print_utf8(utf8_data: Pointer, utf8_len: WordSize) { if let Ok(utf8) = context.read_memory(utf8_data, utf8_len) { - runtime_io::print_utf8(&utf8); + runtime_io::misc::print_utf8(&utf8); } Ok(()) } ext_print_hex(data: Pointer, len: WordSize) { if let Ok(hex) = context.read_memory(data, len) { - runtime_io::print_hex(&hex); + runtime_io::misc::print_hex(&hex); } Ok(()) } ext_print_num(number: u64) { - runtime_io::print_num(number); + runtime_io::misc::print_num(number); Ok(()) } @@ -195,7 +197,10 @@ impl_wasm_host_interface! { let message = context.read_memory(message_data, message_len) .map_err(|_| "Invalid attempt to determine message in ext_log")?; - runtime_io::log(level.into(), &target, &message); + let target_str = std::str::from_utf8(&target) + .map_err(|_| "Target invalid utf8 in ext_log")?; + + runtime_io::logging::log(level.into(), &target_str, &message); Ok(()) } @@ -209,7 +214,7 @@ impl_wasm_host_interface! { .map_err(|_| "Invalid attempt to determine key in ext_set_storage")?; let value = context.read_memory(value_data, value_len) .map_err(|_| "Invalid attempt to determine value in ext_set_storage")?; - Ok(runtime_io::set_storage(&key, &value)) + Ok(runtime_io::storage::set(&key, &value)) } ext_set_child_storage( @@ -227,7 +232,7 @@ impl_wasm_host_interface! { let value = context.read_memory(value_data, value_len) .map_err(|_| "Invalid attempt to determine value in ext_set_child_storage")?; - Ok(runtime_io::set_child_storage(&storage_key, &key, &value)) + Ok(runtime_io::storage::child_set(&storage_key, &key, &value)) } ext_clear_child_storage( @@ -241,19 +246,19 @@ impl_wasm_host_interface! { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_clear_child_storage")?; - Ok(runtime_io::clear_child_storage(&storage_key, &key)) + Ok(runtime_io::storage::child_clear(&storage_key, &key)) } ext_clear_storage(key_data: Pointer, key_len: WordSize) { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_clear_storage")?; - Ok(runtime_io::clear_storage(&key)) + Ok(runtime_io::storage::clear(&key)) } ext_exists_storage(key_data: Pointer, key_len: WordSize) -> u32 { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_exists_storage")?; - Ok(if runtime_io::exists_storage(&key) { 1 } else { 0 }) + Ok(if runtime_io::storage::exists(&key) { 1 } else { 0 }) } ext_exists_child_storage( @@ -267,13 +272,13 @@ impl_wasm_host_interface! { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_exists_child_storage")?; - Ok(if runtime_io::exists_child_storage(&storage_key, &key) { 1 } else { 0 }) + Ok(if runtime_io::storage::child_exists(&storage_key, &key) { 1 } else { 0 }) } ext_clear_prefix(prefix_data: Pointer, prefix_len: WordSize) { let prefix = context.read_memory(prefix_data, prefix_len) .map_err(|_| "Invalid attempt to determine prefix in ext_clear_prefix")?; - Ok(runtime_io::clear_prefix(&prefix)) + Ok(runtime_io::storage::clear_prefix(&prefix)) } ext_clear_child_prefix( @@ -286,13 +291,13 @@ impl_wasm_host_interface! { .map_err(|_| "Invalid attempt to determine storage_key in ext_clear_child_prefix")?; let prefix = context.read_memory(prefix_data, prefix_len) .map_err(|_| "Invalid attempt to determine prefix in ext_clear_child_prefix")?; - Ok(runtime_io::clear_child_prefix(&storage_key, &prefix)) + Ok(runtime_io::storage::child_clear_prefix(&storage_key, &prefix)) } ext_kill_child_storage(storage_key_data: Pointer, storage_key_len: WordSize) { let storage_key = context.read_memory(storage_key_data, storage_key_len) .map_err(|_| "Invalid attempt to determine storage_key in ext_kill_child_storage")?; - Ok(runtime_io::kill_child_storage(&storage_key)) + Ok(runtime_io::storage::child_storage_kill(&storage_key)) } ext_get_allocated_storage( @@ -303,7 +308,7 @@ impl_wasm_host_interface! { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_get_allocated_storage")?; - if let Some(value) = runtime_io::storage(&key) { + if let Some(value) = runtime_io::storage::get(&key) { let offset = context.allocate_memory(value.len() as u32)?; context.write_memory(offset, &value) .map_err(|_| "Invalid attempt to set memory in ext_get_allocated_storage")?; @@ -329,7 +334,7 @@ impl_wasm_host_interface! { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_get_allocated_child_storage")?; - if let Some(value) = runtime_io::child_storage(&storage_key, &key) { + if let Some(value) = runtime_io::storage::child_get(&storage_key, &key) { let offset = context.allocate_memory(value.len() as u32)?; context.write_memory(offset, &value) .map_err(|_| "Invalid attempt to set memory in ext_get_allocated_child_storage")?; @@ -353,7 +358,7 @@ impl_wasm_host_interface! { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to get key in ext_get_storage_into")?; - if let Some(value) = runtime_io::storage(&key) { + if let Some(value) = runtime_io::storage::get(&key) { let data = &value[value.len().min(value_offset as usize)..]; let written = std::cmp::min(value_len as usize, data.len()); context.write_memory(value_data, &data[..written]) @@ -378,7 +383,7 @@ impl_wasm_host_interface! { let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to get key in ext_get_child_storage_into")?; - if let Some(value) = runtime_io::child_storage(&storage_key, &key) { + if let Some(value) = runtime_io::storage::child_get(&storage_key, &key) { let data = &value[value.len().min(value_offset as usize)..]; let written = std::cmp::min(value_len as usize, data.len()); context.write_memory(value_data, &data[..written]) @@ -390,7 +395,7 @@ impl_wasm_host_interface! { } ext_storage_root(result: Pointer) { - context.write_memory(result, runtime_io::storage_root().as_ref()) + context.write_memory(result, runtime_io::storage::root().as_ref()) .map_err(|_| "Invalid attempt to set memory in ext_storage_root".into()) } @@ -401,7 +406,7 @@ impl_wasm_host_interface! { ) -> Pointer { let storage_key = context.read_memory(storage_key_data, storage_key_len) .map_err(|_| "Invalid attempt to determine storage_key in ext_child_storage_root")?; - let value = runtime_io::child_storage_root(&storage_key); + let value = runtime_io::storage::child_root(&storage_key); let offset = context.allocate_memory(value.len() as u32)?; context.write_memory(offset, &value) @@ -420,7 +425,7 @@ impl_wasm_host_interface! { context.read_memory_into(parent_hash_data, &mut parent_hash[..]) .map_err(|_| "Invalid attempt to get parent_hash in ext_storage_changes_root")?; - if let Some(r) = runtime_io::storage_changes_root(parent_hash) { + if let Some(r) = runtime_io::storage::changes_root(parent_hash) { context.write_memory(result, &r[..]) .map_err(|_| "Invalid attempt to set memory in ext_storage_changes_root")?; Ok(1) @@ -454,7 +459,7 @@ impl_wasm_host_interface! { } ext_chain_id() -> u64 { - Ok(runtime_io::chain_id()) + Ok(runtime_io::misc::chain_id()) } ext_twox_64(data: Pointer, len: WordSize, out: Pointer) { @@ -550,7 +555,7 @@ impl_wasm_host_interface! { .map_err(|_| "Invalid attempt to get id in ext_ed25519_public_keys")?; let key_type = KeyTypeId(id); - let keys = runtime_io::ed25519_public_keys(key_type).encode(); + let keys = runtime_io::crypto::ed25519_public_keys(key_type).encode(); let len = keys.len() as u32; let offset = context.allocate_memory(len)?; @@ -605,13 +610,7 @@ impl_wasm_host_interface! { ) }; - let seed = seed.as_ref() - .map(|seed| - std::str::from_utf8(&seed) - .map_err(|_| "Seed not a valid utf8 string in ext_sr25119_generate") - ).transpose()?; - - let pubkey = runtime_io::ed25519_generate(key_type, seed); + let pubkey = runtime_io::crypto::ed25519_generate(key_type, seed); context.write_memory(out, pubkey.as_ref()) .map_err(|_| "Invalid attempt to set out in ext_ed25519_generate".into()) @@ -639,7 +638,7 @@ impl_wasm_host_interface! { let pub_key = ed25519::Public::try_from(pubkey.as_ref()) .map_err(|_| "Invalid `ed25519` public key")?; - let signature = runtime_io::ed25519_sign(key_type, &pub_key, &msg); + let signature = runtime_io::crypto::ed25519_sign(key_type, &pub_key, &msg); match signature { Some(signature) => { @@ -657,7 +656,7 @@ impl_wasm_host_interface! { .map_err(|_| "Invalid attempt to get id in ext_sr25519_public_keys")?; let key_type = KeyTypeId(id); - let keys = runtime_io::sr25519_public_keys(key_type).encode(); + let keys = runtime_io::crypto::sr25519_public_keys(key_type).encode(); let len = keys.len() as u32; let offset = context.allocate_memory(len)?; @@ -711,14 +710,7 @@ impl_wasm_host_interface! { ) }; - let seed = seed.as_ref() - .map(|seed| - std::str::from_utf8(&seed) - .map_err(|_| "Seed not a valid utf8 string in ext_sr25119_generate") - ) - .transpose()?; - - let pubkey = runtime_io::sr25519_generate(key_type, seed); + let pubkey = runtime_io::crypto::sr25519_generate(key_type, seed); context.write_memory(out, pubkey.as_ref()) .map_err(|_| "Invalid attempt to set out in ext_sr25519_generate".into()) @@ -746,7 +738,7 @@ impl_wasm_host_interface! { let pub_key = sr25519::Public::try_from(pubkey.as_ref()) .map_err(|_| "Invalid `sr25519` public key")?; - let signature = runtime_io::sr25519_sign(key_type, &pub_key, &msg); + let signature = runtime_io::crypto::sr25519_sign(key_type, &pub_key, &msg); match signature { Some(signature) => { @@ -789,20 +781,20 @@ impl_wasm_host_interface! { } ext_is_validator() -> u32 { - if runtime_io::is_validator() { Ok(1) } else { Ok(0) } + if runtime_io::offchain::is_validator() { Ok(1) } else { Ok(0) } } ext_submit_transaction(msg_data: Pointer, len: WordSize) -> u32 { let extrinsic = context.read_memory(msg_data, len) .map_err(|_| "OOB while ext_submit_transaction: wasm")?; - let res = runtime_io::submit_transaction(extrinsic); + let res = runtime_io::offchain::submit_transaction(extrinsic); Ok(if res.is_ok() { 0 } else { 1 }) } ext_network_state(written_out: Pointer) -> Pointer { - let res = runtime_io::network_state(); + let res = runtime_io::offchain::network_state(); let encoded = res.encode(); let len = encoded.len() as u32; @@ -817,17 +809,17 @@ impl_wasm_host_interface! { } ext_timestamp() -> u64 { - Ok(runtime_io::timestamp().unix_millis()) + Ok(runtime_io::offchain::timestamp().unix_millis()) } ext_sleep_until(deadline: u64) { - runtime_io::sleep_until(offchain::Timestamp::from_unix_millis(deadline)); + runtime_io::offchain::sleep_until(offchain::Timestamp::from_unix_millis(deadline)); Ok(()) } ext_random_seed(seed_data: Pointer) { // NOTE the runtime as assumptions about seed size. - let seed = runtime_io::random_seed(); + let seed = runtime_io::offchain::random_seed(); context.write_memory(seed_data, &seed) .map_err(|_| "Invalid attempt to set value in ext_random_seed")?; @@ -848,7 +840,7 @@ impl_wasm_host_interface! { let value = context.read_memory(value, value_len) .map_err(|_| "OOB while ext_local_storage_set: wasm")?; - runtime_io::local_storage_set(kind, &key, &value); + runtime_io::offchain::local_storage_set(kind, &key, &value); Ok(()) } @@ -864,7 +856,7 @@ impl_wasm_host_interface! { let key = context.read_memory(key, key_len) .map_err(|_| "OOB while ext_local_storage_get: wasm")?; - let maybe_value = runtime_io::local_storage_get(kind, &key); + let maybe_value = runtime_io::offchain::local_storage_get(kind, &key); let (offset, len) = if let Some(value) = maybe_value { let offset = context.allocate_memory(value.len() as u32)?; @@ -906,10 +898,10 @@ impl_wasm_host_interface! { ) }; - let res = runtime_io::local_storage_compare_and_set( + let res = runtime_io::offchain::local_storage_compare_and_set( kind, &key, - old_value.as_ref().map(|v| v.as_ref()), + old_value, &new_value, ); @@ -936,7 +928,7 @@ impl_wasm_host_interface! { let url_str = str::from_utf8(&url) .map_err(|_| "invalid str while ext_http_request_start: wasm")?; - let id = runtime_io::http_request_start(method_str, url_str, &meta); + let id = runtime_io::offchain::http_request_start(method_str, url_str, &meta); if let Ok(id) = id { Ok(id.into()) @@ -962,7 +954,7 @@ impl_wasm_host_interface! { let value_str = str::from_utf8(&value) .map_err(|_| "Invalid str while ext_http_request_add_header: wasm")?; - let res = runtime_io::http_request_add_header( + let res = runtime_io::offchain::http_request_add_header( offchain::HttpRequestId(request_id as u16), name_str, value_str, @@ -980,7 +972,7 @@ impl_wasm_host_interface! { let chunk = context.read_memory(chunk, chunk_len) .map_err(|_| "OOB while ext_http_request_write_body: wasm")?; - let res = runtime_io::http_request_write_body( + let res = runtime_io::offchain::http_request_write_body( offchain::HttpRequestId(request_id as u16), &chunk, deadline_to_timestamp(deadline), @@ -1006,7 +998,7 @@ impl_wasm_host_interface! { ) .collect::, _>>()?; - let res = runtime_io::http_response_wait(&ids, deadline_to_timestamp(deadline)) + let res = runtime_io::offchain::http_response_wait(&ids, deadline_to_timestamp(deadline)) .into_iter() .map(|status| u32::from(status)) .enumerate() @@ -1027,7 +1019,9 @@ impl_wasm_host_interface! { ) -> Pointer { use codec::Encode; - let headers = runtime_io::http_response_headers(offchain::HttpRequestId(request_id as u16)); + let headers = runtime_io::offchain::http_response_headers( + offchain::HttpRequestId(request_id as u16), + ); let encoded = headers.encode(); let len = encoded.len() as u32; @@ -1050,7 +1044,7 @@ impl_wasm_host_interface! { let mut internal_buffer = Vec::with_capacity(buffer_len as usize); internal_buffer.resize(buffer_len as usize, 0); - let res = runtime_io::http_response_read_body( + let res = runtime_io::offchain::http_response_read_body( offchain::HttpRequestId(request_id as u16), &mut internal_buffer, deadline_to_timestamp(deadline), @@ -1058,7 +1052,7 @@ impl_wasm_host_interface! { Ok(match res { Ok(read) => { - context.write_memory(buffer, &internal_buffer[..read]) + context.write_memory(buffer, &internal_buffer[..read as usize]) .map_err(|_| "Invalid attempt to set memory in ext_http_response_read_body")?; read as u32 @@ -1071,29 +1065,6 @@ impl_wasm_host_interface! { } } -trait WritePrimitive { - fn write_primitive(&mut self, ptr: Pointer, t: T) -> WResult<()>; -} - -impl WritePrimitive for &mut dyn FunctionContext { - fn write_primitive(&mut self, ptr: Pointer, t: u32) -> WResult<()> { - let r = t.to_le_bytes(); - self.write_memory(ptr.cast(), &r) - } -} - -trait ReadPrimitive { - fn read_primitive(&self, offset: Pointer) -> WResult; -} - -impl ReadPrimitive for &mut dyn FunctionContext { - fn read_primitive(&self, ptr: Pointer) -> WResult { - let mut r = [0u8; 4]; - self.read_memory_into(ptr.cast(), &mut r)?; - Ok(u32::from_le_bytes(r)) - } -} - fn deadline_to_timestamp(deadline: u64) -> Option { if deadline == 0 { None diff --git a/core/executor/src/integration_tests/mod.rs b/core/executor/src/integration_tests/mod.rs index 640795f8f0d5e..6db9911d44ab4 100644 --- a/core/executor/src/integration_tests/mod.rs +++ b/core/executor/src/integration_tests/mod.rs @@ -28,10 +28,28 @@ use substrate_offchain::testing; use test_case::test_case; use trie::{TrieConfiguration, trie_types::Layout}; -use crate::{WasmExecutionMethod, call_in_wasm}; +use crate::WasmExecutionMethod; pub type TestExternalities = CoreTestExternalities; +fn call_in_wasm( + function: &str, + call_data: &[u8], + execution_method: WasmExecutionMethod, + ext: &mut E, + code: &[u8], + heap_pages: u64, +) -> crate::error::Result> { + crate::call_in_wasm::( + function, + call_data, + execution_method, + ext, + code, + heap_pages, + ) +} + #[test_case(WasmExecutionMethod::Interpreted)] #[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn returning_should_work(wasm_method: WasmExecutionMethod) { diff --git a/core/executor/src/integration_tests/sandbox.rs b/core/executor/src/integration_tests/sandbox.rs index e4a1a0254dbc1..c18b848acce7e 100644 --- a/core/executor/src/integration_tests/sandbox.rs +++ b/core/executor/src/integration_tests/sandbox.rs @@ -14,14 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . +use super::{TestExternalities, call_in_wasm}; +use crate::WasmExecutionMethod; + use codec::Encode; use runtime_test::WASM_BINARY; use test_case::test_case; use wabt; -use crate::{WasmExecutionMethod, call_in_wasm}; -use crate::integration_tests::TestExternalities; - #[test_case(WasmExecutionMethod::Interpreted)] #[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn sandbox_should_work(wasm_method: WasmExecutionMethod) { @@ -95,6 +95,7 @@ fn sandbox_trap(wasm_method: WasmExecutionMethod) { #[test_case(WasmExecutionMethod::Interpreted)] #[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] +#[should_panic(expected = "Allocator ran out of space")] fn sandbox_should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); @@ -110,18 +111,14 @@ fn sandbox_should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { ) "#).unwrap().encode(); - let res = call_in_wasm( + call_in_wasm( "test_exhaust_heap", &code, wasm_method, &mut ext, &test_code[..], 8, - ); - assert!(res.is_err()); - if let Err(err) = res { - assert!(err.to_string().contains("Allocator ran out of space")); - } + ).unwrap(); } #[test_case(WasmExecutionMethod::Interpreted)] diff --git a/core/executor/src/lib.rs b/core/executor/src/lib.rs index d6d666dd28647..0638a71d1c873 100644 --- a/core/executor/src/lib.rs +++ b/core/executor/src/lib.rs @@ -36,7 +36,7 @@ mod wasmi_execution; mod native_executor; mod sandbox; mod allocator; -mod host_interface; +pub mod deprecated_host_interface; mod wasm_runtime; #[cfg(feature = "wasmtime")] mod wasmtime; @@ -64,7 +64,7 @@ pub use wasm_runtime::WasmExecutionMethod; /// - `heap_pages`: The number of heap pages to allocate. /// /// Returns the `Vec` that contains the return value of the function. -pub fn call_in_wasm( +pub fn call_in_wasm( function: &str, call_data: &[u8], execution_method: WasmExecutionMethod, @@ -76,6 +76,7 @@ pub fn call_in_wasm( execution_method, heap_pages, code, + HF::host_functions(), )?; instance.call(ext, function, call_data) } @@ -102,7 +103,7 @@ mod tests { fn call_in_interpreted_wasm_works() { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let res = call_in_wasm( + let res = call_in_wasm::<_, runtime_io::SubstrateHostFunctions>( "test_empty_return", &[], WasmExecutionMethod::Interpreted, diff --git a/core/executor/src/native_executor.rs b/core/executor/src/native_executor.rs index 0fb84d9972b96..3b33ee514dd39 100644 --- a/core/executor/src/native_executor.rs +++ b/core/executor/src/native_executor.rs @@ -14,15 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use std::{result, cell::RefCell, panic::{UnwindSafe, AssertUnwindSafe}}; -use crate::error::{Error, Result}; -use crate::wasm_runtime::{RuntimesCache, WasmExecutionMethod, WasmRuntime}; -use crate::RuntimeInfo; +use crate::{ + RuntimeInfo, error::{Error, Result}, + wasm_runtime::{RuntimesCache, WasmExecutionMethod, WasmRuntime}, +}; + use runtime_version::{NativeVersion, RuntimeVersion}; + use codec::{Decode, Encode}; + use primitives::{NativeOrEncoded, traits::{CodeExecutor, Externalities}}; + use log::{trace, warn}; +use std::{result, cell::RefCell, panic::{UnwindSafe, AssertUnwindSafe}}; + +use wasm_interface::{HostFunctions, Function}; + thread_local! { static RUNTIMES_CACHE: RefCell = RefCell::new(RuntimesCache::new()); } @@ -62,7 +70,6 @@ pub trait NativeExecutionDispatch: Send + Sync { /// A generic `CodeExecutor` implementation that uses a delegate to determine wasm code equivalence /// and dispatch to native code when possible, falling back on `WasmExecutor` when not. -#[derive(Debug)] pub struct NativeExecutor { /// Dummy field to avoid the compiler complaining about us not using `D`. _dummy: std::marker::PhantomData, @@ -72,6 +79,8 @@ pub struct NativeExecutor { native_version: NativeVersion, /// The number of 64KB pages to allocate for Wasm execution. default_heap_pages: u64, + /// The host functions registered with this instance. + host_functions: Vec<&'static dyn Function>, } impl NativeExecutor { @@ -84,11 +93,18 @@ impl NativeExecutor { /// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. /// Defaults to `DEFAULT_HEAP_PAGES` if `None` is provided. pub fn new(fallback_method: WasmExecutionMethod, default_heap_pages: Option) -> Self { + let mut host_functions = runtime_io::SubstrateHostFunctions::host_functions(); + // Add the old and deprecated host functions as well, so that we support old wasm runtimes. + host_functions.extend( + crate::deprecated_host_interface::SubstrateExternals::host_functions(), + ); + NativeExecutor { _dummy: Default::default(), fallback_method, native_version: D::native_version(), default_heap_pages: default_heap_pages.unwrap_or(DEFAULT_HEAP_PAGES), + host_functions, } } @@ -120,6 +136,7 @@ impl NativeExecutor { ext, self.fallback_method, self.default_heap_pages, + &self.host_functions, )?; let runtime = AssertUnwindSafe(runtime); @@ -143,6 +160,7 @@ impl Clone for NativeExecutor { fallback_method: self.fallback_method, native_version: D::native_version(), default_heap_pages: self.default_heap_pages, + host_functions: self.host_functions.clone(), } } } @@ -194,7 +212,7 @@ impl CodeExecutor for NativeExecutor { target: "executor", "Request for native execution failed (native: {}, chain: {})", self.native_version.runtime_version, - onchain_version + onchain_version, ); safe_call( @@ -211,7 +229,7 @@ impl CodeExecutor for NativeExecutor { target: "executor", "Request for native execution with native call succeeded (native: {}, chain: {}).", self.native_version.runtime_version, - onchain_version + onchain_version, ); used_native = true; diff --git a/core/executor/src/wasm_runtime.rs b/core/executor/src/wasm_runtime.rs index caaa01c43027e..c58f63a1e03de 100644 --- a/core/executor/src/wasm_runtime.rs +++ b/core/executor/src/wasm_runtime.rs @@ -19,16 +19,20 @@ //! The primary means of accessing the runtimes is through a cache which saves the reusable //! components of the runtime that are expensive to initialize. -use crate::error::{Error, WasmError}; -use crate::wasmi_execution; +use crate::{wasmi_execution, error::{Error, WasmError}}; #[cfg(feature = "wasmtime")] use crate::wasmtime; use log::{trace, warn}; + use codec::Decode; + use primitives::{storage::well_known_keys, traits::Externalities, H256}; + use runtime_version::RuntimeVersion; use std::{collections::hash_map::{Entry, HashMap}, panic::AssertUnwindSafe}; +use wasm_interface::Function; + /// The Substrate Wasm runtime. pub trait WasmRuntime { /// Attempt to update the number of heap pages available during execution. @@ -37,6 +41,9 @@ pub trait WasmRuntime { /// the heap pages would not change from its current value. fn update_heap_pages(&mut self, heap_pages: u64) -> bool; + /// Return the host functions that are registered for this Wasm runtime. + fn host_functions(&self) -> &[&'static dyn Function]; + /// Call a method in the Substrate runtime by name. Returns the encoded result on success. fn call(&mut self, ext: &mut dyn Externalities, method: &str, data: &[u8]) -> Result, Error>; @@ -102,6 +109,8 @@ impl RuntimesCache { /// /// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. /// + /// `host_functions` - The host functions that should be registered for the Wasm runtime. + /// /// # Return value /// /// If no error occurred a tuple `(&mut WasmRuntime, H256)` is @@ -118,6 +127,7 @@ impl RuntimesCache { ext: &mut E, wasm_method: WasmExecutionMethod, default_heap_pages: u64, + host_functions: &[&'static dyn Function], ) -> Result<(&mut (dyn WasmRuntime + 'static), &RuntimeVersion, H256), Error> { let code_hash = ext .original_storage_hash(well_known_keys::CODE) @@ -132,12 +142,27 @@ impl RuntimesCache { Entry::Occupied(o) => { let result = o.into_mut(); if let Ok(ref mut cached_runtime) = result { - if !cached_runtime.runtime.update_heap_pages(heap_pages) { + let heap_pages_changed = !cached_runtime.runtime.update_heap_pages(heap_pages); + let host_functions_changed = cached_runtime.runtime.host_functions() + != host_functions; + if heap_pages_changed || host_functions_changed { + let changed = if heap_pages_changed { + "heap_pages" + } else { + "host functions" + }; + trace!( target: "runtimes_cache", - "heap_pages were changed. Reinstantiating the instance", + "{} were changed. Reinstantiating the instance", + changed, + ); + *result = create_versioned_wasm_runtime( + ext, + wasm_method, + heap_pages, + host_functions.into(), ); - *result = create_versioned_wasm_runtime(ext, wasm_method, heap_pages); if let Err(ref err) = result { warn!(target: "runtimes_cache", "cannot create a runtime: {:?}", err); } @@ -147,7 +172,12 @@ impl RuntimesCache { }, Entry::Vacant(v) => { trace!(target: "runtimes_cache", "no instance found in cache, creating now."); - let result = create_versioned_wasm_runtime(ext, wasm_method, heap_pages); + let result = create_versioned_wasm_runtime( + ext, + wasm_method, + heap_pages, + host_functions.into(), + ); if let Err(ref err) = result { warn!(target: "runtimes_cache", "cannot create a runtime: {:?}", err); } @@ -180,14 +210,15 @@ pub fn create_wasm_runtime_with_code( wasm_method: WasmExecutionMethod, heap_pages: u64, code: &[u8], + host_functions: Vec<&'static dyn Function>, ) -> Result, WasmError> { match wasm_method { WasmExecutionMethod::Interpreted => - wasmi_execution::create_instance(code, heap_pages) + wasmi_execution::create_instance(code, heap_pages, host_functions) .map(|runtime| -> Box { Box::new(runtime) }), #[cfg(feature = "wasmtime")] WasmExecutionMethod::Compiled => - wasmtime::create_instance(code, heap_pages) + wasmtime::create_instance(code, heap_pages, host_functions) .map(|runtime| -> Box { Box::new(runtime) }), } } @@ -196,11 +227,12 @@ fn create_versioned_wasm_runtime( ext: &mut E, wasm_method: WasmExecutionMethod, heap_pages: u64, + host_functions: Vec<&'static dyn Function>, ) -> Result { 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)?; + let mut runtime = create_wasm_runtime_with_code(wasm_method, heap_pages, &code, host_functions)?; // Call to determine runtime version. let version_result = { @@ -224,3 +256,16 @@ fn create_versioned_wasm_runtime( version, }) } + +#[cfg(test)] +mod tests { + use wasm_interface::HostFunctions; + + #[test] + fn host_functions_are_equal() { + let host_functions = runtime_io::SubstrateHostFunctions::host_functions(); + + let equal = &host_functions[..] == &host_functions[..]; + assert!(equal, "Host functions are not equal"); + } +} diff --git a/core/executor/src/wasm_utils.rs b/core/executor/src/wasm_utils.rs index 6c1b1ebc50ac1..caa63ddbf2985 100644 --- a/core/executor/src/wasm_utils.rs +++ b/core/executor/src/wasm_utils.rs @@ -47,7 +47,7 @@ macro_rules! gen_functions { { $( $generated:tt )* } $context:ident, ) => ( - &[ $( $generated )* ] + vec![ $( $generated )* ] ); (@INTERNAL { $( $generated:tt )* } @@ -164,7 +164,7 @@ macro_rules! impl_wasm_host_interface { ) => ( impl $crate::wasm_interface::HostFunctions for $interface_name { #[allow(non_camel_case_types)] - fn functions() -> &'static [&'static dyn $crate::wasm_interface::Function] { + fn host_functions() -> Vec<&'static dyn $crate::wasm_interface::Function> { gen_functions!( $context, $( $name( $( $names: $params ),* ) $( -> $returns )? { $( $body )* } )* diff --git a/core/executor/src/wasmi_execution.rs b/core/executor/src/wasmi_execution.rs index dcd6ae89094bc..e8e4aa3e53196 100644 --- a/core/executor/src/wasmi_execution.rs +++ b/core/executor/src/wasmi_execution.rs @@ -24,7 +24,6 @@ use wasmi::{ use crate::error::{Error, WasmError}; use codec::{Encode, Decode}; use primitives::{sandbox as sandbox_primitives, traits::Externalities}; -use crate::host_interface::SubstrateExternals; use crate::sandbox; use crate::allocator; use crate::wasm_utils::interpret_runtime_api_result; @@ -32,28 +31,35 @@ use crate::wasm_runtime::WasmRuntime; use log::trace; use parity_wasm::elements::{deserialize_buffer, DataSegment, Instruction, Module as RawModule}; use wasm_interface::{ - FunctionContext, HostFunctions, Pointer, WordSize, Sandbox, MemoryId, Result as WResult, + FunctionContext, Pointer, WordSize, Sandbox, MemoryId, Result as WResult, Function, }; -struct FunctionExecutor { +struct FunctionExecutor<'a> { sandbox_store: sandbox::Store, heap: allocator::FreeingBumpHeapAllocator, memory: MemoryRef, table: Option, + host_functions: &'a [&'static dyn Function], } -impl FunctionExecutor { - fn new(m: MemoryRef, heap_base: u32, t: Option) -> Result { +impl<'a> FunctionExecutor<'a> { + fn new( + m: MemoryRef, + heap_base: u32, + t: Option, + host_functions: &'a [&'static dyn Function], + ) -> Result { Ok(FunctionExecutor { sandbox_store: sandbox::Store::new(), heap: allocator::FreeingBumpHeapAllocator::new(heap_base), memory: m, table: t, + host_functions, }) } } -impl sandbox::SandboxCapabilities for FunctionExecutor { +impl<'a> sandbox::SandboxCapabilities for FunctionExecutor<'a> { type SupervisorFuncRef = wasmi::FuncRef; fn store(&self) -> &sandbox::Store { @@ -108,7 +114,7 @@ impl sandbox::SandboxCapabilities for FunctionExecutor { } } -impl FunctionContext for FunctionExecutor { +impl<'a> FunctionContext for FunctionExecutor<'a> { fn read_memory_into(&self, address: Pointer, dest: &mut [u8]) -> WResult<()> { self.memory.get_into(address.into(), dest).map_err(|e| e.to_string()) } @@ -136,7 +142,7 @@ impl FunctionContext for FunctionExecutor { } } -impl Sandbox for FunctionExecutor { +impl<'a> Sandbox for FunctionExecutor<'a> { fn memory_get( &mut self, memory_id: MemoryId, @@ -261,48 +267,44 @@ impl Sandbox for FunctionExecutor { } } -impl FunctionExecutor { - fn resolver() -> &'static dyn wasmi::ModuleImportResolver { - struct Resolver; - impl wasmi::ModuleImportResolver for Resolver { - fn resolve_func(&self, name: &str, signature: &wasmi::Signature) - -> std::result::Result - { - let signature = wasm_interface::Signature::from(signature); - - if let Some((index, func)) = SubstrateExternals::functions().iter() - .enumerate() - .find(|f| name == f.1.name()) - { - if signature == func.signature() { - Ok(wasmi::FuncInstance::alloc_host(signature.into(), index)) - } else { - Err(wasmi::Error::Instantiation( - format!( - "Invalid signature for function `{}` expected `{:?}`, got `{:?}`", - func.name(), - signature, - func.signature(), - ) - )) - } +struct Resolver<'a>(&'a[&'static dyn Function]); + +impl<'a> wasmi::ModuleImportResolver for Resolver<'a> { + fn resolve_func(&self, name: &str, signature: &wasmi::Signature) + -> std::result::Result + { + let signature = wasm_interface::Signature::from(signature); + for (function_index, function) in self.0.iter().enumerate() { + if name == function.name() { + if signature == function.signature() { + return Ok( + wasmi::FuncInstance::alloc_host(signature.into(), function_index), + ) } else { - Err(wasmi::Error::Instantiation( - format!("Export {} not found", name), + return Err(wasmi::Error::Instantiation( + format!( + "Invalid signature for function `{}` expected `{:?}`, got `{:?}`", + function.name(), + signature, + function.signature(), + ), )) } } } - &Resolver + + Err(wasmi::Error::Instantiation( + format!("Export {} not found", name), + )) } } -impl wasmi::Externals for FunctionExecutor { +impl<'a> wasmi::Externals for FunctionExecutor<'a> { fn invoke_index(&mut self, index: usize, args: wasmi::RuntimeArgs) -> Result, wasmi::Trap> { let mut args = args.as_ref().iter().copied().map(Into::into); - let function = SubstrateExternals::functions().get(index).ok_or_else(|| + let function = self.host_functions.get(index).ok_or_else(|| Error::from( format!("Could not find host function with index: {}", index), ) @@ -346,38 +348,8 @@ fn call_in_wasm_module( module_instance: &ModuleRef, method: &str, data: &[u8], + host_functions: &[&'static dyn Function], ) -> Result, Error> { - call_in_wasm_module_with_custom_signature( - ext, - module_instance, - method, - |alloc| { - let offset = alloc(data)?; - Ok(vec![I32(offset as i32), I32(data.len() as i32)]) - }, - |res, memory| { - if let Some(I64(retval)) = res { - let (ptr, length) = interpret_runtime_api_result(retval); - memory.get(ptr.into(), length as usize).map_err(|_| Error::Runtime).map(Some) - } else { - Ok(None) - } - } - ) -} - -/// Call a given method in the given wasm-module runtime. -fn call_in_wasm_module_with_custom_signature< - F: FnOnce(&mut dyn FnMut(&[u8]) -> Result) -> Result, Error>, - FR: FnOnce(Option, &MemoryRef) -> Result, Error>, - R, ->( - ext: &mut dyn Externalities, - module_instance: &ModuleRef, - method: &str, - create_parameters: F, - filter_result: FR, -) -> Result { // extract a reference to a linear memory, optional reference to a table // and then initialize FunctionExecutor. let memory = get_mem_instance(module_instance)?; @@ -386,26 +358,25 @@ fn call_in_wasm_module_with_custom_signature< .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, - )?; + let mut fec = FunctionExecutor::new(memory.clone(), heap_base, table, host_functions)?; - let parameters = create_parameters(&mut |data: &[u8]| { - let offset = fec.allocate_memory(data.len() as u32)?; - fec.write_memory(offset, data).map(|_| offset.into()).map_err(Into::into) - })?; + // Write the call data + let offset = fec.allocate_memory(data.len() as u32)?; + fec.write_memory(offset, data)?; let result = externalities::set_and_run_with_externalities( ext, - || module_instance.invoke_export(method, ¶meters, &mut fec), + || module_instance.invoke_export( + method, + &[I32(u32::from(offset) as i32), I32(data.len() as i32)], + &mut fec, + ), ); match result { - Ok(val) => match filter_result(val, &memory)? { - Some(val) => Ok(val), - None => Err(Error::InvalidReturn), + Ok(Some(I64(r))) => { + let (ptr, length) = interpret_runtime_api_result(r); + memory.get(ptr.into(), length as usize).map_err(|_| Error::Runtime) }, Err(e) => { trace!( @@ -415,6 +386,7 @@ fn call_in_wasm_module_with_custom_signature< ); Err(e.into()) }, + _ => Err(Error::InvalidReturn), } } @@ -422,12 +394,13 @@ fn call_in_wasm_module_with_custom_signature< fn instantiate_module( heap_pages: usize, module: &Module, + host_functions: &[&'static dyn Function], ) -> Result { + let resolver = Resolver(host_functions); // start module instantiation. Don't run 'start' function yet. let intermediate_instance = ModuleInstance::new( module, - &ImportsBuilder::new() - .with_resolver("env", FunctionExecutor::resolver()) + &ImportsBuilder::new().with_resolver("env", &resolver), )?; // Verify that the module has the heap base global variable. @@ -559,6 +532,8 @@ pub struct WasmiRuntime { instance: ModuleRef, /// The snapshot of the instance's state taken just after the instantiation. state_snapshot: StateSnapshot, + /// The host functions registered for this instance. + host_functions: Vec<&'static dyn Function>, } impl WasmiRuntime { @@ -583,16 +558,27 @@ impl WasmRuntime for WasmiRuntime { self.state_snapshot.heap_pages == heap_pages } - fn call(&mut self, ext: &mut dyn Externalities, method: &str, data: &[u8]) - -> Result, Error> - { + fn host_functions(&self) -> &[&'static dyn Function] { + &self.host_functions + } + + fn call( + &mut self, + ext: &mut dyn Externalities, + method: &str, + data: &[u8], + ) -> Result, Error> { self.with(|module| { - call_in_wasm_module(ext, module, method, data) + call_in_wasm_module(ext, module, method, data, &self.host_functions) }) } } -pub fn create_instance(code: &[u8], heap_pages: u64) -> Result { +pub fn create_instance( + code: &[u8], + heap_pages: u64, + host_functions: Vec<&'static dyn Function>, +) -> Result { let module = Module::from_buffer(&code).map_err(|_| WasmError::InvalidModule)?; // Extract the data segments from the wasm code. @@ -602,7 +588,7 @@ pub fn create_instance(code: &[u8], heap_pages: u64) -> Result Result, heap_pages: u32, + /// The host functions registered for this instance. + host_functions: Vec<&'static dyn Function>, } impl WasmRuntime for WasmtimeRuntime { @@ -62,6 +63,10 @@ impl WasmRuntime for WasmtimeRuntime { } } + fn host_functions(&self) -> &[&'static dyn Function] { + &self.host_functions + } + fn call(&mut self, ext: &mut dyn Externalities, method: &str, data: &[u8]) -> Result> { call_method( &mut self.context, @@ -76,10 +81,12 @@ impl WasmRuntime for WasmtimeRuntime { /// Create a new `WasmtimeRuntime` given the code. This function performs translation from Wasm to /// machine code, which can be computationally heavy. -pub fn create_instance(code: &[u8], heap_pages: u64) - -> std::result::Result -{ - let (compiled_module, context) = create_compiled_unit(code)?; +pub fn create_instance( + code: &[u8], + heap_pages: u64, + host_functions: Vec<&'static dyn Function>, +) -> std::result::Result { + let (compiled_module, context) = create_compiled_unit(code, &host_functions)?; // Inspect the module for the min and max memory sizes. let (min_memory_size, max_memory_size) = { @@ -103,12 +110,14 @@ pub fn create_instance(code: &[u8], heap_pages: u64) context, max_heap_pages, heap_pages, + host_functions, }) } -fn create_compiled_unit(code: &[u8]) - -> std::result::Result<(CompiledModule, Context), WasmError> -{ +fn create_compiled_unit( + code: &[u8], + host_functions: &[&'static dyn Function], +) -> std::result::Result<(CompiledModule, Context), WasmError> { let compilation_strategy = CompilationStrategy::Cranelift; let compiler = new_compiler(compilation_strategy)?; @@ -120,7 +129,7 @@ fn create_compiled_unit(code: &[u8]) // Instantiate and link the env module. let global_exports = context.get_global_exports(); let compiler = new_compiler(compilation_strategy)?; - let env_module = instantiate_env_module(global_exports, compiler)?; + let env_module = instantiate_env_module(global_exports, compiler, host_functions)?; context.name_instance("env".to_owned(), env_module); // Compile the wasm module. @@ -174,14 +183,12 @@ fn call_method( let trap_error = reset_env_state_and_take_trap(context, None)?; let (output_ptr, output_len) = match outcome { ActionOutcome::Returned { values } => match values.as_slice() { - [RuntimeValue::I64(retval)] => - interpret_runtime_api_result(*retval), + [RuntimeValue::I64(retval)] => interpret_runtime_api_result(*retval), _ => return Err(Error::InvalidReturn), } - ActionOutcome::Trapped { message } => - return Err(trap_error.unwrap_or_else(|| - format!("Wasm execution trapped: {}", message).into() - )), + ActionOutcome::Trapped { message } => return Err(trap_error.unwrap_or_else( + || format!("Wasm execution trapped: {}", message).into() + )), }; // Read the output data from guest memory. @@ -195,6 +202,7 @@ fn call_method( fn instantiate_env_module( global_exports: Rc>>>, compiler: Compiler, + host_functions: &[&'static dyn Function], ) -> std::result::Result { let isa = target_isa()?; @@ -206,7 +214,7 @@ fn instantiate_env_module( let mut finished_functions = >::new(); let mut code_memory = CodeMemory::new(); - for function in SubstrateExternals::functions().iter() { + for function in host_functions { let sig = translate_signature( cranelift_ir_signature(function.signature(), &call_conv), pointer_type @@ -232,7 +240,7 @@ fn instantiate_env_module( let imports = Imports::none(); let data_initializers = Vec::new(); let signatures = PrimaryMap::new(); - let env_state = EnvState::new::(code_memory, compiler); + let env_state = EnvState::new(code_memory, compiler, host_functions); let result = InstanceHandle::new( Rc::new(module), diff --git a/core/executor/src/wasmtime/trampoline.rs b/core/executor/src/wasmtime/trampoline.rs index 7abc59faa5ef5..f6561f485d8e9 100644 --- a/core/executor/src/wasmtime/trampoline.rs +++ b/core/executor/src/wasmtime/trampoline.rs @@ -24,8 +24,8 @@ use cranelift_codegen::ir::{InstBuilder, StackSlotData, StackSlotKind, TrapCode} use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use wasmtime_jit::{CodeMemory, Compiler}; use wasmtime_runtime::{VMContext, VMFunctionBody}; -use wasm_interface::{HostFunctions, Function, Value, ValueType}; -use std::{cmp, panic, ptr}; +use wasm_interface::{Function, Value, ValueType}; +use std::{cmp, panic::{self, AssertUnwindSafe}, ptr}; use crate::error::{Error, WasmError}; use crate::wasmtime::function_executor::{FunctionExecutorState, FunctionExecutor}; @@ -33,7 +33,6 @@ use crate::wasmtime::function_executor::{FunctionExecutorState, FunctionExecutor const CALL_SUCCESS: u32 = 0; const CALL_FAILED_WITH_ERROR: u32 = 1; const CALL_WITH_BAD_HOST_STATE: u32 = 2; -const CALL_PANICKED: u32 = 3; /// A code to trap with that indicates a host call error. const TRAP_USER_CODE: u16 = 0; @@ -45,7 +44,7 @@ const MAX_WASM_TYPE_SIZE: usize = 8; /// The top-level host state of the "env" module. This state is used by the trampoline function to /// construct a `FunctionExecutor` which can execute the host call. pub struct EnvState { - externals: &'static [&'static dyn Function], + host_functions: Vec<&'static dyn Function>, compiler: Compiler, // The code memory must be kept around on the state to prevent it from being dropped. #[allow(dead_code)] @@ -58,13 +57,17 @@ pub struct EnvState { impl EnvState { /// Construct a new `EnvState` which owns the given code memory. - pub fn new(code_memory: CodeMemory, compiler: Compiler) -> Self { + pub fn new( + code_memory: CodeMemory, + compiler: Compiler, + host_functions: &[&'static dyn Function], + ) -> Self { EnvState { - externals: HF::functions(), trap: None, compiler, code_memory, executor_state: None, + host_functions: host_functions.to_vec(), } } @@ -78,11 +81,10 @@ impl EnvState { /// to the call arguments on the stack as arguments. Returns zero on success and a non-zero value /// on failure. unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, func_index: u32, values_vec: *mut i64) -> u32 { - let result = panic::catch_unwind(|| { - if let Some(state) = (*vmctx).host_state().downcast_mut::() { + if let Some(state) = (*vmctx).host_state().downcast_mut::() { match stub_fn_inner( vmctx, - state.externals, + &state.host_functions, &mut state.compiler, state.executor_state.as_mut(), func_index, @@ -94,12 +96,10 @@ unsafe extern "C" fn stub_fn(vmctx: *mut VMContext, func_index: u32, values_vec: CALL_FAILED_WITH_ERROR } } - } else { - // Well, we can't even set a trap message, so we'll just exit without one. - CALL_WITH_BAD_HOST_STATE - } - }); - result.unwrap_or(CALL_PANICKED) + } else { + // Well, we can't even set a trap message, so we'll just exit without one. + CALL_WITH_BAD_HOST_STATE + } } /// Implements most of the logic in `stub_fn` but returning a `Result` instead of an integer error @@ -111,8 +111,7 @@ unsafe fn stub_fn_inner( executor_state: Option<&mut FunctionExecutorState>, func_index: u32, values_vec: *mut i64, -) -> Result<(), Error> -{ +) -> Result<(), Error> { let func = externals.get(func_index as usize) .ok_or_else(|| format!("call to undefined external function with index {}", func_index))?; let executor_state = executor_state @@ -120,22 +119,41 @@ unsafe fn stub_fn_inner( // Build the external function context. let mut context = FunctionExecutor::new(vmctx, compiler, executor_state)?; + let mut context = AssertUnwindSafe(&mut context); - let signature = func.signature(); + // Execute and write output back to the stack. + let return_val = panic::catch_unwind(move || { + let signature = func.signature(); - // Read the arguments from the stack. - let mut args = signature.args.iter() - .enumerate() - .map(|(i, ¶m_type)| read_value_from(values_vec.offset(i as isize), param_type)); + // Read the arguments from the stack. + let mut args = signature.args.iter() + .enumerate() + .map(|(i, ¶m_type)| read_value_from(values_vec.offset(i as isize), param_type)); - // Execute and write output back to the stack. - let return_val = func.execute(&mut context, &mut args) - .map_err(|e| Error::FunctionExecution(func.name().to_string(), e))?; - if let Some(val) = return_val { - write_value_to(values_vec, val); - } + func.execute(&mut **context, &mut args) + }); + + match return_val { + Ok(ret_val) => { + if let Some(val) = ret_val + .map_err(|e| Error::FunctionExecution(func.name().to_string(), e))? { + write_value_to(values_vec, val); + } - Ok(()) + Ok(()) + }, + Err(e) => { + let message = if let Some(err) = e.downcast_ref::() { + err.to_string() + } else if let Some(err) = e.downcast_ref::<&str>() { + err.to_string() + } else { + "Panicked without any further information!".into() + }; + + Err(Error::FunctionExecution(func.name().to_string(), message)) + } + } } /// Create a trampoline for invoking a host function. diff --git a/core/externalities/src/lib.rs b/core/externalities/src/lib.rs index ecd5f7a7a751d..4efbc54a4edc9 100644 --- a/core/externalities/src/lib.rs +++ b/core/externalities/src/lib.rs @@ -132,8 +132,8 @@ pub trait ExternalitiesExt { fn extension(&mut self) -> Option<&mut T>; } -impl ExternalitiesExt for T { - fn extension(&mut self) -> Option<&mut A> { - self.extension_by_type_id(TypeId::of::()).and_then(Any::downcast_mut) +impl ExternalitiesExt for &mut dyn Externalities { + fn extension(&mut self) -> Option<&mut T> { + self.extension_by_type_id(TypeId::of::()).and_then(Any::downcast_mut) } } diff --git a/core/primitives/Cargo.toml b/core/primitives/Cargo.toml index a74092a48e713..20c7e7c9caa2f 100644 --- a/core/primitives/Cargo.toml +++ b/core/primitives/Cargo.toml @@ -36,6 +36,7 @@ tiny-keccak = { version = "1.5.0", optional = true } substrate-debug-derive = { version = "2.0.0", path = "./debug-derive" } externalities = { package = "substrate-externalities", path = "../externalities", optional = true } primitives-storage = { package = "substrate-primitives-storage", path = "storage", default-features = false } +runtime-interface = { package = "substrate-runtime-interface", path = "../runtime-interface", default-features = false } [dev-dependencies] substrate-serializer = { path = "../serializer" } @@ -90,10 +91,11 @@ std = [ "substrate-debug-derive/std", "externalities", "primitives-storage/std", + "runtime-interface/std", ] -# This feature enables all crypto primitives for `no_std` builds like microcontrollers -# or Intel SGX. +# This feature enables all crypto primitives for `no_std` builds like microcontrollers +# or Intel SGX. # For the regular wasm runtime builds this should not be used. full_crypto = [ "ed25519-dalek", @@ -102,5 +104,6 @@ full_crypto = [ "libsecp256k1", "hex", "sha2", - "twox-hash" + "twox-hash", + "runtime-interface/disable_target_static_assertions", ] diff --git a/core/primitives/src/crypto.rs b/core/primitives/src/crypto.rs index ad883c1dc3d86..a452e9ce5b3c6 100644 --- a/core/primitives/src/crypto.rs +++ b/core/primitives/src/crypto.rs @@ -35,6 +35,7 @@ use base58::{FromBase58, ToBase58}; use zeroize::Zeroize; #[doc(hidden)] pub use rstd::ops::Deref; +use runtime_interface::pass_by::PassByInner; /// The root phrase for our publicly known keys. pub const DEV_PHRASE: &str = "bottom drive obey lake curtain smoke basket hold race lonely fit walk"; @@ -866,8 +867,10 @@ pub trait CryptoType { /// /// Values whose first character is `_` are reserved for private use and won't conflict with any /// public modules. -#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)] -#[derive(crate::RuntimeDebug)] +#[derive( + Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode, PassByInner, + crate::RuntimeDebug +)] pub struct KeyTypeId(pub [u8; 4]); impl From for KeyTypeId { diff --git a/core/primitives/src/ecdsa.rs b/core/primitives/src/ecdsa.rs index 691e9fba5e184..f1d4d2446aa13 100644 --- a/core/primitives/src/ecdsa.rs +++ b/core/primitives/src/ecdsa.rs @@ -18,6 +18,7 @@ //! Simple ECDSA API. // end::description[] +#[cfg(feature = "full_crypto")] use rstd::vec::Vec; use rstd::cmp::Ordering; diff --git a/core/primitives/src/ed25519.rs b/core/primitives/src/ed25519.rs index 0a25b8c806644..c0894b8782d49 100644 --- a/core/primitives/src/ed25519.rs +++ b/core/primitives/src/ed25519.rs @@ -18,6 +18,7 @@ //! Simple Ed25519 API. // end::description[] +#[cfg(feature = "full_crypto")] use rstd::vec::Vec; use crate::{hash::H256, hash::H512}; @@ -36,6 +37,7 @@ use crate::crypto::Ss58Codec; #[cfg(feature = "std")] use serde::{de, Serializer, Serialize, Deserializer, Deserialize}; use crate::{crypto::{Public as TraitPublic, UncheckedFrom, CryptoType, Derive}}; +use runtime_interface::pass_by::PassByInner; /// A secret seed. It's not called a "secret key" because ring doesn't expose the secret keys /// of the key pair (yeah, dumb); as such we're forced to remember the seed manually if we @@ -45,7 +47,7 @@ type Seed = [u8; 32]; /// A public key. #[cfg_attr(feature = "full_crypto", derive(Hash))] -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Default)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Default, PassByInner)] pub struct Public(pub [u8; 32]); /// A key pair. @@ -162,7 +164,7 @@ impl<'de> Deserialize<'de> for Public { } /// A signature (a 512-bit value). -#[derive(Encode, Decode)] +#[derive(Encode, Decode, PassByInner)] pub struct Signature(pub [u8; 64]); impl rstd::convert::TryFrom<&[u8]> for Signature { diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index 21483cbd5c52d..c7b18399b30b2 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -236,7 +236,7 @@ pub trait TypeId { /// A log level matching the one from `log` crate. /// /// Used internally by `runtime_io::log` method. -#[repr(u32)] +#[derive(Encode, Decode, runtime_interface::pass_by::PassByEnum, Copy, Clone)] pub enum LogLevel { /// `Error` log level. Error = 1, diff --git a/core/primitives/src/offchain.rs b/core/primitives/src/offchain.rs index c69c074b747b1..7b24e5ee72c98 100644 --- a/core/primitives/src/offchain.rs +++ b/core/primitives/src/offchain.rs @@ -19,11 +19,12 @@ use codec::{Encode, Decode}; use rstd::{prelude::{Vec, Box}, convert::TryFrom}; use crate::RuntimeDebug; +use runtime_interface::pass_by::{PassByCodec, PassByInner, PassByEnum}; pub use crate::crypto::KeyTypeId; /// A type of supported crypto. -#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, RuntimeDebug, PassByEnum)] #[repr(C)] pub enum StorageKind { /// Persistent storage is non-revertible and not fork-aware. It means that any value @@ -59,7 +60,7 @@ impl From for u32 { } /// Opaque type for offchain http requests. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, Encode, Decode, PassByInner)] #[cfg_attr(feature = "std", derive(Hash))] pub struct HttpRequestId(pub u16); @@ -70,7 +71,7 @@ impl From for u32 { } /// An error enum returned by some http methods. -#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug)] +#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, Encode, Decode, PassByEnum)] #[repr(C)] pub enum HttpError { /// The requested action couldn't been completed within a deadline. @@ -101,7 +102,7 @@ impl From for u32 { } /// Status of the HTTP request -#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug)] +#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, Encode, Decode, PassByCodec)] pub enum HttpRequestStatus { /// Deadline was reached while we waited for this request to finish. /// @@ -147,7 +148,7 @@ impl TryFrom for HttpRequestStatus { /// A blob to hold information about the local node's network state /// without committing to its format. -#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug)] +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByCodec)] pub struct OpaqueNetworkState { /// PeerId of the local node. pub peer_id: OpaquePeerId, @@ -156,7 +157,7 @@ pub struct OpaqueNetworkState { } /// Simple blob to hold a `PeerId` without committing to its format. -#[derive(Default, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug)] +#[derive(Default, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByInner)] pub struct OpaquePeerId(pub Vec); impl OpaquePeerId { @@ -167,7 +168,7 @@ impl OpaquePeerId { } /// Simple blob to hold a `Multiaddr` without committing to its format. -#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug)] +#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByInner)] pub struct OpaqueMultiaddr(pub Vec); impl OpaqueMultiaddr { @@ -178,11 +179,11 @@ impl OpaqueMultiaddr { } /// Opaque timestamp type -#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, RuntimeDebug)] +#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, RuntimeDebug, PassByInner, Encode, Decode)] pub struct Timestamp(u64); /// Duration type -#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, RuntimeDebug)] +#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, RuntimeDebug, PassByInner, Encode, Decode)] pub struct Duration(u64); impl Duration { diff --git a/core/primitives/src/sr25519.rs b/core/primitives/src/sr25519.rs index 7bec910d730ca..eed2b70830889 100644 --- a/core/primitives/src/sr25519.rs +++ b/core/primitives/src/sr25519.rs @@ -20,6 +20,7 @@ //! Note: `CHAIN_CODE_LENGTH` must be equal to `crate::crypto::JUNCTION_ID_LEN` //! for this to work. // end::description[] +#[cfg(feature = "full_crypto")] use rstd::vec::Vec; #[cfg(feature = "full_crypto")] use schnorrkel::{signing_context, ExpansionMode, Keypair, SecretKey, MiniSecretKey, PublicKey, @@ -44,6 +45,7 @@ use codec::{Encode, Decode}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "full_crypto")] use schnorrkel::keys::{MINI_SECRET_KEY_LENGTH, SECRET_KEY_LENGTH}; +use runtime_interface::pass_by::PassByInner; // signing context #[cfg(feature = "full_crypto")] @@ -51,7 +53,7 @@ const SIGNING_CTX: &[u8] = b"substrate"; /// An Schnorrkel/Ristretto x25519 ("sr25519") public key. #[cfg_attr(feature = "full_crypto", derive(Hash))] -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Default)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Default, PassByInner)] pub struct Public(pub [u8; 32]); /// An Schnorrkel/Ristretto x25519 ("sr25519") key pair. @@ -163,7 +165,7 @@ impl<'de> Deserialize<'de> for Public { /// An Schnorrkel/Ristretto x25519 ("sr25519") signature. /// /// Instead of importing it for the local module, alias it to be available as a public type -#[derive(Encode, Decode)] +#[derive(Encode, Decode, PassByInner)] pub struct Signature(pub [u8; 64]); impl rstd::convert::TryFrom<&[u8]> for Signature { diff --git a/core/primitives/storage/src/lib.rs b/core/primitives/storage/src/lib.rs index dcdc223994e26..ba36e2c80f81e 100644 --- a/core/primitives/storage/src/lib.rs +++ b/core/primitives/storage/src/lib.rs @@ -40,6 +40,14 @@ pub struct StorageData( pub Vec, ); +/// A set of key value pairs for storage. +#[cfg(feature = "std")] +pub type StorageOverlay = std::collections::HashMap, Vec>; + +/// A set of key value pairs for children storage; +#[cfg(feature = "std")] +pub type ChildrenStorageOverlay = std::collections::HashMap, StorageOverlay>; + /// Storage change set #[derive(RuntimeDebug)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize, PartialEq, Eq))] diff --git a/core/rpc/src/state/tests.rs b/core/rpc/src/state/tests.rs index 5dfa234337afa..2e3690f3058a2 100644 --- a/core/rpc/src/state/tests.rs +++ b/core/rpc/src/state/tests.rs @@ -22,7 +22,7 @@ use std::sync::Arc; use assert_matches::assert_matches; use futures::stream::Stream; use primitives::storage::well_known_keys; -use sr_io::blake2_256; +use sr_io::hashing::blake2_256; use test_client::{ prelude::*, consensus::BlockOrigin, diff --git a/core/runtime-interface/Cargo.toml b/core/runtime-interface/Cargo.toml new file mode 100644 index 0000000000000..b809e0ccbe1ac --- /dev/null +++ b/core/runtime-interface/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "substrate-runtime-interface" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +wasm-interface = { package = "substrate-wasm-interface", path = "../wasm-interface", optional = true } +rstd = { package = "sr-std", path = "../sr-std", default-features = false } +substrate-runtime-interface-proc-macro = { path = "proc-macro" } +externalities = { package = "substrate-externalities", path = "../externalities", optional = true } +codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false } +environmental = { version = "1.0.2", optional = true } +static_assertions = "1.0.0" +primitive-types = { version = "0.6.1", default-features = false } + +[dev-dependencies] +executor = { package = "substrate-executor", path = "../executor" } +test-wasm = { package = "substrate-runtime-interface-test-wasm", path = "test-wasm" } +state_machine = { package = "substrate-state-machine", path = "../state-machine" } +primitives = { package = "substrate-primitives", path = "../primitives" } +runtime-io = { package = "sr-io", path = "../sr-io" } + +[features] +default = [ "std" ] +std = [ + "wasm-interface", + "rstd/std", + "codec/std", + "externalities", + "environmental", + "primitive-types/std", +] + +# ATTENTION +# +# Only use when you know what you are doing. +# +# Disables static assertions in `impls.rs` that checks the word size. To prevent any footgun, the +# check is changed into a runtime check. +disable_target_static_assertions = [] diff --git a/core/runtime-interface/proc-macro/Cargo.toml b/core/runtime-interface/proc-macro/Cargo.toml new file mode 100644 index 0000000000000..0b073b854749d --- /dev/null +++ b/core/runtime-interface/proc-macro/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "substrate-runtime-interface-proc-macro" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "1.0.5", features = [ "full", "visit", "fold", "extra-traits" ] } +quote = "1.0.2" +proc-macro2 = "1.0.3" +Inflector = "0.11.4" +proc-macro-crate = "0.1.4" + +[dev-dependencies] +runtime-interface = { package = "substrate-runtime-interface", path = ".." } +codec = { package = "parity-scale-codec", version = "1.0.6", features = [ "derive" ] } +externalities = { package = "substrate-externalities", path = "../../externalities" } +rustversion = "1.0.0" +trybuild = "1.0.17" + +# We actually don't need the `std` feature in this crate, but the tests require it. +[features] +default = [ "std" ] +std = [] diff --git a/core/runtime-interface/proc-macro/src/lib.rs b/core/runtime-interface/proc-macro/src/lib.rs new file mode 100644 index 0000000000000..febc388c77b17 --- /dev/null +++ b/core/runtime-interface/proc-macro/src/lib.rs @@ -0,0 +1,260 @@ +// Copyright 2019 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 . + +//! This crate provides procedural macros for usage within the context of the Substrate runtime +//! interface. +//! +//! The following macros are provided: +//! +//! 1. The [`#[runtime_interface]`](attr.runtime_interface.html) attribute macro for generating the +//! runtime interfaces. +//! 2. The [`PassByCodec`](derive.PassByCodec.html) derive macro for implementing `PassBy` with `Codec`. +//! 3. The [`PassByEnum`](derive.PassByInner.html) derive macro for implementing `PassBy` with `Enum`. +//! 4. The [`PassByInner`](derive.PassByInner.html) derive macro for implementing `PassBy` with `Inner`. + +extern crate proc_macro; + +use syn::{parse_macro_input, ItemTrait, DeriveInput}; + +mod pass_by; +mod runtime_interface; +mod utils; + +/// Attribute macro for transforming a trait declaration into a runtime interface. +/// +/// A runtime interface is a fixed interface between a Substrate compatible runtime and the native +/// node. This interface is callable from a native and a wasm runtime. The macro will generate the +/// corresponding code for the native implementation and the code for calling from the wasm +/// side to the native implementation. +/// +/// The macro expects the runtime interface declaration as trait declaration: +/// +/// ``` +/// # use runtime_interface::runtime_interface; +/// +/// #[runtime_interface] +/// trait Interface { +/// /// A function that can be called from native/wasm. +/// /// +/// /// The implementation given to this function is only compiled on native. +/// fn call_some_complex_code(data: &[u8]) -> Vec { +/// // Here you could call some rather complex code that only compiles on native or +/// // is way faster in native than executing it in wasm. +/// Vec::new() +/// } +/// +/// /// A function can take a `&self` or `&mut self` argument to get access to the +/// /// `Externalities`. (The generated method does not require +/// /// this argument, so the function can be called just with the `optional` argument) +/// fn set_or_clear(&mut self, optional: Option>) { +/// match optional { +/// Some(value) => self.set_storage([1, 2, 3, 4].to_vec(), value), +/// None => self.clear_storage(&[1, 2, 3, 4]), +/// } +/// } +/// } +/// ``` +/// +/// +/// The given example will generate roughly the following code for native: +/// +/// ``` +/// // The name of the trait is converted to snake case and used as mod name. +/// // +/// // Be aware that this module is not `public`, the visibility of the module is determined based +/// // on the visibility of the trait declaration. +/// mod interface { +/// trait Interface { +/// fn call_some_complex_code(data: &[u8]) -> Vec; +/// fn set_or_clear(&mut self, optional: Option>); +/// } +/// +/// impl Interface for &mut dyn externalities::Externalities { +/// fn call_some_complex_code(data: &[u8]) -> Vec { Vec::new() } +/// fn set_or_clear(&mut self, optional: Option>) { +/// match optional { +/// Some(value) => self.set_storage([1, 2, 3, 4].to_vec(), value), +/// None => self.clear_storage(&[1, 2, 3, 4]), +/// } +/// } +/// } +/// +/// pub fn call_some_complex_code(data: &[u8]) -> Vec { +/// <&mut dyn externalities::Externalities as Interface>::call_some_complex_code(data) +/// } +/// +/// pub fn set_or_clear(optional: Option>) { +/// externalities::with_externalities(|mut ext| Interface::set_or_clear(&mut ext, optional)) +/// .expect("`set_or_clear` called outside of an Externalities-provided environment.") +/// } +/// +/// /// This type implements the `HostFunctions` trait (from `substrate-wasm-interface`) and +/// /// provides the host implementation for the wasm side. The host implementation converts the +/// /// arguments from wasm to native and calls the corresponding native function. +/// /// +/// /// This type needs to be passed to the wasm executor, so that the host functions will be +/// /// registered in the executor. +/// pub struct HostFunctions; +/// } +/// ``` +/// +/// +/// The given example will generate roughly the following code for wasm: +/// +/// ``` +/// mod interface { +/// mod extern_host_functions_impls { +/// extern "C" { +/// /// Every function is exported as `ext_TRAIT_NAME_FUNCTION_NAME_version_VERSION`. +/// /// +/// /// The type for each argument of the exported function depends on +/// /// `::FFIType`. +/// pub fn ext_Interface_call_some_complex_code_version_1(data: u64); +/// pub fn ext_Interface_set_or_clear_version_1(optional: u64); +/// } +/// } +/// +/// /// The type is actually `ExchangeableFunction` (from `substrate-runtime-interface`). +/// /// +/// /// This can be used to replace the implementation of the `call_some_complex_code` function. +/// /// Instead of calling into the host, the callee will automatically call the other +/// /// implementation. +/// /// +/// /// To replace the implementation: +/// /// +/// /// `host_call_some_complex_code.replace_implementation(some_other_impl)` +/// pub static host_call_some_complex_code: () = (); +/// pub static host_set_or_clear: () = (); +/// +/// pub fn call_some_complex_code(data: &[u8]) -> Vec { +/// // This is the actual call: `host_call_some_complex_code.get()(data)` +/// // +/// // But that does not work for several reasons in this example, so we just return an +/// // empty vector. +/// Vec::new() +/// } +/// +/// pub fn set_or_clear(optional: Option>) { +/// // Same as above +/// } +/// } +/// ``` +/// +/// # Argument types +/// +/// The macro supports any kind of argument type, as long as it implements `RIType` and the required +/// `FromFFIValue`/`IntoFFIValue` from `substrate-runtime-interface`. The macro will convert each +/// argument to the corresponding FFI representation and will call into the host using this FFI +/// representation. On the host each argument is converted back to the native representation and +/// the native implementation is called. Any return value is handled in the same way. +/// +/// # Wasm only interfaces +/// +/// Some interfaces are only required from within the wasm runtime e.g. the allocator interface. +/// To support this, the macro can be called like `#[runtime_interface(wasm_only)]`. This instructs +/// the macro to make two significant changes to the generated code: +/// +/// 1. The generated functions are not callable from the native side. +/// 2. The trait as shown above is not implemented for `Externalities` and is instead implemented +/// for `FunctionExecutor` (from `substrate-wasm-interface`). +#[proc_macro_attribute] +pub fn runtime_interface( + attrs: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let trait_def = parse_macro_input!(input as ItemTrait); + let wasm_only = parse_macro_input!(attrs as Option); + + runtime_interface::runtime_interface_impl(trait_def, wasm_only.is_some()) + .unwrap_or_else(|e| e.to_compile_error()) + .into() +} + +/// Derive macro for implementing `PassBy` with the `Codec` strategy. +/// +/// This requires that the type implements `Encode` and `Decode` from `parity-scale-codec`. +/// +/// # Example +/// +/// ``` +/// # use runtime_interface::pass_by::PassByCodec; +/// # use codec::{Encode, Decode}; +/// #[derive(PassByCodec, Encode, Decode)] +/// struct EncodableType { +/// name: Vec, +/// param: u32, +/// } +/// ``` +#[proc_macro_derive(PassByCodec)] +pub fn pass_by_codec(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + pass_by::codec_derive_impl(input).unwrap_or_else(|e| e.to_compile_error()).into() +} + +/// Derive macro for implementing `PassBy` with the `Inner` strategy. +/// +/// Besides implementing `PassBy`, this derive also implements the helper trait `PassByInner`. +/// +/// The type is required to be a struct with just one field. The field type needs to implement +/// the required traits to pass it between the wasm and the native side. (See the runtime interface +/// crate for more information about these traits.) +/// +/// # Example +/// +/// ``` +/// # use runtime_interface::pass_by::PassByInner; +/// #[derive(PassByInner)] +/// struct Data([u8; 32]); +/// ``` +/// +/// ``` +/// # use runtime_interface::pass_by::PassByInner; +/// #[derive(PassByInner)] +/// struct Data { +/// data: [u8; 32], +/// } +/// ``` +#[proc_macro_derive(PassByInner)] +pub fn pass_by_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + pass_by::inner_derive_impl(input).unwrap_or_else(|e| e.to_compile_error()).into() +} + +/// Derive macro for implementing `PassBy` with the `Enum` strategy. +/// +/// Besides implementing `PassBy`, this derive also implements `TryFrom` and `From for u8` +/// for the type. +/// +/// The type is required to be an enum with only unit variants and at maximum `256` variants. Also +/// it is required that the type implements `Copy`. +/// +/// # Example +/// +/// ``` +/// # use runtime_interface::pass_by::PassByEnum; +/// #[derive(PassByEnum, Copy, Clone)] +/// enum Data { +/// Okay, +/// NotOkay, +/// // This will not work with the derive. +/// //Why(u32), +/// } +/// ``` +#[proc_macro_derive(PassByEnum)] +pub fn pass_by_enum(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + pass_by::enum_derive_impl(input).unwrap_or_else(|e| e.to_compile_error()).into() +} diff --git a/core/runtime-interface/proc-macro/src/pass_by/codec.rs b/core/runtime-interface/proc-macro/src/pass_by/codec.rs new file mode 100644 index 0000000000000..c5c6980d2c652 --- /dev/null +++ b/core/runtime-interface/proc-macro/src/pass_by/codec.rs @@ -0,0 +1,58 @@ +// Copyright 2019 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 . + +//! Derive macro implementation of `PassBy` with the associated type set to `Codec`. +//! +//! It is required that the type implements `Encode` and `Decode` from the `parity-scale-codec` +//! crate. + +use crate::utils::{generate_crate_access, generate_runtime_interface_include}; + +use syn::{DeriveInput, Result, Generics, parse_quote}; + +use quote::quote; + +use proc_macro2::TokenStream; + +/// The derive implementation for `PassBy` with `Codec`. +pub fn derive_impl(mut input: DeriveInput) -> Result { + add_trait_bounds(&mut input.generics); + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let crate_include = generate_runtime_interface_include(); + let crate_ = generate_crate_access(); + let ident = input.ident; + + let res = quote! { + const _: () = { + #crate_include + + impl #impl_generics #crate_::pass_by::PassBy for #ident #ty_generics #where_clause { + type PassBy = #crate_::pass_by::Codec<#ident>; + } + }; + }; + + Ok(res) +} + +/// Add the `codec::Codec` trait bound to every type parameter. +fn add_trait_bounds(generics: &mut Generics) { + let crate_ = generate_crate_access(); + + generics.type_params_mut() + .for_each(|type_param| type_param.bounds.push(parse_quote!(#crate_::codec::Codec))); +} + diff --git a/core/runtime-interface/proc-macro/src/pass_by/enum_.rs b/core/runtime-interface/proc-macro/src/pass_by/enum_.rs new file mode 100644 index 0000000000000..ac5e67552083b --- /dev/null +++ b/core/runtime-interface/proc-macro/src/pass_by/enum_.rs @@ -0,0 +1,101 @@ +// Copyright 2019 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 . + +//! Derive macro implementation of `PassBy` with the associated type set to `Enum`. +//! +//! Besides `PassBy`, `TryFrom` and `From for u8` are implemented for the type. + +use crate::utils::{generate_crate_access, generate_runtime_interface_include}; + +use syn::{DeriveInput, Result, Data, Fields, Error, Ident}; + +use quote::quote; + +use proc_macro2::{TokenStream, Span}; + +/// The derive implementation for `PassBy` with `Enum`. +pub fn derive_impl(input: DeriveInput) -> Result { + let crate_include = generate_runtime_interface_include(); + let crate_ = generate_crate_access(); + let ident = input.ident; + let enum_fields = get_enum_field_idents(&input.data)? + .enumerate() + .map(|(i, v)| { + let i = i as u8; + + v.map(|v| (quote!(#i => Ok(#ident::#v)), quote!(#ident::#v => #i))) + }) + .collect::>>()?; + let try_from_variants = enum_fields.iter().map(|i| &i.0); + let into_variants = enum_fields.iter().map(|i| &i.1); + + let res = quote! { + const _: () = { + #crate_include + + impl #crate_::pass_by::PassBy for #ident { + type PassBy = #crate_::pass_by::Enum<#ident>; + } + + impl #crate_::rstd::convert::TryFrom for #ident { + type Error = (); + + fn try_from(inner: u8) -> #crate_::rstd::result::Result { + match inner { + #( #try_from_variants, )* + _ => Err(()), + } + } + } + + impl From<#ident> for u8 { + fn from(var: #ident) -> u8 { + match var { + #( #into_variants ),* + } + } + } + }; + }; + + Ok(res) +} + +/// Get the enum fields idents of the given `data` object as iterator. +/// +/// Returns an error if the number of variants is greater than `256`, the given `data` is not an +/// enum or a variant is not an unit. +fn get_enum_field_idents<'a>(data: &'a Data) -> Result>> { + match data { + Data::Enum(d) => { + if d.variants.len() <= 256 { + Ok( + d.variants.iter().map(|v| if let Fields::Unit = v.fields { + Ok(&v.ident) + } else { + Err(Error::new( + Span::call_site(), + "`PassByEnum` only supports unit variants.", + )) + }) + ) + } else { + Err(Error::new(Span::call_site(), "`PassByEnum` only supports `256` variants.")) + } + }, + _ => Err(Error::new(Span::call_site(), "`PassByEnum` only supports enums as input type.")) + } +} diff --git a/core/runtime-interface/proc-macro/src/pass_by/inner.rs b/core/runtime-interface/proc-macro/src/pass_by/inner.rs new file mode 100644 index 0000000000000..0ca8cd0b5cd91 --- /dev/null +++ b/core/runtime-interface/proc-macro/src/pass_by/inner.rs @@ -0,0 +1,110 @@ +// Copyright 2019 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 . + +//! Derive macro implementation of `PassBy` with the associated type set to `Inner` and of the +//! helper trait `PassByInner`. +//! +//! It is required that the type is a newtype struct, otherwise an error is generated. + +use crate::utils::{generate_crate_access, generate_runtime_interface_include}; + +use syn::{DeriveInput, Result, Generics, parse_quote, Type, Data, Error, Fields, Ident}; + +use quote::quote; + +use proc_macro2::{TokenStream, Span}; + +/// The derive implementation for `PassBy` with `Inner` and `PassByInner`. +pub fn derive_impl(mut input: DeriveInput) -> Result { + add_trait_bounds(&mut input.generics); + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let crate_include = generate_runtime_interface_include(); + let crate_ = generate_crate_access(); + let ident = input.ident; + let (inner_ty, inner_name) = extract_inner_ty_and_name(&input.data)?; + + let access_inner = match inner_name { + Some(ref name) => quote!(self.#name), + None => quote!(self.0), + }; + + let from_inner = match inner_name { + Some(name) => quote!(Self { #name: inner }), + None => quote!(Self(inner)), + }; + + let res = quote! { + const _: () = { + #crate_include + + impl #impl_generics #crate_::pass_by::PassBy for #ident #ty_generics #where_clause { + type PassBy = #crate_::pass_by::Inner<#ident, #inner_ty>; + } + + impl #impl_generics #crate_::pass_by::PassByInner for #ident #ty_generics #where_clause { + type Inner = #inner_ty; + + fn into_inner(self) -> Self::Inner { + #access_inner + } + + fn inner(&self) -> &Self::Inner { + &#access_inner + } + + fn from_inner(inner: Self::Inner) -> Self { + #from_inner + } + } + }; + }; + + Ok(res) +} + +/// Add the `RIType` trait bound to every type parameter. +fn add_trait_bounds(generics: &mut Generics) { + let crate_ = generate_crate_access(); + + generics.type_params_mut() + .for_each(|type_param| type_param.bounds.push(parse_quote!(#crate_::RIType))); +} + +/// Extract the inner type and optional name from given input data. +/// +/// It also checks that the input data is a newtype struct. +fn extract_inner_ty_and_name(data: &Data) -> Result<(Type, Option)> { + if let Data::Struct(ref struct_data) = data { + match struct_data.fields { + Fields::Named(ref named) if named.named.len() == 1 => { + let field = &named.named[0]; + return Ok((field.ty.clone(), field.ident.clone())) + }, + Fields::Unnamed(ref unnamed) if unnamed.unnamed.len() == 1 => { + let field = &unnamed.unnamed[0]; + return Ok((field.ty.clone(), field.ident.clone())) + } + _ => {}, + } + } + + Err( + Error::new( + Span::call_site(), + "Only newtype/one field structs are supported by `PassByInner`!", + ) + ) +} diff --git a/core/runtime-interface/proc-macro/src/pass_by/mod.rs b/core/runtime-interface/proc-macro/src/pass_by/mod.rs new file mode 100644 index 0000000000000..23d511183293c --- /dev/null +++ b/core/runtime-interface/proc-macro/src/pass_by/mod.rs @@ -0,0 +1,25 @@ +// Copyright 2019 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 . + +//! All the `PassBy*` derive implementations. + +mod codec; +mod enum_; +mod inner; + +pub use self::codec::derive_impl as codec_derive_impl; +pub use enum_::derive_impl as enum_derive_impl; +pub use inner::derive_impl as inner_derive_impl; diff --git a/core/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs b/core/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs new file mode 100644 index 0000000000000..dbedba000e262 --- /dev/null +++ b/core/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs @@ -0,0 +1,186 @@ +// Copyright 2019 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 . + +//! Generates the bare function interface for a given trait definition. +//! +//! The bare functions are the ones that will be called by the user. On the native/host side, these +//! functions directly execute the provided implementation. On the wasm side, these +//! functions will prepare the parameters for the FFI boundary, call the external host function +//! exported into wasm and convert back the result. +//! +//! [`generate`](bare_function_interface::generate) is the entry point for generating for each +//! trait method one bare function. +//! +//! [`function_for_method`](bare_function_interface::function_for_method) generates the bare +//! function per trait method. Each bare function contains both implementations. The implementations +//! are feature-gated, so that one is compiled for the native and the other for the wasm side. + +use crate::utils::{ + generate_crate_access, create_exchangeable_host_function_ident, get_function_arguments, + get_function_argument_names, get_trait_methods, +}; + +use syn::{ + Ident, ItemTrait, TraitItemMethod, FnArg, Signature, Result, spanned::Spanned, parse_quote, +}; + +use proc_macro2::{TokenStream, Span}; + +use quote::{quote, quote_spanned}; + +use std::iter; + +/// Generate one bare function per trait method. The name of the bare function is equal to the name +/// of the trait method. +pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool) -> Result { + let trait_name = &trait_def.ident; + get_trait_methods(trait_def).try_fold(TokenStream::new(), |mut t, m| { + t.extend(function_for_method(trait_name, m, is_wasm_only)?); + Ok(t) + }) +} + +/// Generates the bare function implementation for the given method for the host and wasm side. +fn function_for_method( + trait_name: &Ident, + method: &TraitItemMethod, + is_wasm_only: bool, +) -> Result { + let std_impl = function_std_impl(trait_name, method, is_wasm_only)?; + let no_std_impl = function_no_std_impl(method)?; + + Ok( + quote! { + #std_impl + + #no_std_impl + } + ) +} + +/// Generates the bare function implementation for `cfg(not(feature = "std"))`. +fn function_no_std_impl(method: &TraitItemMethod) -> Result { + let function_name = &method.sig.ident; + let host_function_name = create_exchangeable_host_function_ident(&method.sig.ident); + let args = get_function_arguments(&method.sig); + let arg_names = get_function_argument_names(&method.sig); + let return_value = &method.sig.output; + let attrs = &method.attrs; + + Ok( + quote! { + #[cfg(not(feature = "std"))] + #( #attrs )* + pub fn #function_name( #( #args, )* ) #return_value { + // Call the host function + #host_function_name.get()( #( #arg_names, )* ) + } + } + ) +} + +/// Generates the bare function implementation for `cfg(feature = "std")`. +fn function_std_impl( + trait_name: &Ident, + method: &TraitItemMethod, + is_wasm_only: bool, +) -> Result { + let function_name = &method.sig.ident; + let crate_ = generate_crate_access(); + let args = get_function_arguments(&method.sig).map(FnArg::Typed).chain( + // Add the function context as last parameter when this is a wasm only interface. + iter::from_fn(|| + if is_wasm_only { + Some( + parse_quote!( + mut __function_context__: &mut dyn #crate_::wasm_interface::FunctionContext + ) + ) + } else { + None + } + ).take(1), + ); + let return_value = &method.sig.output; + let attrs = &method.attrs; + // Don't make the function public accessible when this is a wasm only interface. + let vis = if is_wasm_only { quote!() } else { quote!(pub) }; + let call_to_trait = generate_call_to_trait(trait_name, method, is_wasm_only); + + Ok( + quote_spanned! { method.span() => + #[cfg(feature = "std")] + #( #attrs )* + #vis fn #function_name( #( #args, )* ) #return_value { + #call_to_trait + } + } + ) +} + +/// Generate the call to the interface trait. +fn generate_call_to_trait( + trait_name: &Ident, + method: &TraitItemMethod, + is_wasm_only: bool, +) -> TokenStream { + let crate_ = generate_crate_access(); + let method_name = &method.sig.ident; + let expect_msg = format!( + "`{}` called outside of an Externalities-provided environment.", + method_name, + ); + let arg_names = get_function_argument_names(&method.sig); + + if takes_self_argument(&method.sig) { + let instance = if is_wasm_only { + Ident::new("__function_context__", Span::call_site()) + } else { + Ident::new("__externalities__", Span::call_site()) + }; + + let impl_ = quote!( #trait_name::#method_name(&mut #instance, #( #arg_names, )*) ); + + if is_wasm_only { + quote_spanned! { method.span() => #impl_ } + } else { + quote_spanned! { method.span() => + #crate_::with_externalities(|mut #instance| #impl_).expect(#expect_msg) + } + } + } else { + // The name of the trait the interface trait is implemented for + let impl_trait_name = if is_wasm_only { + quote!( #crate_::wasm_interface::FunctionContext ) + } else { + quote!( #crate_::Externalities ) + }; + + quote_spanned! { method.span() => + <&mut dyn #impl_trait_name as #trait_name>::#method_name( + #( #arg_names, )* + ) + } + } +} + +/// Returns if the given `Signature` takes a `self` argument. +fn takes_self_argument(sig: &Signature) -> bool { + match sig.inputs.first() { + Some(FnArg::Receiver(_)) => true, + _ => false, + } +} diff --git a/core/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs b/core/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs new file mode 100644 index 0000000000000..f710e9b60cbf4 --- /dev/null +++ b/core/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs @@ -0,0 +1,415 @@ +// Copyright 2019 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 . + +//! Generates the extern host functions and the implementation for these host functions. +//! +//! The extern host functions will be called by the bare function interface from the Wasm side. +//! The implementation of these host functions will be called on the host side from the Wasm +//! executor. These implementations call the bare function interface. + +use crate::utils::{ + generate_crate_access, create_host_function_ident, get_function_argument_names, + get_function_argument_types_without_ref, get_function_argument_types_ref_and_mut, + get_function_argument_names_and_types_without_ref, get_trait_methods, get_function_arguments, + get_function_argument_types, create_exchangeable_host_function_ident, +}; + +use syn::{ + ItemTrait, TraitItemMethod, Result, ReturnType, Ident, TraitItem, Pat, Error, Signature, + spanned::Spanned, +}; + +use proc_macro2::{TokenStream, Span}; + +use quote::{quote, ToTokens}; + +use inflector::Inflector; + +use std::iter::{Iterator, self}; + +/// Generate the extern host functions for wasm and the `HostFunctions` struct that provides the +/// implementations for the host functions on the host. +pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool) -> Result { + let trait_name = &trait_def.ident; + let extern_host_function_impls = get_trait_methods(trait_def) + .try_fold(TokenStream::new(), |mut t, m| { + t.extend(generate_extern_host_function(m, trait_name)?); + Ok::<_, Error>(t) + })?; + let exchangeable_host_functions = get_trait_methods(trait_def) + .try_fold(TokenStream::new(), |mut t, m| { + t.extend(generate_exchangeable_host_function(m)?); + Ok::<_, Error>(t) + })?; + let host_functions_struct = generate_host_functions_struct(trait_def, is_wasm_only)?; + + Ok( + quote! { + /// The implementations of the extern host functions. This special implementation module + /// is required to change the extern host functions signature to + /// `unsafe fn name(args) -> ret` to make the function implementations exchangeable. + #[cfg(not(feature = "std"))] + mod extern_host_function_impls { + use super::*; + + #extern_host_function_impls + } + + #exchangeable_host_functions + + #host_functions_struct + } + ) +} + +/// Generate the extern host function for the given method. +fn generate_extern_host_function(method: &TraitItemMethod, trait_name: &Ident) -> Result { + let crate_ = generate_crate_access(); + let args = get_function_arguments(&method.sig); + let arg_types = get_function_argument_types_without_ref(&method.sig); + let arg_types2 = get_function_argument_types_without_ref(&method.sig); + let arg_names = get_function_argument_names(&method.sig); + let arg_names2 = get_function_argument_names(&method.sig); + let arg_names3 = get_function_argument_names(&method.sig); + let function = &method.sig.ident; + let ext_function = create_host_function_ident(&method.sig.ident, trait_name); + let doc_string = format!( + " Default extern host function implementation for [`super::{}`].", + method.sig.ident, + ); + let return_value = &method.sig.output; + + let ffi_return_value = match method.sig.output { + ReturnType::Default => quote!(), + ReturnType::Type(_, ref ty) => quote! { + -> <#ty as #crate_::RIType>::FFIType + }, + }; + + let convert_return_value = match return_value { + ReturnType::Default => quote!(), + ReturnType::Type(_, ref ty) => quote! { + <#ty as #crate_::wasm::FromFFIValue>::from_ffi_value(result) + } + }; + + Ok( + quote! { + #[doc = #doc_string] + pub fn #function ( #( #args ),* ) #return_value { + extern "C" { + /// The extern function. + pub fn #ext_function ( + #( #arg_names: <#arg_types as #crate_::RIType>::FFIType ),* + ) #ffi_return_value; + } + + // Generate all wrapped ffi values. + #( + let #arg_names2 = <#arg_types2 as #crate_::wasm::IntoFFIValue>::into_ffi_value( + &#arg_names2, + ); + )* + + let result = unsafe { #ext_function( #( #arg_names3.get() ),* ) }; + + #convert_return_value + } + } + ) +} + +/// Generate the host exchangeable function for the given method. +fn generate_exchangeable_host_function(method: &TraitItemMethod) -> Result { + let crate_ = generate_crate_access(); + let arg_types = get_function_argument_types(&method.sig); + let function = &method.sig.ident; + let exchangeable_function = create_exchangeable_host_function_ident(&method.sig.ident); + let doc_string = format!(" Exchangeable host function used by [`{}`].", method.sig.ident); + let output = &method.sig.output; + + Ok( + quote! { + #[cfg(not(feature = "std"))] + #[allow(non_upper_case_globals)] + #[doc = #doc_string] + pub static #exchangeable_function : #crate_::wasm::ExchangeableFunction< + fn ( #( #arg_types ),* ) #output + > = #crate_::wasm::ExchangeableFunction::new(extern_host_function_impls::#function); + } + ) +} + +/// Generate the `HostFunctions` struct that implements `wasm-interface::HostFunctions` to provide +/// implementations for the extern host functions. +fn generate_host_functions_struct(trait_def: &ItemTrait, is_wasm_only: bool) -> Result { + let crate_ = generate_crate_access(); + let host_functions = trait_def + .items + .iter() + .filter_map(|i| match i { + TraitItem::Method(ref method) => Some(method), + _ => None, + }) + .map(|m| generate_host_function_implementation(&trait_def.ident, m, is_wasm_only)) + .collect::>>()?; + + Ok( + quote! { + /// Provides implementations for the extern host functions. + #[cfg(feature = "std")] + pub struct HostFunctions; + + #[cfg(feature = "std")] + impl #crate_::wasm_interface::HostFunctions for HostFunctions { + fn host_functions() -> Vec<&'static dyn #crate_::wasm_interface::Function> { + vec![ #( #host_functions ),* ] + } + } + } + ) +} + +/// Generates the host function struct that implements `wasm_interface::Function` and returns a static +/// reference to this struct. +/// +/// When calling from wasm into the host, we will call the `execute` function that calls the native +/// implementation of the function. +fn generate_host_function_implementation( + trait_name: &Ident, + method: &TraitItemMethod, + is_wasm_only: bool, +) -> Result { + let name = create_host_function_ident(&method.sig.ident, trait_name).to_string(); + let struct_name = Ident::new(&name.to_pascal_case(), Span::call_site()); + let crate_ = generate_crate_access(); + let signature = generate_wasm_interface_signature_for_host_function(&method.sig)?; + let wasm_to_ffi_values = generate_wasm_to_ffi_values( + &method.sig, + trait_name, + ).collect::>>()?; + let ffi_to_host_values = generate_ffi_to_host_value(&method.sig).collect::>>()?; + let host_function_call = generate_host_function_call(&method.sig, is_wasm_only); + let into_preallocated_ffi_value = generate_into_preallocated_ffi_value(&method.sig)?; + let convert_return_value = generate_return_value_into_wasm_value(&method.sig); + + Ok( + quote! { + { + struct #struct_name; + + #[allow(unused)] + impl #crate_::wasm_interface::Function for #struct_name { + fn name(&self) -> &str { + #name + } + + fn signature(&self) -> #crate_::wasm_interface::Signature { + #signature + } + + fn execute( + &self, + __function_context__: &mut dyn #crate_::wasm_interface::FunctionContext, + args: &mut dyn Iterator, + ) -> std::result::Result, String> { + #( #wasm_to_ffi_values )* + #( #ffi_to_host_values )* + #host_function_call + #into_preallocated_ffi_value + #convert_return_value + } + } + + &#struct_name as &dyn #crate_::wasm_interface::Function + } + } + ) +} + +/// Generate the `wasm_interface::Signature` for the given host function `sig`. +fn generate_wasm_interface_signature_for_host_function(sig: &Signature) -> Result { + let crate_ = generate_crate_access(); + let return_value = match &sig.output { + ReturnType::Type(_, ty) => + quote! { + Some( <<#ty as #crate_::RIType>::FFIType as #crate_::wasm_interface::IntoValue>::VALUE_TYPE ) + }, + ReturnType::Default => quote!( None ), + }; + let arg_types = get_function_argument_types_without_ref(sig) + .map(|ty| quote! { + <<#ty as #crate_::RIType>::FFIType as #crate_::wasm_interface::IntoValue>::VALUE_TYPE + }); + + Ok( + quote! { + #crate_::wasm_interface::Signature { + args: std::borrow::Cow::Borrowed(&[ #( #arg_types ),* ][..]), + return_value: #return_value, + } + } + ) +} + +/// Generate the code that converts the wasm values given to `HostFunctions::execute` into the FFI +/// values. +fn generate_wasm_to_ffi_values<'a>( + sig: &'a Signature, + trait_name: &'a Ident, +) -> impl Iterator> + 'a { + let crate_ = generate_crate_access(); + let function_name = &sig.ident; + let error_message = format!( + "Number of arguments given to `{}` does not match the expected number of arguments!", + function_name, + ); + + get_function_argument_names_and_types_without_ref(sig) + .map(move |(name, ty)| { + let try_from_error = format!( + "Could not instantiate `{}` from wasm value while executing `{}` from interface `{}`!", + name.to_token_stream(), + function_name, + trait_name, + ); + + let var_name = generate_ffi_value_var_name(&name)?; + + Ok(quote! { + let val = args.next().ok_or_else(|| #error_message)?; + let #var_name = < + <#ty as #crate_::RIType>::FFIType as #crate_::wasm_interface::TryFromValue + >::try_from_value(val).ok_or_else(|| #try_from_error)?; + }) + }) +} + +/// Generate the code to convert the ffi values on the host to the host values using `FromFFIValue`. +fn generate_ffi_to_host_value<'a>( + sig: &'a Signature, +) -> impl Iterator> + 'a { + let mut_access = get_function_argument_types_ref_and_mut(sig); + let crate_ = generate_crate_access(); + + get_function_argument_names_and_types_without_ref(sig) + .zip(mut_access.map(|v| v.and_then(|m| m.1))) + .map(move |((name, ty), mut_access)| { + let ffi_value_var_name = generate_ffi_value_var_name(&name)?; + + Ok( + quote! { + let #mut_access #name = <#ty as #crate_::host::FromFFIValue>::from_ffi_value( + __function_context__, + #ffi_value_var_name, + )?; + } + ) + }) +} + +/// Generate the code to call the host function and the ident that stores the result. +fn generate_host_function_call(sig: &Signature, is_wasm_only: bool) -> TokenStream { + let host_function_name = &sig.ident; + let result_var_name = generate_host_function_result_var_name(&sig.ident); + let ref_and_mut = get_function_argument_types_ref_and_mut(sig).map(|ram| + ram.map(|(vr, vm)| quote!(#vr #vm)) + ); + let names = get_function_argument_names(sig); + + let var_access = names.zip(ref_and_mut) + .map(|(n, ref_and_mut)| { + quote!( #ref_and_mut #n ) + }) + // If this is a wasm only interface, we add the function context as last parameter. + .chain( + iter::from_fn(|| if is_wasm_only { Some(quote!(__function_context__)) } else { None }) + .take(1) + ); + + quote! { + let #result_var_name = #host_function_name ( #( #var_access ),* ); + } +} + +/// Generate the variable name that stores the result of the host function. +fn generate_host_function_result_var_name(name: &Ident) -> Ident { + Ident::new(&format!("{}_result", name), Span::call_site()) +} + +/// Generate the variable name that stores the FFI value. +fn generate_ffi_value_var_name(pat: &Pat) -> Result { + match pat { + Pat::Ident(pat_ident) => { + if let Some(by_ref) = pat_ident.by_ref { + Err(Error::new(by_ref.span(), "`ref` not supported!")) + } else if let Some(sub_pattern) = &pat_ident.subpat { + Err(Error::new(sub_pattern.0.span(), "Not supported!")) + } else { + Ok(Ident::new(&format!("{}_ffi_value", pat_ident.ident), Span::call_site())) + } + } + _ => Err(Error::new(pat.span(), "Not supported as variable name!")) + } +} + +/// Generate code that copies data from the host back to preallocated wasm memory. +/// +/// Any argument that is given as `&mut` is interpreted as preallocated memory and it is expected +/// that the type implements `IntoPreAllocatedFFIValue`. +fn generate_into_preallocated_ffi_value(sig: &Signature) -> Result { + let crate_ = generate_crate_access(); + let ref_and_mut = get_function_argument_types_ref_and_mut(sig).map(|ram| + ram.and_then(|(vr, vm)| vm.map(|v| (vr, v))) + ); + let names_and_types = get_function_argument_names_and_types_without_ref(sig); + + ref_and_mut.zip(names_and_types) + .filter_map(|(ram, (name, ty))| ram.map(|_| (name, ty))) + .map(|(name, ty)| { + let ffi_var_name = generate_ffi_value_var_name(&name)?; + + Ok( + quote! { + <#ty as #crate_::host::IntoPreallocatedFFIValue>::into_preallocated_ffi_value( + #name, + __function_context__, + #ffi_var_name, + )?; + } + ) + }) + .collect() +} + +/// Generate the code that converts the return value into the appropriate wasm value. +fn generate_return_value_into_wasm_value(sig: &Signature) -> TokenStream { + let crate_ = generate_crate_access(); + + match &sig.output { + ReturnType::Default => quote!( Ok(None) ), + ReturnType::Type(_, ty) => { + let result_var_name = generate_host_function_result_var_name(&sig.ident); + + quote! { + <#ty as #crate_::host::IntoFFIValue>::into_ffi_value( + #result_var_name, + __function_context__, + ).map(#crate_::wasm_interface::IntoValue::into_value).map(Some) + } + } + } +} diff --git a/core/runtime-interface/proc-macro/src/runtime_interface/mod.rs b/core/runtime-interface/proc-macro/src/runtime_interface/mod.rs new file mode 100644 index 0000000000000..142fff646ed18 --- /dev/null +++ b/core/runtime-interface/proc-macro/src/runtime_interface/mod.rs @@ -0,0 +1,65 @@ +// Copyright 2019 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 crate::utils::generate_runtime_interface_include; + +use proc_macro2::{Span, TokenStream}; + +use syn::{Ident, ItemTrait, Result}; + +use inflector::Inflector; + +use quote::quote; + +mod bare_function_interface; +mod host_function_interface; +mod trait_decl_impl; + +/// Custom keywords supported by the `runtime_interface` attribute. +pub mod keywords { + // Custom keyword `wasm_only` that can be given as attribute to [`runtime_interface`]. + syn::custom_keyword!(wasm_only); +} + +/// Implementation of the `runtime_interface` attribute. +/// +/// It expects the trait definition the attribute was put above and if this should be an wasm only +/// interface. +pub fn runtime_interface_impl(trait_def: ItemTrait, is_wasm_only: bool) -> Result { + let bare_functions = bare_function_interface::generate(&trait_def, is_wasm_only)?; + let crate_include = generate_runtime_interface_include(); + let mod_name = Ident::new(&trait_def.ident.to_string().to_snake_case(), Span::call_site()); + let trait_decl_impl = trait_decl_impl::process(&trait_def, is_wasm_only)?; + let host_functions = host_function_interface::generate(&trait_def, is_wasm_only)?; + let vis = trait_def.vis; + let attrs = &trait_def.attrs; + + let res = quote! { + #( #attrs )* + #vis mod #mod_name { + use super::*; + #crate_include + + #bare_functions + + #trait_decl_impl + + #host_functions + } + }; + + Ok(res) +} diff --git a/core/runtime-interface/proc-macro/src/runtime_interface/trait_decl_impl.rs b/core/runtime-interface/proc-macro/src/runtime_interface/trait_decl_impl.rs new file mode 100644 index 0000000000000..0e5ae906ab680 --- /dev/null +++ b/core/runtime-interface/proc-macro/src/runtime_interface/trait_decl_impl.rs @@ -0,0 +1,146 @@ +// Copyright 2019 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 . + +//! Checks the trait declaration, makes the trait declaration module local, removes all method +//! default implementations and implements the trait for `&mut dyn Externalities`. + +use crate::utils::{generate_crate_access, get_function_argument_types_without_ref}; + +use syn::{ + ItemTrait, TraitItemMethod, Result, TraitItem, Error, fold::{self, Fold}, spanned::Spanned, + Visibility, Receiver, Type, Generics, +}; + +use proc_macro2::TokenStream; + +use quote::quote; + +/// Process the given trait definition, by checking that the definition is valid, fold it to the +/// essential definition and implement this essential definition for `dyn Externalities`. +pub fn process(trait_def: &ItemTrait, is_wasm_only: bool) -> Result { + let impl_trait = impl_trait_for_externalities(trait_def, is_wasm_only)?; + let essential_trait_def = ToEssentialTraitDef::convert(trait_def.clone())?; + + Ok( + quote! { + #impl_trait + + #essential_trait_def + } + ) +} + +/// Converts the given trait definition into the essential trait definition without method +/// default implementations and visibility set to inherited. +struct ToEssentialTraitDef { + /// All errors found while doing the conversion. + errors: Vec, +} + +impl ToEssentialTraitDef { + /// Convert the given trait definition to the essential trait definition. + fn convert(trait_def: ItemTrait) -> Result { + let mut folder = ToEssentialTraitDef { + errors: Vec::new(), + }; + + let res = folder.fold_item_trait(trait_def); + + if let Some(first_error) = folder.errors.pop() { + Err( + folder.errors.into_iter().fold(first_error, |mut o, n| { + o.combine(n); + o + }) + ) + } else { + Ok(res) + } + } + + fn push_error(&mut self, span: &S, msg: &str) { + self.errors.push(Error::new(span.span(), msg)); + } + + fn error_on_generic_parameters(&mut self, generics: &Generics) { + if let Some(param) = generics.params.first() { + self.push_error(param, "Generic parameters not supported."); + } + } +} + +impl Fold for ToEssentialTraitDef { + fn fold_trait_item_method(&mut self, mut method: TraitItemMethod) -> TraitItemMethod { + if method.default.take().is_none() { + self.push_error(&method, "Methods need to have an implementation."); + } + + let arg_types = get_function_argument_types_without_ref(&method.sig); + arg_types.filter_map(|ty| + match *ty { + Type::ImplTrait(impl_trait) => Some(impl_trait), + _ => None + } + ).for_each(|invalid| self.push_error(&invalid, "`impl Trait` syntax not supported.")); + + self.error_on_generic_parameters(&method.sig.generics); + + fold::fold_trait_item_method(self, method) + } + + fn fold_item_trait(&mut self, mut trait_def: ItemTrait) -> ItemTrait { + self.error_on_generic_parameters(&trait_def.generics); + + trait_def.vis = Visibility::Inherited; + fold::fold_item_trait(self, trait_def) + } + + fn fold_receiver(&mut self, receiver: Receiver) -> Receiver { + if receiver.reference.is_none() { + self.push_error(&receiver, "Taking `Self` by value is not allowed."); + } + + fold::fold_receiver(self, receiver) + } +} + +/// Implements the given trait definition for `dyn Externalities`. +fn impl_trait_for_externalities(trait_def: &ItemTrait, is_wasm_only: bool) -> Result { + let trait_ = &trait_def.ident; + let crate_ = generate_crate_access(); + let methods = trait_def + .items + .iter() + .filter_map(|i| match i { + TraitItem::Method(ref method) => Some(method), + _ => None, + }); + + let impl_type = if is_wasm_only { + quote!( &mut dyn #crate_::wasm_interface::FunctionContext ) + } else { + quote!( &mut dyn #crate_::Externalities ) + }; + + Ok( + quote! { + #[cfg(feature = "std")] + impl #trait_ for #impl_type { + #( #methods )* + } + } + ) +} diff --git a/core/runtime-interface/proc-macro/src/utils.rs b/core/runtime-interface/proc-macro/src/utils.rs new file mode 100644 index 0000000000000..d868452dcec2a --- /dev/null +++ b/core/runtime-interface/proc-macro/src/utils.rs @@ -0,0 +1,162 @@ +// Copyright 2019 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 + +//! Util function used by this crate. + +use proc_macro2::{TokenStream, Span}; + +use syn::{ + Ident, Error, Signature, Pat, PatType, FnArg, Type, token, TraitItemMethod, ItemTrait, + TraitItem, parse_quote, spanned::Spanned, +}; + +use proc_macro_crate::crate_name; + +use std::env; + +use quote::quote; + +use inflector::Inflector; + +/// Generates the include for the runtime-interface crate. +pub fn generate_runtime_interface_include() -> TokenStream { + if env::var("CARGO_PKG_NAME").unwrap() == "substrate-runtime-interface" { + TokenStream::new() + } else { + match crate_name("substrate-runtime-interface") { + Ok(crate_name) => { + let crate_name = Ident::new(&crate_name, Span::call_site()); + quote!( + #[doc(hidden)] + extern crate #crate_name as proc_macro_runtime_interface; + ) + }, + Err(e) => { + let err = Error::new(Span::call_site(), &e).to_compile_error(); + quote!( #err ) + } + } + } +} + +/// Generates the access to the `substrate-runtime-interface` crate. +pub fn generate_crate_access() -> TokenStream { + if env::var("CARGO_PKG_NAME").unwrap() == "substrate-runtime-interface" { + quote!( substrate_runtime_interface ) + } else { + quote!( proc_macro_runtime_interface ) + } +} + +/// Create the exchangeable host function identifier for the given function name. +pub fn create_exchangeable_host_function_ident(name: &Ident) -> Ident { + Ident::new(&format!("host_{}", name), Span::call_site()) +} + +/// Create the host function identifier for the given function name. +pub fn create_host_function_ident(name: &Ident, trait_name: &Ident) -> Ident { + Ident::new( + &format!( + "ext_{}_{}_version_1", + trait_name.to_string().to_snake_case(), + name, + ), + Span::call_site(), + ) +} + +/// Returns the function arguments of the given `Signature`, minus any `self` arguments. +pub fn get_function_arguments<'a>(sig: &'a Signature) -> impl Iterator + 'a { + sig.inputs + .iter() + .filter_map(|a| match a { + FnArg::Receiver(_) => None, + FnArg::Typed(pat_type) => Some(pat_type), + }) + .enumerate() + .map(|(i, arg)| { + let mut res = arg.clone(); + if let Pat::Wild(wild) = &*arg.pat { + let ident = Ident::new( + &format!("__runtime_interface_generated_{}_", i), + wild.span(), + ); + + res.pat = Box::new(parse_quote!( #ident )) + } + + res + }) +} + +/// Returns the function argument names of the given `Signature`, minus any `self`. +pub fn get_function_argument_names<'a>(sig: &'a Signature) -> impl Iterator> + 'a { + get_function_arguments(sig).map(|pt| pt.pat) +} + +/// Returns the function argument types of the given `Signature`, minus any `Self` type. +pub fn get_function_argument_types<'a>(sig: &'a Signature) -> impl Iterator> + 'a { + get_function_arguments(sig).map(|pt| pt.ty) +} + +/// Returns the function argument types, minus any `Self` type. If any of the arguments +/// is a reference, the underlying type without the ref is returned. +pub fn get_function_argument_types_without_ref<'a>( + sig: &'a Signature, +) -> impl Iterator> + 'a { + get_function_arguments(sig) + .map(|pt| pt.ty) + .map(|ty| match *ty { + Type::Reference(type_ref) => type_ref.elem, + _ => ty, + }) +} + +/// Returns the function argument names and types, minus any `self`. If any of the arguments +/// is a reference, the underlying type without the ref is returned. +pub fn get_function_argument_names_and_types_without_ref<'a>( + sig: &'a Signature, +) -> impl Iterator, Box)> + 'a { + get_function_arguments(sig) + .map(|pt| match *pt.ty { + Type::Reference(type_ref) => (pt.pat, type_ref.elem), + _ => (pt.pat, pt.ty), + }) +} + +/// Returns the `&`/`&mut` for all function argument types, minus the `self` arg. If a function +/// argument is not a reference, `None` is returned. +pub fn get_function_argument_types_ref_and_mut<'a>( + sig: &'a Signature, +) -> impl Iterator)>> + 'a { + get_function_arguments(sig) + .map(|pt| pt.ty) + .map(|ty| match *ty { + Type::Reference(type_ref) => Some((type_ref.and_token, type_ref.mutability)), + _ => None, + }) +} + +/// Returns an iterator over all trait methods for the given trait definition. +pub fn get_trait_methods<'a>(trait_def: &'a ItemTrait) -> impl Iterator { + trait_def + .items + .iter() + .filter_map(|i| match i { + TraitItem::Method(ref method) => Some(method), + _ => None, + }) +} diff --git a/core/runtime-interface/proc-macro/tests/ui.rs b/core/runtime-interface/proc-macro/tests/ui.rs new file mode 100644 index 0000000000000..5b14ee81e8e8a --- /dev/null +++ b/core/runtime-interface/proc-macro/tests/ui.rs @@ -0,0 +1,27 @@ +// Copyright 2019 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. + env::set_var("BUILD_DUMMY_WASM_BINARY", "1"); + + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); +} diff --git a/core/runtime-interface/proc-macro/tests/ui/no_generic_parameters.rs b/core/runtime-interface/proc-macro/tests/ui/no_generic_parameters.rs new file mode 100644 index 0000000000000..489fe5d9b4f91 --- /dev/null +++ b/core/runtime-interface/proc-macro/tests/ui/no_generic_parameters.rs @@ -0,0 +1,8 @@ +use runtime_interface::runtime_interface; + +#[runtime_interface] +trait Test { + fn test() {} +} + +fn main() {} diff --git a/core/runtime-interface/proc-macro/tests/ui/no_generic_parameters.stderr b/core/runtime-interface/proc-macro/tests/ui/no_generic_parameters.stderr new file mode 100644 index 0000000000000..c3e46655e5be7 --- /dev/null +++ b/core/runtime-interface/proc-macro/tests/ui/no_generic_parameters.stderr @@ -0,0 +1,11 @@ +error: Generic parameters not supported. + --> $DIR/no_generic_parameters.rs:5:10 + | +5 | fn test() {} + | ^ + +error: Generic parameters not supported. + --> $DIR/no_generic_parameters.rs:4:12 + | +4 | trait Test { + | ^ diff --git a/core/runtime-interface/proc-macro/tests/ui/no_method_implementation.rs b/core/runtime-interface/proc-macro/tests/ui/no_method_implementation.rs new file mode 100644 index 0000000000000..5291942420fd5 --- /dev/null +++ b/core/runtime-interface/proc-macro/tests/ui/no_method_implementation.rs @@ -0,0 +1,8 @@ +use runtime_interface::runtime_interface; + +#[runtime_interface] +trait Test { + fn test(); +} + +fn main() {} diff --git a/core/runtime-interface/proc-macro/tests/ui/no_method_implementation.stderr b/core/runtime-interface/proc-macro/tests/ui/no_method_implementation.stderr new file mode 100644 index 0000000000000..31b2d39762340 --- /dev/null +++ b/core/runtime-interface/proc-macro/tests/ui/no_method_implementation.stderr @@ -0,0 +1,5 @@ +error: Methods need to have an implementation. + --> $DIR/no_method_implementation.rs:5:2 + | +5 | fn test(); + | ^^ diff --git a/core/runtime-interface/proc-macro/tests/ui/pass_by_enum_with_struct.rs b/core/runtime-interface/proc-macro/tests/ui/pass_by_enum_with_struct.rs new file mode 100644 index 0000000000000..a729e0a99adca --- /dev/null +++ b/core/runtime-interface/proc-macro/tests/ui/pass_by_enum_with_struct.rs @@ -0,0 +1,6 @@ +use runtime_interface::pass_by::PassByEnum; + +#[derive(PassByEnum)] +struct Test; + +fn main() {} diff --git a/core/runtime-interface/proc-macro/tests/ui/pass_by_enum_with_struct.stderr b/core/runtime-interface/proc-macro/tests/ui/pass_by_enum_with_struct.stderr new file mode 100644 index 0000000000000..6502a36fc18b7 --- /dev/null +++ b/core/runtime-interface/proc-macro/tests/ui/pass_by_enum_with_struct.stderr @@ -0,0 +1,5 @@ +error: `PassByEnum` only supports enums as input type. + --> $DIR/pass_by_enum_with_struct.rs:3:10 + | +3 | #[derive(PassByEnum)] + | ^^^^^^^^^^ diff --git a/core/runtime-interface/proc-macro/tests/ui/pass_by_enum_with_value_variant.rs b/core/runtime-interface/proc-macro/tests/ui/pass_by_enum_with_value_variant.rs new file mode 100644 index 0000000000000..d2558e797770b --- /dev/null +++ b/core/runtime-interface/proc-macro/tests/ui/pass_by_enum_with_value_variant.rs @@ -0,0 +1,8 @@ +use runtime_interface::pass_by::PassByEnum; + +#[derive(PassByEnum)] +enum Test { + Var0(u32), +} + +fn main() {} diff --git a/core/runtime-interface/proc-macro/tests/ui/pass_by_enum_with_value_variant.stderr b/core/runtime-interface/proc-macro/tests/ui/pass_by_enum_with_value_variant.stderr new file mode 100644 index 0000000000000..1f03436d4e007 --- /dev/null +++ b/core/runtime-interface/proc-macro/tests/ui/pass_by_enum_with_value_variant.stderr @@ -0,0 +1,5 @@ +error: `PassByEnum` only supports unit variants. + --> $DIR/pass_by_enum_with_value_variant.rs:3:10 + | +3 | #[derive(PassByEnum)] + | ^^^^^^^^^^ diff --git a/core/runtime-interface/proc-macro/tests/ui/pass_by_inner_with_two_fields.rs b/core/runtime-interface/proc-macro/tests/ui/pass_by_inner_with_two_fields.rs new file mode 100644 index 0000000000000..eab79eae1910c --- /dev/null +++ b/core/runtime-interface/proc-macro/tests/ui/pass_by_inner_with_two_fields.rs @@ -0,0 +1,9 @@ +use runtime_interface::pass_by::PassByInner; + +#[derive(PassByInner)] +struct Test { + data: u32, + data2: u32, +} + +fn main() {} diff --git a/core/runtime-interface/proc-macro/tests/ui/pass_by_inner_with_two_fields.stderr b/core/runtime-interface/proc-macro/tests/ui/pass_by_inner_with_two_fields.stderr new file mode 100644 index 0000000000000..7f576a69f0e50 --- /dev/null +++ b/core/runtime-interface/proc-macro/tests/ui/pass_by_inner_with_two_fields.stderr @@ -0,0 +1,5 @@ +error: Only newtype/one field structs are supported by `PassByInner`! + --> $DIR/pass_by_inner_with_two_fields.rs:3:10 + | +3 | #[derive(PassByInner)] + | ^^^^^^^^^^^ diff --git a/core/runtime-interface/proc-macro/tests/ui/take_self_by_value.rs b/core/runtime-interface/proc-macro/tests/ui/take_self_by_value.rs new file mode 100644 index 0000000000000..f01c2de21ef61 --- /dev/null +++ b/core/runtime-interface/proc-macro/tests/ui/take_self_by_value.rs @@ -0,0 +1,8 @@ +use runtime_interface::runtime_interface; + +#[runtime_interface] +trait Test { + fn test(self) {} +} + +fn main() {} diff --git a/core/runtime-interface/proc-macro/tests/ui/take_self_by_value.stderr b/core/runtime-interface/proc-macro/tests/ui/take_self_by_value.stderr new file mode 100644 index 0000000000000..9b17a63a35cbc --- /dev/null +++ b/core/runtime-interface/proc-macro/tests/ui/take_self_by_value.stderr @@ -0,0 +1,5 @@ +error: Taking `Self` by value is not allowed. + --> $DIR/take_self_by_value.rs:5:10 + | +5 | fn test(self) {} + | ^^^^ diff --git a/core/runtime-interface/src/host.rs b/core/runtime-interface/src/host.rs new file mode 100644 index 0000000000000..313aba3d855d6 --- /dev/null +++ b/core/runtime-interface/src/host.rs @@ -0,0 +1,62 @@ +// Copyright 2019 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 . + +//! Traits required by the runtime interface from the host side. + +use crate::RIType; + +use wasm_interface::{FunctionContext, Result}; + +/// Something that can be converted into a ffi value. +pub trait IntoFFIValue: RIType { + /// Convert `self` into a ffi value. + fn into_ffi_value(self, context: &mut dyn FunctionContext) -> Result; +} + +/// Something that can be converted into a preallocated ffi value. +/// +/// Every type parameter that should be given as `&mut` into a runtime interface function, needs +/// to implement this trait. After executing the host implementation of the runtime interface +/// function, the value is copied into the preallocated wasm memory. +/// +/// This should only be used for types which have a fixed size, like slices. Other types like a vec +/// do not work with this interface, as we can not call into wasm to reallocate memory. So, this +/// trait should be implemented carefully. +pub trait IntoPreallocatedFFIValue: RIType { + /// As `Self` can be an unsized type, it needs to be represented by a sized type at the host. + /// This `SelfInstance` is the sized type. + type SelfInstance; + + /// Convert `self_instance` into the given preallocated ffi value. + fn into_preallocated_ffi_value( + self_instance: Self::SelfInstance, + context: &mut dyn FunctionContext, + allocated: Self::FFIType, + ) -> Result<()>; +} + +/// Something that can be created from a ffi value. +pub trait FromFFIValue: RIType { + /// As `Self` can be an unsized type, it needs to be represented by a sized type at the host. + /// This `SelfInstance` is the sized type. + type SelfInstance; + + /// Create `SelfInstance` from the given + fn from_ffi_value( + context: &mut dyn FunctionContext, + arg: Self::FFIType, + ) -> Result; +} diff --git a/core/runtime-interface/src/impls.rs b/core/runtime-interface/src/impls.rs new file mode 100644 index 0000000000000..7a6adc90c96ea --- /dev/null +++ b/core/runtime-interface/src/impls.rs @@ -0,0 +1,491 @@ +// Copyright 2019 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 . + +//! Provides implementations for the runtime interface traits. + +use crate::{RIType, Pointer, pass_by::{PassBy, Codec, Inner, PassByInner}}; +#[cfg(feature = "std")] +use crate::host::*; +#[cfg(not(feature = "std"))] +use crate::wasm::*; + +#[cfg(all(not(feature = "std"), not(feature = "disable_target_static_assertions")))] +use static_assertions::assert_eq_size; + +#[cfg(feature = "std")] +use wasm_interface::{FunctionContext, Result}; + +use codec::{Encode, Decode}; + +use rstd::{any::TypeId, mem, vec::Vec}; + +#[cfg(feature = "std")] +use rstd::borrow::Cow; + +#[cfg(not(feature = "std"))] +use rstd::{slice, boxed::Box}; + +// Make sure that our assumptions for storing a pointer + its size in `u64` is valid. +#[cfg(all(not(feature = "std"), not(feature = "disable_target_static_assertions")))] +assert_eq_size!(usize, u32); +#[cfg(all(not(feature = "std"), not(feature = "disable_target_static_assertions")))] +assert_eq_size!(*const u8, u32); + +/// Converts a pointer and length into an `u64`. +pub fn pointer_and_len_to_u64(ptr: u32, len: u32) -> u64 { + // The static assertions from above are changed into a runtime check. + #[cfg(all(feature = "std", not(feature = "disable_target_static_assertions")))] + assert_eq!(4, rstd::mem::size_of::()); + + (u64::from(len) << 32) | u64::from(ptr) +} + +/// Splits an `u64` into the pointer and length. +pub fn pointer_and_len_from_u64(val: u64) -> (u32, u32) { + // The static assertions from above are changed into a runtime check. + #[cfg(all(feature = "std", not(feature = "disable_target_static_assertions")))] + assert_eq!(4, rstd::mem::size_of::()); + + let ptr = (val & (!0u32 as u64)) as u32; + let len = (val >> 32) as u32; + + (ptr, len) +} + +/// Implement the traits for the given primitive traits. +macro_rules! impl_traits_for_primitives { + ( + $( + $rty:ty, $fty:ty, + )* + ) => { + $( + /// The type is passed directly. + impl RIType for $rty { + type FFIType = $fty; + } + + #[cfg(not(feature = "std"))] + impl IntoFFIValue for $rty { + type Owned = (); + + fn into_ffi_value(&self) -> WrappedFFIValue<$fty> { + (*self as $fty).into() + } + } + + #[cfg(not(feature = "std"))] + impl FromFFIValue for $rty { + fn from_ffi_value(arg: $fty) -> $rty { + arg as $rty + } + } + + #[cfg(feature = "std")] + impl FromFFIValue for $rty { + type SelfInstance = $rty; + + fn from_ffi_value(_: &mut dyn FunctionContext, arg: $fty) -> Result<$rty> { + Ok(arg as $rty) + } + } + + #[cfg(feature = "std")] + impl IntoFFIValue for $rty { + fn into_ffi_value(self, _: &mut dyn FunctionContext) -> Result<$fty> { + Ok(self as $fty) + } + } + )* + } +} + +impl_traits_for_primitives! { + u8, u8, + u16, u16, + u32, u32, + u64, u64, + i8, i8, + i16, i16, + i32, i32, + i64, i64, +} + +/// `bool` is passed as `u8`. +/// +/// - `1`: true +/// - `0`: false +impl RIType for bool { + type FFIType = u8; +} + +#[cfg(not(feature = "std"))] +impl IntoFFIValue for bool { + type Owned = (); + + fn into_ffi_value(&self) -> WrappedFFIValue { + if *self { 1 } else { 0 }.into() + } +} + +#[cfg(not(feature = "std"))] +impl FromFFIValue for bool { + fn from_ffi_value(arg: u8) -> bool { + arg == 1 + } +} + +#[cfg(feature = "std")] +impl FromFFIValue for bool { + type SelfInstance = bool; + + fn from_ffi_value(_: &mut dyn FunctionContext, arg: u8) -> Result { + Ok(arg == 1) + } +} + +#[cfg(feature = "std")] +impl IntoFFIValue for bool { + fn into_ffi_value(self, _: &mut dyn FunctionContext) -> Result { + Ok(if self { 1 } else { 0 }) + } +} + +/// The type is passed as `u64`. +/// +/// The `u64` value is build by `length 32bit << 32 | pointer 32bit` +/// +/// If `T == u8` the length and the pointer are taken directly from the `Self`. +/// Otherwise `Self` is encoded and the length and the pointer are taken from the encoded vector. +impl RIType for Vec { + type FFIType = u64; +} + +#[cfg(feature = "std")] +impl IntoFFIValue for Vec { + fn into_ffi_value(self, context: &mut dyn FunctionContext) -> Result { + let vec: Cow<'_, [u8]> = if TypeId::of::() == TypeId::of::() { + unsafe { Cow::Borrowed(mem::transmute(&self[..])) } + } else { + Cow::Owned(self.encode()) + }; + + let ptr = context.allocate_memory(vec.as_ref().len() as u32)?; + context.write_memory(ptr, &vec)?; + + Ok(pointer_and_len_to_u64(ptr.into(), vec.len() as u32)) + } +} + +#[cfg(feature = "std")] +impl FromFFIValue for Vec { + type SelfInstance = Vec; + + fn from_ffi_value(context: &mut dyn FunctionContext, arg: u64) -> Result> { + <[T] as FromFFIValue>::from_ffi_value(context, arg) + } +} + +#[cfg(not(feature = "std"))] +impl IntoFFIValue for Vec { + type Owned = Vec; + + fn into_ffi_value(&self) -> WrappedFFIValue> { + self[..].into_ffi_value() + } +} + +#[cfg(not(feature = "std"))] +impl FromFFIValue for Vec { + fn from_ffi_value(arg: u64) -> Vec { + let (ptr, len) = pointer_and_len_from_u64(arg); + let len = len as usize; + + if TypeId::of::() == TypeId::of::() { + unsafe { mem::transmute(Vec::from_raw_parts(ptr as *mut u8, len, len)) } + } else { + let slice = unsafe { slice::from_raw_parts(ptr as *const u8, len) }; + Self::decode(&mut &slice[..]).expect("Host to wasm values are encoded correctly; qed") + } + } +} + +/// The type is passed as `u64`. +/// +/// The `u64` value is build by `length 32bit << 32 | pointer 32bit` +/// +/// If `T == u8` the length and the pointer are taken directly from the `Self`. +/// Otherwise `Self` is encoded and the length and the pointer are taken from the encoded vector. +impl RIType for [T] { + type FFIType = u64; +} + +#[cfg(feature = "std")] +impl FromFFIValue for [T] { + type SelfInstance = Vec; + + fn from_ffi_value(context: &mut dyn FunctionContext, arg: u64) -> Result> { + let (ptr, len) = pointer_and_len_from_u64(arg); + + let vec = context.read_memory(Pointer::new(ptr), len)?; + + if TypeId::of::() == TypeId::of::() { + Ok(unsafe { mem::transmute(vec) }) + } else { + Ok(Vec::::decode(&mut &vec[..]).expect("Wasm to host values are encoded correctly; qed")) + } + } +} + +#[cfg(feature = "std")] +impl IntoPreallocatedFFIValue for [u8] { + type SelfInstance = Vec; + + fn into_preallocated_ffi_value( + self_instance: Self::SelfInstance, + context: &mut dyn FunctionContext, + allocated: u64, + ) -> Result<()> { + let (ptr, len) = pointer_and_len_from_u64(allocated); + + if (len as usize) < self_instance.len() { + Err( + format!( + "Preallocated buffer is not big enough (given {} vs needed {})!", + len, + self_instance.len() + ) + ) + } else { + context.write_memory(Pointer::new(ptr), &self_instance) + } + } +} + +#[cfg(not(feature = "std"))] +impl IntoFFIValue for [T] { + type Owned = Vec; + + fn into_ffi_value(&self) -> WrappedFFIValue> { + if TypeId::of::() == TypeId::of::() { + let slice = unsafe { mem::transmute::<&[T], &[u8]>(self) }; + pointer_and_len_to_u64(slice.as_ptr() as u32, slice.len() as u32).into() + } else { + let data = self.encode(); + let ffi_value = pointer_and_len_to_u64(data.as_ptr() as u32, data.len() as u32); + (ffi_value, data).into() + } + } +} + +/// Implement the traits for the `[u8; N]` arrays, where `N` is the input to this macro. +macro_rules! impl_traits_for_arrays { + ( + $( + $n:expr + ),* + $(,)? + ) => { + $( + /// The type is passed as `u32`. + /// + /// The `u32` is the pointer to the array. + impl RIType for [u8; $n] { + type FFIType = u32; + } + + #[cfg(not(feature = "std"))] + impl IntoFFIValue for [u8; $n] { + type Owned = (); + + fn into_ffi_value(&self) -> WrappedFFIValue { + (self.as_ptr() as u32).into() + } + } + + #[cfg(not(feature = "std"))] + impl FromFFIValue for [u8; $n] { + fn from_ffi_value(arg: u32) -> [u8; $n] { + let mut res = unsafe { mem::MaybeUninit::<[u8; $n]>::zeroed().assume_init() }; + res.copy_from_slice(unsafe { slice::from_raw_parts(arg as *const u8, $n) }); + + // Make sure we free the pointer. + let _ = unsafe { Box::from_raw(arg as *mut u8) }; + + res + } + } + + #[cfg(feature = "std")] + impl FromFFIValue for [u8; $n] { + type SelfInstance = [u8; $n]; + + fn from_ffi_value(context: &mut dyn FunctionContext, arg: u32) -> Result<[u8; $n]> { + let data = context.read_memory(Pointer::new(arg), $n)?; + let mut res = unsafe { mem::MaybeUninit::<[u8; $n]>::zeroed().assume_init() }; + res.copy_from_slice(&data); + Ok(res) + } + } + + #[cfg(feature = "std")] + impl IntoFFIValue for [u8; $n] { + fn into_ffi_value(self, context: &mut dyn FunctionContext) -> Result { + let addr = context.allocate_memory($n)?; + context.write_memory(addr, &self)?; + Ok(addr.into()) + } + } + + #[cfg(feature = "std")] + impl IntoPreallocatedFFIValue for [u8; $n] { + type SelfInstance = [u8; $n]; + + fn into_preallocated_ffi_value( + self_instance: Self::SelfInstance, + context: &mut dyn FunctionContext, + allocated: u32, + ) -> Result<()> { + context.write_memory(Pointer::new(allocated), &self_instance) + } + } + )* + } +} + +impl_traits_for_arrays! { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, +} + +impl PassBy for rstd::result::Result { + type PassBy = Codec; +} + +impl PassBy for Option { + type PassBy = Codec; +} + +/// Implement `PassBy` with `Inner` for the given fixed sized hash types. +macro_rules! for_primitive_types { + { $( $hash:ident $n:expr ),* $(,)? } => { + $( + impl PassBy for primitive_types::$hash { + type PassBy = Inner; + } + + impl PassByInner for primitive_types::$hash { + type Inner = [u8; $n]; + + fn inner(&self) -> &Self::Inner { + &self.0 + } + + fn into_inner(self) -> Self::Inner { + self.0 + } + + fn from_inner(inner: Self::Inner) -> Self { + Self(inner) + } + } + )* + } +} + +for_primitive_types! { + H160 20, + H256 32, + H512 64, +} + +/// The type is passed as `u64`. +/// +/// The `u64` value is build by `length 32bit << 32 | pointer 32bit` +/// +/// The length and the pointer are taken directly from the `Self`. +impl RIType for str { + type FFIType = u64; +} + +#[cfg(feature = "std")] +impl FromFFIValue for str { + type SelfInstance = String; + + fn from_ffi_value(context: &mut dyn FunctionContext, arg: u64) -> Result { + let (ptr, len) = pointer_and_len_from_u64(arg); + + let vec = context.read_memory(Pointer::new(ptr), len)?; + + // The data is valid utf8, as it is stored as `&str` in wasm. + String::from_utf8(vec).map_err(|_| "Invalid utf8 data provided".into()) + } +} + +#[cfg(not(feature = "std"))] +impl IntoFFIValue for str { + type Owned = (); + + fn into_ffi_value(&self) -> WrappedFFIValue { + let bytes = self.as_bytes(); + pointer_and_len_to_u64(bytes.as_ptr() as u32, bytes.len() as u32).into() + } +} + +#[cfg(feature = "std")] +impl RIType for Pointer { + type FFIType = u32; +} + +/// The type is passed as `u32`. +#[cfg(not(feature = "std"))] +impl RIType for Pointer { + type FFIType = u32; +} + +#[cfg(not(feature = "std"))] +impl IntoFFIValue for Pointer { + type Owned = (); + + fn into_ffi_value(&self) -> WrappedFFIValue { + (*self as u32).into() + } +} + +#[cfg(not(feature = "std"))] +impl FromFFIValue for Pointer { + fn from_ffi_value(arg: u32) -> Self { + arg as _ + } +} + +#[cfg(feature = "std")] +impl FromFFIValue for Pointer { + type SelfInstance = Self; + + fn from_ffi_value(_: &mut dyn FunctionContext, arg: u32) -> Result { + Ok(Pointer::new(arg)) + } +} + +#[cfg(feature = "std")] +impl IntoFFIValue for Pointer { + fn into_ffi_value(self, _: &mut dyn FunctionContext) -> Result { + Ok(self.into()) + } +} diff --git a/core/runtime-interface/src/lib.rs b/core/runtime-interface/src/lib.rs new file mode 100644 index 0000000000000..fb70d252a6772 --- /dev/null +++ b/core/runtime-interface/src/lib.rs @@ -0,0 +1,212 @@ +// Copyright 2019 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 . + +//! Substrate runtime interface +//! +//! This crate provides types, traits and macros around runtime interfaces. A runtime interface is +//! a fixed interface between a Substrate runtime and a Substrate node. For a native runtime the +//! interface maps to a direct function call of the implementation. For a wasm runtime the interface +//! maps to an external function call. These external functions are exported by the wasm executor +//! and they map to the same implementation as the native calls. +//! +//! # Using a type in a runtime interface +//! +//! Any type that should be used in a runtime interface as argument or return value needs to +//! implement [`RIType`]. The associated type `FFIType` is the type that is used in the FFI +//! function to represent the actual type. For example `[T]` is represented by an `u64`. The slice +//! pointer and the length will be mapped to an `u64` value. For more information, see the +//! implementation of [`RIType`] for [`T`]. The FFI function definition is used when calling from +//! the wasm runtime into the node. +//! +//! Traits are used to convert from a type to the corresponding [`RIType::FFIType`]. +//! Depending on where and how a type should be used in a function signature, a combination of the +//! following traits need to be implemented: +//! +//! 1. Pass as function argument: [`wasm::IntoFFIValue`] and [`host::FromFFIValue`] +//! 2. As function return value: [`wasm::FromFFIValue`] and [`host::IntoFFIValue`] +//! 3. Pass as mutable function argument: [`host::IntoPreallocatedFFIValue`] +//! +//! The traits are implemented for most of the common types like `[T]`, `Vec`, arrays and +//! primitive types. +//! +//! For custom types, we provide the [`PassBy`](pass_by::PassBy) trait and strategies that define +//! how a type is passed between the wasm runtime and the node. Each strategy also provides a derive +//! macro to simplify the implementation. +//! +//! # Performance +//! +//! To not waste any more performance when calling into the node, not all types are SCALE encoded +//! when being passed as arguments between the wasm runtime and the node. For most types that +//! are raw bytes like `Vec`, `[u8]` or `[u8; N]` we pass them directly, without SCALE encoding +//! them in front of. The implementation of [`RIType`] each type provides more information on how +//! the data is passed. +//! +//! # Declaring a runtime interface +//! +//! Declaring a runtime interface is similar to declaring a trait in Rust: +//! +//! ``` +//! #[substrate_runtime_interface::runtime_interface] +//! trait RuntimeInterface { +//! fn some_function(value: &[u8]) -> bool { +//! value.iter().all(|v| *v > 125) +//! } +//! } +//! ``` +//! +//! For more information on declaring a runtime interface, see +//! [`#[runtime_interface]`](attr.runtime_interface.html). + +#![cfg_attr(not(feature = "std"), no_std)] + +#[doc(hidden)] +#[cfg(feature = "std")] +pub use wasm_interface; + +#[doc(hidden)] +pub use rstd; + +pub use substrate_runtime_interface_proc_macro::runtime_interface; + +#[doc(hidden)] +#[cfg(feature = "std")] +pub use externalities::{ + set_and_run_with_externalities, with_externalities, Externalities, ExternalitiesExt, ExtensionStore, +}; + +#[doc(hidden)] +pub use codec; + +pub(crate) mod impls; +#[cfg(feature = "std")] +pub mod host; +#[cfg(not(feature = "std"))] +pub mod wasm; +pub mod pass_by; + +/// Something that can be used by the runtime interface as type to communicate between wasm and the +/// host. +/// +/// Every type that should be used in a runtime interface function signature needs to implement +/// this trait. +pub trait RIType { + /// The ffi type that is used to represent `Self`. + #[cfg(feature = "std")] + type FFIType: wasm_interface::IntoValue + wasm_interface::TryFromValue; + #[cfg(not(feature = "std"))] + type FFIType; +} + +/// A pointer that can be used in a runtime interface function signature. +#[cfg(not(feature = "std"))] +pub type Pointer = *mut T; + +/// A pointer that can be used in a runtime interface function signature. +#[cfg(feature = "std")] +pub type Pointer = wasm_interface::Pointer; + +#[cfg(test)] +mod tests { + use super::*; + use test_wasm::{WASM_BINARY, test_api::HostFunctions}; + use wasm_interface::HostFunctions as HostFunctionsT; + + type TestExternalities = state_machine::TestExternalities; + + fn call_wasm_method(method: &str) -> TestExternalities { + let mut ext = TestExternalities::default(); + let mut ext_ext = ext.ext(); + + executor::call_in_wasm::< + _, + ( + HF, + runtime_io::SubstrateHostFunctions, + executor::deprecated_host_interface::SubstrateExternals + ) + >( + method, + &[], + executor::WasmExecutionMethod::Interpreted, + &mut ext_ext, + &WASM_BINARY[..], + 8, + ).expect(&format!("Executes `{}`", method)); + + ext + } + + #[test] + fn test_return_data() { + call_wasm_method::("test_return_data"); + } + + #[test] + fn test_return_option_data() { + call_wasm_method::("test_return_option_data"); + } + + #[test] + fn test_set_storage() { + let mut ext = call_wasm_method::("test_set_storage"); + + let expected = "world"; + assert_eq!(expected.as_bytes(), &ext.ext().storage("hello".as_bytes()).unwrap()[..]); + } + + #[test] + fn test_return_value_into_mutable_reference() { + call_wasm_method::("test_return_value_into_mutable_reference"); + } + + #[test] + fn test_get_and_return_array() { + call_wasm_method::("test_get_and_return_array"); + } + + #[test] + fn test_array_as_mutable_reference() { + call_wasm_method::("test_array_as_mutable_reference"); + } + + #[test] + fn test_return_input_public_key() { + call_wasm_method::("test_return_input_public_key"); + } + + #[test] + #[should_panic( + expected = "Other(\"Instantiation: Export ext_test_api_return_input_version_1 not found\")" + )] + fn host_function_not_found() { + call_wasm_method::<()>("test_return_data"); + } + + #[test] + #[should_panic( + expected = + "FunctionExecution(\"ext_test_api_invalid_utf8_data_version_1\", \ + \"Invalid utf8 data provided\")" + )] + fn test_invalid_utf8_data_should_return_an_error() { + call_wasm_method::("test_invalid_utf8_data_should_return_an_error"); + } + + #[test] + fn test_overwrite_native_function_implementation() { + call_wasm_method::("test_overwrite_native_function_implementation"); + } +} diff --git a/core/runtime-interface/src/pass_by.rs b/core/runtime-interface/src/pass_by.rs new file mode 100644 index 0000000000000..46265237c0c47 --- /dev/null +++ b/core/runtime-interface/src/pass_by.rs @@ -0,0 +1,384 @@ +// Copyright 2019 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 . + +//! Provides the [`PassBy`](pass_by::PassBy) trait to simplify the implementation of the +//! runtime interface traits for custom types. +//! +//! [`Codec`](pass_by::Codec), [`Inner`](pass_by::Inner) and [`Enum`](pass_by::Enum) are the +//! provided strategy implementations. + +use crate::{RIType, impls::{pointer_and_len_from_u64, pointer_and_len_to_u64}}; + +#[cfg(feature = "std")] +use crate::host::*; +#[cfg(not(feature = "std"))] +use crate::wasm::*; + +#[cfg(feature = "std")] +use wasm_interface::{FunctionContext, Pointer, Result}; + +use rstd::{marker::PhantomData, convert::TryFrom}; + +#[cfg(not(feature = "std"))] +use rstd::{slice, vec::Vec}; + +pub use substrate_runtime_interface_proc_macro::{PassByCodec, PassByInner, PassByEnum}; + +/// Something that should be passed between wasm and the host using the given strategy. +/// +/// See [`Codec`], [`Inner`] or [`Enum`] for more information about the provided strategies. +pub trait PassBy: Sized { + /// The strategy that should be used to pass the type. + type PassBy: PassByImpl; +} + +/// Something that provides a strategy for passing a type between wasm and the host. +/// +/// This trait exposes the same functionality as [`crate::host::IntoFFIValue`] and +/// [`crate::host::FromFFIValue`] to delegate the implementation for a type to a different type. +/// +/// This trait is used for the host implementation. +#[cfg(feature = "std")] +pub trait PassByImpl: RIType { + /// Convert the given instance to the ffi value. + /// + /// For more information see: [`crate::host::IntoFFIValue::into_ffi_value`] + fn into_ffi_value( + instance: T, + context: &mut dyn FunctionContext, + ) -> Result; + + /// Create `T` from the given ffi value. + /// + /// For more information see: [`crate::host::FromFFIValue::from_ffi_value`] + fn from_ffi_value( + context: &mut dyn FunctionContext, + arg: Self::FFIType, + ) -> Result; +} + +/// Something that provides a strategy for passing a type between wasm and the host. +/// +/// This trait exposes the same functionality as [`crate::wasm::IntoFFIValue`] and +/// [`crate::wasm::FromFFIValue`] to delegate the implementation for a type to a different type. +/// +/// This trait is used for the wasm implementation. +#[cfg(not(feature = "std"))] +pub trait PassByImpl: RIType { + /// The owned rust type that is stored with the ffi value in [`crate::wasm::WrappedFFIValue`]. + type Owned; + + /// Convert the given `instance` into [`crate::wasm::WrappedFFIValue`]. + /// + /// For more information see: [`crate::wasm::IntoFFIValue::into_ffi_value`] + fn into_ffi_value(instance: &T) -> WrappedFFIValue; + + /// Create `T` from the given ffi value. + /// + /// For more information see: [`crate::wasm::FromFFIValue::from_ffi_value`] + fn from_ffi_value(arg: Self::FFIType) -> T; +} + +impl RIType for T { + type FFIType = ::FFIType; +} + +#[cfg(feature = "std")] +impl IntoFFIValue for T { + fn into_ffi_value( + self, + context: &mut dyn FunctionContext, + ) -> Result<::FFIType> { + T::PassBy::into_ffi_value(self, context) + } +} + +#[cfg(feature = "std")] +impl FromFFIValue for T { + type SelfInstance = Self; + + fn from_ffi_value( + context: &mut dyn FunctionContext, + arg: ::FFIType, + ) -> Result { + T::PassBy::from_ffi_value(context, arg) + } +} + +#[cfg(not(feature = "std"))] +impl IntoFFIValue for T { + type Owned = >::Owned; + + fn into_ffi_value(&self) -> WrappedFFIValue<::FFIType, Self::Owned> { + T::PassBy::into_ffi_value(self) + } +} + +#[cfg(not(feature = "std"))] +impl FromFFIValue for T { + fn from_ffi_value(arg: ::FFIType) -> Self { + T::PassBy::from_ffi_value(arg) + } +} + +/// The implementation of the pass by codec strategy. This strategy uses a SCALE encoded +/// representation of the type between wasm and the host. +/// +/// Use this type as associated type for [`PassBy`] to implement this strategy for a type. +/// +/// This type expects the type that wants to implement this strategy as generic parameter. +/// +/// [`PassByCodec`](derive.PassByCodec.html) is a derive macro to implement this strategy. +/// +/// # Example +/// ``` +/// # use substrate_runtime_interface::pass_by::{PassBy, Codec}; +/// #[derive(codec::Encode, codec::Decode)] +/// struct Test; +/// +/// impl PassBy for Test { +/// type PassBy = Codec; +/// } +/// ``` +pub struct Codec(PhantomData); + +#[cfg(feature = "std")] +impl PassByImpl for Codec { + fn into_ffi_value( + instance: T, + context: &mut dyn FunctionContext, + ) -> Result { + let vec = instance.encode(); + let ptr = context.allocate_memory(vec.len() as u32)?; + context.write_memory(ptr, &vec)?; + + Ok(pointer_and_len_to_u64(ptr.into(), vec.len() as u32)) + } + + fn from_ffi_value( + context: &mut dyn FunctionContext, + arg: Self::FFIType, + ) -> Result { + let (ptr, len) = pointer_and_len_from_u64(arg); + let vec = context.read_memory(Pointer::new(ptr), len)?; + T::decode(&mut &vec[..]) + .map_err(|e| format!("Could not decode value from wasm: {}", e.what())) + } +} + +#[cfg(not(feature = "std"))] +impl PassByImpl for Codec { + type Owned = Vec; + + fn into_ffi_value(instance: &T) -> WrappedFFIValue { + let data = instance.encode(); + let ffi_value = pointer_and_len_to_u64(data.as_ptr() as u32, data.len() as u32); + (ffi_value, data).into() + } + + fn from_ffi_value(arg: Self::FFIType) -> T { + let (ptr, len) = pointer_and_len_from_u64(arg); + let len = len as usize; + + let slice = unsafe { slice::from_raw_parts(ptr as *const u8, len) }; + T::decode(&mut &slice[..]).expect("Host to wasm values are encoded correctly; qed") + } +} + +/// The type is passed as `u64`. +/// +/// The `u64` value is build by `length 32bit << 32 | pointer 32bit` +/// +/// `Self` is encoded and the length and the pointer are taken from the encoded vector. +impl RIType for Codec { + type FFIType = u64; +} + +/// Trait that needs to be implemented by a type that should be passed between wasm and the host, +/// by using the inner type. See [`Inner`] for more information. +pub trait PassByInner: Sized { + /// The inner type that is wrapped by `Self`. + type Inner: RIType; + + /// Consumes `self` and returns the inner type. + fn into_inner(self) -> Self::Inner; + + /// Returns the reference to the inner type. + fn inner(&self) -> &Self::Inner; + + /// Construct `Self` from the given `inner`. + fn from_inner(inner: Self::Inner) -> Self; +} + +/// The implementation of the pass by inner type strategy. The type that uses this strategy will be +/// passed between wasm and the host by using the wrapped inner type. So, this strategy is only +/// usable by newtype structs. +/// +/// Use this type as associated type for [`PassBy`] to implement this strategy for a type. Besides +/// that the `PassByInner` trait need to be implemented as well. +/// +/// This type expects the type that wants to use this strategy as generic parameter `T` and the +/// inner type as generic parameter `I`. +/// +/// [`PassByInner`](derive.PassByInner.html) is a derive macro to implement this strategy. +/// +/// # Example +/// ``` +/// # use substrate_runtime_interface::pass_by::{PassBy, Inner, PassByInner}; +/// struct Test([u8; 32]); +/// +/// impl PassBy for Test { +/// type PassBy = Inner; +/// } +/// +/// impl PassByInner for Test { +/// type Inner = [u8; 32]; +/// +/// fn into_inner(self) -> [u8; 32] { +/// self.0 +/// } +/// fn inner(&self) -> &[u8; 32] { +/// &self.0 +/// } +/// fn from_inner(inner: [u8; 32]) -> Self { +/// Self(inner) +/// } +/// } +/// ``` +pub struct Inner, I: RIType>(PhantomData<(T, I)>); + +#[cfg(feature = "std")] +impl, I: RIType> PassByImpl for Inner + where I: IntoFFIValue + FromFFIValue +{ + fn into_ffi_value( + instance: T, + context: &mut dyn FunctionContext, + ) -> Result { + instance.into_inner().into_ffi_value(context) + } + + fn from_ffi_value( + context: &mut dyn FunctionContext, + arg: Self::FFIType, + ) -> Result { + I::from_ffi_value(context, arg).map(T::from_inner) + } +} + +#[cfg(not(feature = "std"))] +impl, I: RIType> PassByImpl for Inner + where I: IntoFFIValue + FromFFIValue +{ + type Owned = I::Owned; + + fn into_ffi_value(instance: &T) -> WrappedFFIValue { + instance.inner().into_ffi_value() + } + + fn from_ffi_value(arg: Self::FFIType) -> T { + T::from_inner(I::from_ffi_value(arg)) + } +} + +/// The type is passed as the inner type. +impl, I: RIType> RIType for Inner { + type FFIType = I::FFIType; +} + +/// The implementation of the pass by enum strategy. This strategy uses an `u8` internally to pass +/// the enum between wasm and the host. So, this strategy only supports enums with unit variants. +/// +/// Use this type as associated type for [`PassBy`] to implement this strategy for a type. +/// +/// This type expects the type that wants to implement this strategy as generic parameter. Besides +/// that the type needs to implement `TryFrom` and `From for u8`. +/// +/// [`PassByEnum`](derive.PassByEnum.html) is a derive macro to implement this strategy. +/// +/// # Example +/// ``` +/// # use substrate_runtime_interface::pass_by::{PassBy, Enum}; +/// #[derive(Clone, Copy)] +/// enum Test { +/// Test1, +/// Test2, +/// } +/// +/// impl From for u8 { +/// fn from(val: Test) -> u8 { +/// match val { +/// Test::Test1 => 0, +/// Test::Test2 => 1, +/// } +/// } +/// } +/// +/// impl std::convert::TryFrom for Test { +/// type Error = (); +/// +/// fn try_from(val: u8) -> Result { +/// match val { +/// 0 => Ok(Test::Test1), +/// 1 => Ok(Test::Test2), +/// _ => Err(()), +/// } +/// } +/// } +/// +/// impl PassBy for Test { +/// type PassBy = Enum; +/// } +/// ``` +pub struct Enum + TryFrom>(PhantomData); + +#[cfg(feature = "std")] +impl + TryFrom> PassByImpl for Enum { + fn into_ffi_value( + instance: T, + _: &mut dyn FunctionContext, + ) -> Result { + Ok(instance.into()) + } + + fn from_ffi_value( + _: &mut dyn FunctionContext, + arg: Self::FFIType, + ) -> Result { + T::try_from(arg).map_err(|_| format!("Invalid enum discriminant: {}", arg)) + } +} + +#[cfg(not(feature = "std"))] +impl + TryFrom> PassByImpl for Enum { + type Owned = (); + + fn into_ffi_value(instance: &T) -> WrappedFFIValue { + let value: u8 = (*instance).into(); + value.into() + } + + fn from_ffi_value(arg: Self::FFIType) -> T { + T::try_from(arg).expect("Host to wasm provides a valid enum discriminant; qed") + } +} + +/// The type is passed as `u8`. +/// +/// The value is corresponds to the discriminant of the variant. +impl + TryFrom> RIType for Enum { + type FFIType = u8; +} diff --git a/core/runtime-interface/src/wasm.rs b/core/runtime-interface/src/wasm.rs new file mode 100644 index 0000000000000..7ac890a3ca3ba --- /dev/null +++ b/core/runtime-interface/src/wasm.rs @@ -0,0 +1,142 @@ +// Copyright 2019 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 . + +//! Traits required by the runtime interface from the wasm side. + +use crate::RIType; + +use rstd::cell::Cell; + +/// Something that can be created from a ffi value. +/// +/// # Safety +/// +/// It is unsafe behavior to call `Something::into_ffi_value().get()` and take this as input for +/// `from_ffi_value`. Implementations are safe to assume that the `arg` given to `from_ffi_value` +/// is only generated by the corresponding `host::IntoFFIValue` implementation. +pub trait FromFFIValue: Sized + RIType { + /// Create `Self` from the given ffi value. + fn from_ffi_value(arg: Self::FFIType) -> Self; +} + +/// Something that can be converted into a ffi value. +pub trait IntoFFIValue: RIType { + /// The owned rust type that is stored with the ffi value in [`WrappedFFIValue`]. + /// + /// If no owned value is required, `()` can be used as a type. + type Owned; + + /// Convert `self` into a [`WrappedFFIValue`]. + fn into_ffi_value(&self) -> WrappedFFIValue; +} + +/// Represents a wrapped ffi value. +/// +/// It is either the ffi value itself or the ffi value plus some other owned value. By providing +/// support for storing another owned value besides the actual ffi value certain performance +/// optimizations can be applied. For example using the pointer to a `Vec`, while using the +/// pointer to a SCALE encoded `Vec` that is stored in this wrapper for any other `Vec`. +pub enum WrappedFFIValue { + Wrapped(T), + WrappedAndOwned(T, O), +} + +impl WrappedFFIValue { + /// Returns the wrapped ffi value. + pub fn get(&self) -> T { + match self { + Self::Wrapped(data) | Self::WrappedAndOwned(data, _) => *data, + } + } +} + +impl From for WrappedFFIValue { + fn from(val: T) -> Self { + WrappedFFIValue::Wrapped(val) + } +} + +impl From<(T, O)> for WrappedFFIValue { + fn from(val: (T, O)) -> Self { + WrappedFFIValue::WrappedAndOwned(val.0, val.1) + } +} + +/// The state of an exchangeable function. +#[derive(Clone, Copy)] +enum ExchangeableFunctionState { + /// Original function is present + Original, + /// The function has been replaced. + Replaced, +} + +/// A function which implementation can be exchanged. +/// +/// Internally this works by swapping function pointers. +pub struct ExchangeableFunction(Cell<(T, ExchangeableFunctionState)>); + +impl ExchangeableFunction { + /// Create a new instance of `ExchangeableFunction`. + pub const fn new(impl_: T) -> Self { + Self(Cell::new((impl_, ExchangeableFunctionState::Original))) + } +} + +impl ExchangeableFunction { + /// Replace the implementation with `new_impl`. + /// + /// # Panics + /// + /// Panics when trying to replace an already replaced implementation. + /// + /// # Returns + /// + /// Returns the original implementation wrapped in [`RestoreImplementation`]. + pub fn replace_implementation(&'static self, new_impl: T) -> RestoreImplementation { + if let ExchangeableFunctionState::Replaced = self.0.get().1 { + panic!("Trying to replace an already replaced implementation!") + } + + let old = self.0.replace((new_impl, ExchangeableFunctionState::Replaced)); + + RestoreImplementation(self, Some(old.0)) + } + + /// Restore the original implementation. + fn restore_orig_implementation(&self, orig: T) { + self.0.set((orig, ExchangeableFunctionState::Original)); + } + + /// Returns the internal function pointer. + pub fn get(&self) -> T { + self.0.get().0 + } +} + +// Wasm does not support threads, so this is safe; qed. +unsafe impl Sync for ExchangeableFunction {} + +/// Restores a function implementation on drop. +/// +/// Stores a static reference to the function object and the original implementation. +pub struct RestoreImplementation(&'static ExchangeableFunction, Option); + +impl Drop for RestoreImplementation { + fn drop(&mut self) { + self.0.restore_orig_implementation(self.1.take().expect("Value is only taken on drop; qed")); + } +} diff --git a/core/runtime-interface/test-wasm/Cargo.toml b/core/runtime-interface/test-wasm/Cargo.toml new file mode 100644 index 0000000000000..dd870164e98c2 --- /dev/null +++ b/core/runtime-interface/test-wasm/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "substrate-runtime-interface-test-wasm" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" +build = "build.rs" + +[dependencies] +runtime-interface = { package = "substrate-runtime-interface", path = "../", default-features = false } +rstd = { package = "sr-std", path = "../../sr-std", default-features = false } +runtime-io = { package = "sr-io", path = "../../sr-io", default-features = false } +primitives = { package = "substrate-primitives", path = "../../primitives", default-features = false } + +[build-dependencies] +wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.3", path = "../../utils/wasm-builder-runner" } + +[features] +default = [ "std" ] +std = [ "runtime-interface/std", "rstd/std", "primitives/std", "runtime-io/std" ] diff --git a/core/runtime-interface/test-wasm/build.rs b/core/runtime-interface/test-wasm/build.rs new file mode 100644 index 0000000000000..fd4749b34c45e --- /dev/null +++ b/core/runtime-interface/test-wasm/build.rs @@ -0,0 +1,30 @@ +// Copyright 2019 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 wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource}; + +fn main() { + build_current_project_with_rustflags( + "wasm_binary.rs", + WasmBuilderSource::CratesOrPath { + path: "../../utils/wasm-builder", + version: "1.0.6", + }, + // This instructs LLD to export __heap_base as a global variable, which is used by the + // external memory allocator. + "-Clink-arg=--export=__heap_base", + ); +} diff --git a/core/runtime-interface/test-wasm/src/lib.rs b/core/runtime-interface/test-wasm/src/lib.rs new file mode 100644 index 0000000000000..d61315c521b68 --- /dev/null +++ b/core/runtime-interface/test-wasm/src/lib.rs @@ -0,0 +1,194 @@ +// Copyright 2019 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 . + +//! Tests for the runtime interface traits and proc macros. + +#![cfg_attr(not(feature = "std"), no_std)] + +use runtime_interface::runtime_interface; + +#[cfg(not(feature = "std"))] +use rstd::{vec, vec::Vec, mem, convert::TryFrom}; + +use primitives::{sr25519::Public, wasm_export_functions}; + +// Inlucde the WASM binary +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +/// Used in the `test_array_as_mutable_reference` test. +const TEST_ARRAY: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + +#[runtime_interface] +pub trait TestApi { + /// Returns the input data as result. + fn return_input(data: Vec) -> Vec { + data + } + + /// Set the storage at key with value. + fn set_storage(&mut self, key: &[u8], data: &[u8]) { + self.place_storage(key.to_vec(), Some(data.to_vec())); + } + + /// Copy `hello` into the given mutable reference + fn return_value_into_mutable_reference(&self, data: &mut [u8]) { + let res = "hello"; + data[..res.as_bytes().len()].copy_from_slice(res.as_bytes()); + } + + /// Returns the input data wrapped in an `Option` as result. + fn return_option_input(data: Vec) -> Option> { + Some(data) + } + + /// Get an array as input and returns a subset of this array. + fn get_and_return_array(data: [u8; 34]) -> [u8; 16] { + let mut res = [0u8; 16]; + res.copy_from_slice(&data[..16]); + res + } + + /// Take and fill mutable array. + fn array_as_mutable_reference(data: &mut [u8; 16]) { + data.copy_from_slice(&TEST_ARRAY); + } + + /// Returns the given public key as result. + fn return_input_public_key(key: Public) -> Public { + key + } + + /// A function that is called with invalid utf8 data from the runtime. + /// + /// This also checks that we accept `_` (wild card) argument names. + fn invalid_utf8_data(_: &str) {} + + /// Overwrite the native implementation in wasm. The native implementation always returns + /// `false` and the replacement function will return always `true`. + fn overwrite_native_function_implementation() -> bool { + false + } +} + +/// Two random external functions from the old runtime interface. +/// This ensures that we still inherently export these functions from the host and that we are still +/// compatible with old wasm runtimes. +extern "C" { + pub fn ext_clear_storage(key_data: *const u8, key_len: u32); + pub fn ext_keccak_256(data: *const u8, len: u32, out: *mut u8); +} + +/// Make sure the old runtime interface needs to be imported. +#[no_mangle] +pub fn force_old_runtime_interface_import() { + unsafe { ext_clear_storage(rstd::ptr::null(), 0); } + unsafe { ext_keccak_256(rstd::ptr::null(), 0, rstd::ptr::null_mut()); } +} + +/// This function is not used, but we require it for the compiler to include `runtime-io`. +/// `runtime-io` is required for its panic and oom handler. +#[no_mangle] +pub fn import_runtime_io() { + runtime_io::misc::print_utf8(&[]); +} + +wasm_export_functions! { + fn test_return_data() { + let input = vec![1, 2, 3, 4, 5, 6]; + let res = test_api::return_input(input.clone()); + + assert_eq!(input, res); + } + + fn test_return_option_data() { + let input = vec![1, 2, 3, 4, 5, 6]; + let res = test_api::return_option_input(input.clone()); + + assert_eq!(Some(input), res); + } + + fn test_set_storage() { + let key = "hello"; + let value = "world"; + + test_api::set_storage(key.as_bytes(), value.as_bytes()); + } + + fn test_return_value_into_mutable_reference() { + let mut data = vec![1, 2, 3, 4, 5, 6]; + + test_api::return_value_into_mutable_reference(&mut data); + + let expected = "hello"; + assert_eq!(expected.as_bytes(), &data[..expected.len()]); + } + + fn test_get_and_return_array() { + let mut input = unsafe { mem::MaybeUninit::<[u8; 34]>::zeroed().assume_init() }; + input.copy_from_slice(&[ + 24, 3, 23, 20, 2, 16, 32, 1, 12, 26, 27, 8, 29, 31, 6, 5, 4, 19, 10, 28, 34, 21, 18, 33, 9, + 13, 22, 25, 15, 11, 30, 7, 14, 17, + ]); + + let res = test_api::get_and_return_array(input); + + assert_eq!(&res, &input[..16]); + } + + fn test_array_as_mutable_reference() { + let mut array = [0u8; 16]; + test_api::array_as_mutable_reference(&mut array); + + assert_eq!(array, TEST_ARRAY); + } + + fn test_return_input_public_key() { + let key = Public::try_from( + &[ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, + ][..], + ).unwrap(); + let ret_key = test_api::return_input_public_key(key.clone()); + + let key_data: &[u8] = key.as_ref(); + let ret_key_data: &[u8] = ret_key.as_ref(); + assert_eq!(key_data, ret_key_data); + } + + fn test_invalid_utf8_data_should_return_an_error() { + let data = vec![0, 159, 146, 150]; + // I'm an evil hacker, trying to hack! + let data_str = unsafe { rstd::str::from_utf8_unchecked(&data) }; + + test_api::invalid_utf8_data(data_str); + } + + fn test_overwrite_native_function_implementation() { + fn new_implementation() -> bool { + true + } + + // Check native implementation + assert!(!test_api::overwrite_native_function_implementation()); + + let _guard = test_api::host_overwrite_native_function_implementation + .replace_implementation(new_implementation); + + assert!(test_api::overwrite_native_function_implementation()); + } +} diff --git a/core/sr-api-macros/tests/trybuild.rs b/core/sr-api-macros/tests/trybuild.rs index 9baea83196e9b..5b14ee81e8e8a 100644 --- a/core/sr-api-macros/tests/trybuild.rs +++ b/core/sr-api-macros/tests/trybuild.rs @@ -1,3 +1,19 @@ +// Copyright 2019 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)] diff --git a/core/sr-io/Cargo.toml b/core/sr-io/Cargo.toml index 4d140d289dafe..4e6d0b652b427 100644 --- a/core/sr-io/Cargo.toml +++ b/core/sr-io/Cargo.toml @@ -2,20 +2,17 @@ name = "sr-io" version = "2.0.0" authors = ["Parity Technologies "] -build = "build.rs" edition = "2018" -[build-dependencies] -rustc_version = "0.2.3" - [dependencies] -codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false } hash-db = { version = "0.15.2", default-features = false } primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } rstd = { package = "sr-std", path = "../sr-std", default-features = false } libsecp256k1 = { version = "0.3.0", optional = true } tiny-keccak = { version = "1.5.0", optional = true } substrate-state-machine = { path = "../state-machine", optional = true } +runtime-interface = { package = "substrate-runtime-interface", path = "../runtime-interface", default-features = false } trie = { package = "substrate-trie", path = "../trie", optional = true } externalities = { package = "substrate-externalities", path = "../externalities", optional = true } log = { version = "0.4.8", optional = true } @@ -31,15 +28,15 @@ std = [ "substrate-state-machine", "libsecp256k1", "tiny-keccak", + "runtime-interface/std", "externalities", "log", ] -nightly = [] -strict = [] # These two features are used for `no_std` builds for the environments which already provides -# `#[panic_handler]` and `#[alloc_error_handler]`. +# `#[panic_handler]`, `#[alloc_error_handler]` and `#[global_allocator]`. # # For the regular wasm runtime builds those are not used. -no_panic_handler = [] -no_oom = [] +disable_panic_handler = [] +disable_oom = [] +disable_allocator = [] diff --git a/core/sr-io/build.rs b/core/sr-io/build.rs deleted file mode 100644 index 5b5d06b65a253..0000000000000 --- a/core/sr-io/build.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Set a nightly feature - -use rustc_version::{version, version_meta, Channel}; - -fn main() { - // Assert we haven't traveled back in time - assert!(version().unwrap().major >= 1); - - // Set cfg flags depending on release channel - if let Channel::Nightly = version_meta().unwrap().channel { - println!("cargo:rustc-cfg=feature=\"nightly\""); - } -} diff --git a/core/sr-io/src/lib.rs b/core/sr-io/src/lib.rs index fe5e50b3eda3e..896cde804fa3f 100644 --- a/core/sr-io/src/lib.rs +++ b/core/sr-io/src/lib.rs @@ -28,15 +28,34 @@ use rstd::vec::Vec; +#[cfg(feature = "std")] +use rstd::ops::Deref; + +#[cfg(feature = "std")] +use primitives::{ + crypto::Pair, traits::KeystoreExt, offchain::OffchainExt, hexdisplay::HexDisplay, + storage::ChildStorageKey, +}; + use primitives::{ - crypto::KeyTypeId, ed25519, sr25519, H256, + crypto::KeyTypeId, ed25519, sr25519, H256, LogLevel, offchain::{ Timestamp, HttpRequestId, HttpRequestStatus, HttpError, StorageKind, OpaqueNetworkState, }, - LogLevel, }; +#[cfg(feature = "std")] +use trie::{TrieConfiguration, trie_types::Layout}; + +use runtime_interface::{runtime_interface, Pointer}; + +use codec::{Encode, Decode}; + +#[cfg(feature = "std")] +use externalities::{ExternalitiesExt, Externalities}; + /// Error verifying ECDSA signature +#[derive(Encode, Decode)] pub enum EcdsaVerifyError { /// Incorrect value of R or S BadRS, @@ -46,352 +65,774 @@ pub enum EcdsaVerifyError { BadSignature, } -/// Converts a public trait definition into a private trait and set of public functions -/// that assume the trait is implemented for `()` for ease of calling. -macro_rules! export_api { - ( - $( #[$trait_attr:meta] )* - pub(crate) trait $trait_name:ident { - $( - $( #[$attr:meta] )* - fn $name:ident - ( $( $arg:ident : $arg_ty:ty ),* $(,)? ) - $( -> $ret:ty )? - $( where $( $w_name:path : $w_ty:path ),+ )?; - )* - } - ) => { - $( #[$trait_attr] )* - pub(crate) trait $trait_name { - $( - $( #[$attr] )* - fn $name ( $($arg : $arg_ty ),* ) $( -> $ret )? - $( where $( $w_name : $w_ty ),+ )?; - )* +/// Returns a `ChildStorageKey` if the given `storage_key` slice is a valid storage +/// key or panics otherwise. +/// +/// Panicking here is aligned with what the `without_std` environment would do +/// in the case of an invalid child storage key. +#[cfg(feature = "std")] +fn child_storage_key_or_panic(storage_key: &[u8]) -> ChildStorageKey { + match ChildStorageKey::from_slice(storage_key) { + Some(storage_key) => storage_key, + None => panic!("child storage key is invalid"), + } +} + +/// Interface for accessing the storage from within the runtime. +#[runtime_interface] +pub trait Storage { + /// Returns the data for `key` in the storage or `None` if the key can not be found. + fn get(&self, key: &[u8]) -> Option> { + self.storage(key).map(|s| s.to_vec()) + } + + /// Returns the data for `key` in the child storage or `None` if the key can not be found. + fn child_get(&self, child_storage_key: &[u8], key: &[u8]) -> Option> { + let storage_key = child_storage_key_or_panic(child_storage_key); + self.child_storage(storage_key, key).map(|s| s.to_vec()) + } + + /// Get `key` from storage, placing the value into `value_out` and return the number of + /// bytes that the entry in storage has beyond the offset or `None` if the storage entry + /// doesn't exist at all. + /// If `value_out` length is smaller than the returned length, only `value_out` length bytes + /// are copied into `value_out`. + fn read(&self, key: &[u8], value_out: &mut [u8], value_offset: u32) -> Option { + self.storage(key).map(|value| { + let value_offset = value_offset as usize; + let data = &value[value_offset.min(value.len())..]; + let written = std::cmp::min(data.len(), value_out.len()); + value_out[..written].copy_from_slice(&data[..written]); + value.len() as u32 + }) + } + + /// Get `key` from child storage, placing the value into `value_out` and return the number + /// of bytes that the entry in storage has beyond the offset or `None` if the storage entry + /// doesn't exist at all. + /// If `value_out` length is smaller than the returned length, only `value_out` length bytes + /// are copied into `value_out`. + fn child_read( + &self, + child_storage_key: &[u8], + key: &[u8], + value_out: &mut [u8], + value_offset: u32, + ) -> Option { + let storage_key = child_storage_key_or_panic(child_storage_key); + self.child_storage(storage_key, key) + .map(|value| { + let value_offset = value_offset as usize; + let data = &value[value_offset.min(value.len())..]; + let written = std::cmp::min(data.len(), value_out.len()); + value_out[..written].copy_from_slice(&data[..written]); + value.len() as u32 + }) + } + + /// Set `key` to `value` in the storage. + fn set(&mut self, key: &[u8], value: &[u8]) { + self.set_storage(key.to_vec(), value.to_vec()); + } + + /// Set `key` to `value` in the child storage denoted by `child_storage_key`. + fn child_set(&mut self, child_storage_key: &[u8], key: &[u8], value: &[u8]) { + let storage_key = child_storage_key_or_panic(child_storage_key); + self.set_child_storage(storage_key, key.to_vec(), value.to_vec()); + } + + /// Clear the storage of the given `key` and its value. + fn clear(&mut self, key: &[u8]) { + self.clear_storage(key) + } + + /// Clear the given child storage of the given `key` and its value. + fn child_clear(&mut self, child_storage_key: &[u8], key: &[u8]) { + let storage_key = child_storage_key_or_panic(child_storage_key); + self.clear_child_storage(storage_key, key); + } + + /// Clear an entire child storage. + fn child_storage_kill(&mut self, child_storage_key: &[u8]) { + let storage_key = child_storage_key_or_panic(child_storage_key); + self.kill_child_storage(storage_key); + } + + /// Check whether the given `key` exists in storage. + fn exists(&self, key: &[u8]) -> bool { + self.exists_storage(key) + } + + /// Check whether the given `key` exists in storage. + fn child_exists(&self, child_storage_key: &[u8], key: &[u8]) -> bool { + let storage_key = child_storage_key_or_panic(child_storage_key); + self.exists_child_storage(storage_key, key) + } + + /// Clear the storage of each key-value pair where the key starts with the given `prefix`. + fn clear_prefix(&mut self, prefix: &[u8]) { + Externalities::clear_prefix(*self, prefix) + } + + /// Clear the child storage of each key-value pair where the key starts with the given `prefix`. + fn child_clear_prefix(&mut self, child_storage_key: &[u8], prefix: &[u8]) { + let storage_key = child_storage_key_or_panic(child_storage_key); + self.clear_child_prefix(storage_key, prefix); + } + + /// "Commit" all existing operations and compute the resulting storage root. + fn root(&mut self) -> H256 { + self.storage_root() + } + + /// "Commit" all existing operations and compute the resulting child storage root. + fn child_root(&mut self, child_storage_key: &[u8]) -> Vec { + let storage_key = child_storage_key_or_panic(child_storage_key); + self.child_storage_root(storage_key) + } + + /// "Commit" all existing operations and get the resulting storage change root. + fn changes_root(&mut self, parent_hash: [u8; 32]) -> Option { + self.storage_changes_root(parent_hash.into()).ok().and_then(|h| h) + } + + /// A trie root formed from the iterated items. + fn blake2_256_trie_root(input: Vec<(Vec, Vec)>) -> H256 { + Layout::::trie_root(input) + } + + /// A trie root formed from the enumerated items. + fn blake2_256_ordered_trie_root(input: Vec>) -> H256 { + Layout::::ordered_trie_root(input) + } +} + +/// Interface that provides miscellaneous functions for communicating between the runtime and the node. +#[runtime_interface] +pub trait Misc { + /// The current relay chain identifier. + fn chain_id(&self) -> u64 { + externalities::Externalities::chain_id(*self) + } + + /// Print a number. + fn print_num(val: u64) { + log::debug!(target: "runtime", "{}", val); + } + + /// Print any valid `utf8` buffer. + fn print_utf8(utf8: &[u8]) { + if let Ok(data) = std::str::from_utf8(utf8) { + log::debug!(target: "runtime", "{}", data) } + } - $( - $( #[$attr] )* - pub fn $name ( $($arg : $arg_ty ),* ) $( -> $ret )? - $( where $( $w_name : $w_ty ),+ )? - { - #[allow(deprecated)] - <()>:: $name ( $( $arg ),* ) - } - )* + /// Print any `u8` slice as hex. + fn print_hex(data: &[u8]) { + log::debug!(target: "runtime", "{}", HexDisplay::from(&data)); } } -export_api! { - pub(crate) trait StorageApi { - /// Get `key` from storage and return a `Vec`, empty if there's a problem. - fn storage(key: &[u8]) -> Option>; +/// Interfaces for working with crypto related types from within the runtime. +#[runtime_interface] +pub trait Crypto { + /// Returns all `ed25519` public keys for the given key id from the keystore. + fn ed25519_public_keys(&mut self, id: KeyTypeId) -> Vec { + self.extension::() + .expect("No `keystore` associated for the current context!") + .read() + .ed25519_public_keys(id) + } - /// Get `key` from child storage and return a `Vec`, empty if there's a problem. - fn child_storage(storage_key: &[u8], key: &[u8]) -> Option>; + /// Generate an `ed22519` key for the given key type using an optional `seed` and + /// store it in the keystore. + /// + /// The `seed` needs to be a valid utf8. + /// + /// Returns the public key. + fn ed25519_generate(&mut self, id: KeyTypeId, seed: Option>) -> ed25519::Public { + let seed = seed.as_ref().map(|s| std::str::from_utf8(&s).expect("Seed is valid utf8!")); + self.extension::() + .expect("No `keystore` associated for the current context!") + .write() + .ed25519_generate_new(id, seed) + .expect("`ed25519_generate` failed") + } - /// Get `key` from storage, placing the value into `value_out` and return the number of - /// bytes that the entry in storage has beyond the offset or `None` if the storage entry - /// doesn't exist at all. - /// If `value_out` length is smaller than the returned length, only `value_out` length bytes - /// are copied into `value_out`. - fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option; + /// Sign the given `msg` with the `ed25519` key that corresponds to the given public key and + /// key type in the keystore. + /// + /// Returns the signature. + fn ed25519_sign( + &mut self, + id: KeyTypeId, + pub_key: &ed25519::Public, + msg: &[u8], + ) -> Option { + self.extension::() + .expect("No `keystore` associated for the current context!") + .read() + .ed25519_key_pair(id, &pub_key) + .map(|k| k.sign(msg)) + } - /// Get `key` from child storage, placing the value into `value_out` and return the number - /// of bytes that the entry in storage has beyond the offset or `None` if the storage entry - /// doesn't exist at all. - /// If `value_out` length is smaller than the returned length, only `value_out` length bytes - /// are copied into `value_out`. - fn read_child_storage(storage_key: &[u8], key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option; + /// Verify an `ed25519` signature. + /// + /// Returns `true` when the verification in successful. + fn ed25519_verify( + &self, + sig: &ed25519::Signature, + msg: &[u8], + pub_key: &ed25519::Public, + ) -> bool { + ed25519::Pair::verify(sig, msg, pub_key) + } - /// Set the storage of some particular key to Some value. - fn set_storage(key: &[u8], value: &[u8]); + /// Returns all `sr25519` public keys for the given key id from the keystore. + fn sr25519_public_keys(&mut self, id: KeyTypeId) -> Vec { + self.extension::() + .expect("No `keystore` associated for the current context!") + .read() + .sr25519_public_keys(id) + } - /// Set the child storage of some particular key to Some value. - fn set_child_storage(storage_key: &[u8], key: &[u8], value: &[u8]); + /// Generate an `sr22519` key for the given key type using an optional seed and + /// store it in the keystore. + /// + /// The `seed` needs to be a valid utf8. + /// + /// Returns the public key. + fn sr25519_generate(&mut self, id: KeyTypeId, seed: Option>) -> sr25519::Public { + let seed = seed.as_ref().map(|s| std::str::from_utf8(&s).expect("Seed is valid utf8!")); + self.extension::() + .expect("No `keystore` associated for the current context!") + .write() + .sr25519_generate_new(id, seed) + .expect("`sr25519_generate` failed") + } - /// Clear the storage of a key. - fn clear_storage(key: &[u8]); + /// Sign the given `msg` with the `sr25519` key that corresponds to the given public key and + /// key type in the keystore. + /// + /// Returns the signature. + fn sr25519_sign( + &mut self, + id: KeyTypeId, + pub_key: &sr25519::Public, + msg: &[u8], + ) -> Option { + self.extension::() + .expect("No `keystore` associated for the current context!") + .read() + .sr25519_key_pair(id, &pub_key) + .map(|k| k.sign(msg)) + } - /// Clear the storage of a key. - fn clear_child_storage(storage_key: &[u8], key: &[u8]); + /// Verify an `sr25519` signature. + /// + /// Returns `true` when the verification in successful. + fn sr25519_verify(sig: &sr25519::Signature, msg: &[u8], pubkey: &sr25519::Public) -> bool { + sr25519::Pair::verify(sig, msg, pubkey) + } - /// Clear an entire child storage. - fn kill_child_storage(storage_key: &[u8]); + /// Verify and recover a SECP256k1 ECDSA signature. + /// - `sig` is passed in RSV format. V should be either 0/1 or 27/28. + /// Returns `Err` if the signature is bad, otherwise the 64-byte pubkey + /// (doesn't include the 0x04 prefix). + fn secp256k1_ecdsa_recover( + sig: &[u8; 65], + msg: &[u8; 32], + ) -> Result<[u8; 64], EcdsaVerifyError> { + let rs = secp256k1::Signature::parse_slice(&sig[0..64]) + .map_err(|_| EcdsaVerifyError::BadRS)?; + let v = secp256k1::RecoveryId::parse(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8) + .map_err(|_| EcdsaVerifyError::BadV)?; + let pubkey = secp256k1::recover(&secp256k1::Message::parse(msg), &rs, &v) + .map_err(|_| EcdsaVerifyError::BadSignature)?; + let mut res = [0u8; 64]; + res.copy_from_slice(&pubkey.serialize()[1..65]); + Ok(res) + } - /// Check whether a given `key` exists in storage. - fn exists_storage(key: &[u8]) -> bool; + /// Verify and recover a SECP256k1 ECDSA signature. + /// - `sig` is passed in RSV format. V should be either 0/1 or 27/28. + /// - returns `Err` if the signature is bad, otherwise the 33-byte compressed pubkey. + fn secp256k1_ecdsa_recover_compressed( + sig: &[u8; 65], + msg: &[u8; 32], + ) -> Result<[u8; 33], EcdsaVerifyError> { + let rs = secp256k1::Signature::parse_slice(&sig[0..64]) + .map_err(|_| EcdsaVerifyError::BadRS)?; + let v = secp256k1::RecoveryId::parse(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8) + .map_err(|_| EcdsaVerifyError::BadV)?; + let pubkey = secp256k1::recover(&secp256k1::Message::parse(msg), &rs, &v) + .map_err(|_| EcdsaVerifyError::BadSignature)?; + Ok(pubkey.serialize_compressed()) + } +} - /// Check whether a given `key` exists in storage. - fn exists_child_storage(storage_key: &[u8], key: &[u8]) -> bool; +/// Interface that provides functions for hashing with different algorithms. +#[runtime_interface] +pub trait Hashing { + /// Conduct a 256-bit Keccak hash. + fn keccak_256(data: &[u8]) -> [u8; 32] { + tiny_keccak::keccak256(data) + } - /// Clear the storage entries with a key that starts with the given prefix. - fn clear_prefix(prefix: &[u8]); + /// Conduct a 128-bit Blake2 hash. + fn blake2_128(data: &[u8]) -> [u8; 16] { + primitives::hashing::blake2_128(data) + } - /// Clear the child storage entries with a key that starts with the given prefix. - fn clear_child_prefix(storage_key: &[u8], prefix: &[u8]); + /// Conduct a 256-bit Blake2 hash. + fn blake2_256(data: &[u8]) -> [u8; 32] { + primitives::hashing::blake2_256(data) + } + + /// Conduct four XX hashes to give a 256-bit result. + fn twox_256(data: &[u8]) -> [u8; 32] { + primitives::hashing::twox_256(data) + } + + /// Conduct two XX hashes to give a 128-bit result. + fn twox_128(data: &[u8]) -> [u8; 16] { + primitives::hashing::twox_128(data) + } + + /// Conduct two XX hashes to give a 64-bit result. + fn twox_64(data: &[u8]) -> [u8; 8] { + primitives::hashing::twox_64(data) + } +} + +/// Interface that provides functions to access the offchain functionality. +#[runtime_interface] +pub trait Offchain { + /// Returns if the local node is a potential validator. + /// + /// Even if this function returns `true`, it does not mean that any keys are configured + /// and that the validator is registered in the chain. + fn is_validator(&mut self) -> bool { + self.extension::() + .expect("is_validator can be called only in the offchain worker context") + .is_validator() + } + + /// Submit an encoded transaction to the pool. + /// + /// The transaction will end up in the pool. + fn submit_transaction(&mut self, data: Vec) -> Result<(), ()> { + self.extension::() + .expect("submit_transaction can be called only in the offchain worker context") + .submit_transaction(data) + } + + /// Returns information about the local node's network state. + fn network_state(&mut self) -> Result { + self.extension::() + .expect("network_state can be called only in the offchain worker context") + .network_state() + } + + /// Returns current UNIX timestamp (in millis) + fn timestamp(&mut self) -> Timestamp { + self.extension::() + .expect("timestamp can be called only in the offchain worker context") + .timestamp() + } + + /// Pause the execution until `deadline` is reached. + fn sleep_until(&mut self, deadline: Timestamp) { + self.extension::() + .expect("sleep_until can be called only in the offchain worker context") + .sleep_until(deadline) + } - /// "Commit" all existing operations and compute the resultant storage root. - fn storage_root() -> [u8; 32]; + /// Returns a random seed. + /// + /// This is a trully random non deterministic seed generated by host environment. + /// Obviously fine in the off-chain worker context. + fn random_seed(&mut self) -> [u8; 32] { + self.extension::() + .expect("random_seed can be called only in the offchain worker context") + .random_seed() + } + + /// Sets a value in the local storage. + /// + /// Note this storage is not part of the consensus, it's only accessible by + /// offchain worker tasks running on the same machine. It IS persisted between runs. + fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) { + self.extension::() + .expect("random_seed can be called only in the offchain worker context") + .local_storage_set(kind, key, value) + } + + /// Sets a value in the local storage if it matches current value. + /// + /// Since multiple offchain workers may be running concurrently, to prevent + /// data races use CAS to coordinate between them. + /// + /// Returns `true` if the value has been set, `false` otherwise. + /// + /// Note this storage is not part of the consensus, it's only accessible by + /// offchain worker tasks running on the same machine. It IS persisted between runs. + fn local_storage_compare_and_set( + &mut self, + kind: StorageKind, + key: &[u8], + old_value: Option>, + new_value: &[u8], + ) -> bool { + self.extension::() + .expect("random_seed can be called only in the offchain worker context") + .local_storage_compare_and_set(kind, key, old_value.as_ref().map(|v| v.deref()), new_value) + } + + /// Gets a value from the local storage. + /// + /// If the value does not exist in the storage `None` will be returned. + /// Note this storage is not part of the consensus, it's only accessible by + /// offchain worker tasks running on the same machine. It IS persisted between runs. + fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option> { + self.extension::() + .expect("random_seed can be called only in the offchain worker context") + .local_storage_get(kind, key) + } + + /// Initiates a http request given HTTP verb and the URL. + /// + /// Meta is a future-reserved field containing additional, parity-scale-codec encoded parameters. + /// Returns the id of newly started request. + fn http_request_start( + &mut self, + method: &str, + uri: &str, + meta: &[u8], + ) -> Result { + self.extension::() + .expect("random_seed can be called only in the offchain worker context") + .http_request_start(method, uri, meta) + } - /// "Commit" all existing operations and compute the resultant child storage root. - fn child_storage_root(storage_key: &[u8]) -> Vec; + /// Append header to the request. + fn http_request_add_header( + &mut self, + request_id: HttpRequestId, + name: &str, + value: &str, + ) -> Result<(), ()> { + self.extension::() + .expect("random_seed can be called only in the offchain worker context") + .http_request_add_header(request_id, name, value) + } - /// "Commit" all existing operations and get the resultant storage change root. - fn storage_changes_root(parent_hash: [u8; 32]) -> Option<[u8; 32]>; + /// Write a chunk of request body. + /// + /// Writing an empty chunks finalizes the request. + /// Passing `None` as deadline blocks forever. + /// + /// Returns an error in case deadline is reached or the chunk couldn't be written. + fn http_request_write_body( + &mut self, + request_id: HttpRequestId, + chunk: &[u8], + deadline: Option, + ) -> Result<(), HttpError> { + self.extension::() + .expect("random_seed can be called only in the offchain worker context") + .http_request_write_body(request_id, chunk, deadline) + } - /// A trie root formed from the iterated items. - fn blake2_256_trie_root(input: Vec<(Vec, Vec)>) -> H256; + /// Block and wait for the responses for given requests. + /// + /// Returns a vector of request statuses (the len is the same as ids). + /// Note that if deadline is not provided the method will block indefinitely, + /// otherwise unready responses will produce `DeadlineReached` status. + /// + /// Passing `None` as deadline blocks forever. + fn http_response_wait( + &mut self, + ids: &[HttpRequestId], + deadline: Option, + ) -> Vec { + self.extension::() + .expect("random_seed can be called only in the offchain worker context") + .http_response_wait(ids, deadline) + } + + /// Read all response headers. + /// + /// Returns a vector of pairs `(HeaderKey, HeaderValue)`. + /// NOTE response headers have to be read before response body. + fn http_response_headers(&mut self, request_id: HttpRequestId) -> Vec<(Vec, Vec)> { + self.extension::() + .expect("random_seed can be called only in the offchain worker context") + .http_response_headers(request_id) + } - /// A trie root formed from the enumerated items. - fn blake2_256_ordered_trie_root(input: Vec>) -> H256; + /// Read a chunk of body response to given buffer. + /// + /// Returns the number of bytes written or an error in case a deadline + /// is reached or server closed the connection. + /// If `0` is returned it means that the response has been fully consumed + /// and the `request_id` is now invalid. + /// NOTE this implies that response headers must be read before draining the body. + /// Passing `None` as a deadline blocks forever. + fn http_response_read_body( + &mut self, + request_id: HttpRequestId, + buffer: &mut [u8], + deadline: Option, + ) -> Result { + self.extension::() + .expect("random_seed can be called only in the offchain worker context") + .http_response_read_body(request_id, buffer, deadline) + .map(|r| r as u32) } } -export_api! { - pub(crate) trait OtherApi { - /// The current relay chain identifier. - fn chain_id() -> u64; - - /// Print a number. - fn print_num(val: u64); - /// Print any valid `utf8` buffer. - fn print_utf8(utf8: &[u8]); - /// Print any `u8` slice as hex. - fn print_hex(data: &[u8]); - - /// Request to print a log message (stderr) on the host. - /// - /// Note that this will be only displayed if the host - /// is enabed to display log messages with given - /// level and target. - /// - /// Instead of using directly, prefer setting up `RuntimeLogger` - /// and using `log` macros. - fn log( - level: LogLevel, - target: &[u8], - message: &[u8] - ); +/// Wasm only interface that provides functions for calling into the allocator. +#[runtime_interface(wasm_only)] +trait Allocator { + /// Malloc the given number of bytes and return the pointer to the allocated memory location. + fn malloc(&mut self, size: u32) -> Pointer { + self.allocate_memory(size).expect("Failed to allocate memory") + } + + /// Free the given pointer. + fn free(&mut self, ptr: Pointer) { + self.deallocate_memory(ptr).expect("Failed to deallocate memory") } } -export_api! { - pub(crate) trait CryptoApi { - /// Returns all ed25519 public keys for the given key id from the keystore. - fn ed25519_public_keys(id: KeyTypeId) -> Vec; - /// Generate an ed22519 key for the given key type and store it in the keystore. - /// - /// Returns the raw public key. - fn ed25519_generate(id: KeyTypeId, seed: Option<&str>) -> ed25519::Public; - /// Sign the given `msg` with the ed25519 key that corresponds to the given public key and - /// key type in the keystore. - /// - /// Returns the raw signature. - fn ed25519_sign( - id: KeyTypeId, - pubkey: &ed25519::Public, - msg: &[u8], - ) -> Option; - /// Verify an ed25519 signature. - /// - /// Returns `true` when the verification in successful. - fn ed25519_verify(sig: &ed25519::Signature, msg: &[u8], pubkey: &ed25519::Public) -> bool; - - /// Returns all sr25519 public keys for the given key id from the keystore. - fn sr25519_public_keys(id: KeyTypeId) -> Vec; - /// Generate an sr22519 key for the given key type and store it in the keystore. - /// - /// Returns the raw public key. - fn sr25519_generate(id: KeyTypeId, seed: Option<&str>) -> sr25519::Public; - /// Sign the given `msg` with the sr25519 key that corresponds to the given public key and - /// key type in the keystore. - /// - /// Returns the raw signature. - fn sr25519_sign( - id: KeyTypeId, - pubkey: &sr25519::Public, - msg: &[u8], - ) -> Option; - /// Verify an sr25519 signature. - /// - /// Returns `true` when the verification in successful. - fn sr25519_verify(sig: &sr25519::Signature, msg: &[u8], pubkey: &sr25519::Public) -> bool; - - /// Verify and recover a SECP256k1 ECDSA signature. - /// - `sig` is passed in RSV format. V should be either 0/1 or 27/28. - /// - returns `Err` if the signature is bad, otherwise the 64-byte raw pubkey (doesn't include the 0x04 prefix). - fn secp256k1_ecdsa_recover(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 64], EcdsaVerifyError>; - - /// Verify and recover a SECP256k1 ECDSA signature. - /// - `sig` is passed in RSV format. V should be either 0/1 or 27/28. - /// - returns `Err` if the signature is bad, otherwise the 33-byte compressed pubkey. - fn secp256k1_ecdsa_recover_compressed(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 33], EcdsaVerifyError>; +/// Interface that provides functions for logging from within the runtime. +#[runtime_interface] +pub trait Logging { + /// Request to print a log message on the host. + /// + /// Note that this will be only displayed if the host is enabled to display log messages with + /// given level and target. + /// + /// Instead of using directly, prefer setting up `RuntimeLogger` and using `log` macros. + fn log(level: LogLevel, target: &str, message: &[u8]) { + if let Ok(message) = std::str::from_utf8(message) { + log::log!( + target: target, + log::Level::from(level), + "{}", + message, + ) + } } } -export_api! { - pub(crate) trait HashingApi { - /// Conduct a 256-bit Keccak hash. - fn keccak_256(data: &[u8]) -> [u8; 32]; +/// Wasm-only interface that provides functions for interacting with the sandbox. +#[runtime_interface(wasm_only)] +pub trait Sandbox { + /// Instantiate a new sandbox instance with the given `wasm_code`. + fn instantiate( + &mut self, + dispatch_thunk: u32, + wasm_code: &[u8], + env_def: &[u8], + state_ptr: Pointer, + ) -> u32 { + self.sandbox() + .instance_new(dispatch_thunk, wasm_code, env_def, state_ptr.into()) + .expect("Failed to instantiate a new sandbox") + } - /// Conduct a 128-bit Blake2 hash. - fn blake2_128(data: &[u8]) -> [u8; 16]; + /// Invoke `function` in the sandbox with `sandbox_idx`. + fn invoke( + &mut self, + instance_idx: u32, + function: &str, + args: &[u8], + return_val_ptr: Pointer, + return_val_len: u32, + state_ptr: Pointer, + ) -> u32 { + self.sandbox().invoke( + instance_idx, + &function, + &args, + return_val_ptr, + return_val_len, + state_ptr.into(), + ).expect("Failed to invoke function with sandbox") + } - /// Conduct a 256-bit Blake2 hash. - fn blake2_256(data: &[u8]) -> [u8; 32]; + /// Create a new memory instance with the given `initial` and `maximum` size. + fn memory_new(&mut self, initial: u32, maximum: u32) -> u32 { + self.sandbox() + .memory_new(initial, maximum) + .expect("Failed to create new memory with sandbox") + } - /// Conduct four XX hashes to give a 256-bit result. - fn twox_256(data: &[u8]) -> [u8; 32]; + /// Get the memory starting at `offset` from the instance with `memory_idx` into the buffer. + fn memory_get( + &mut self, + memory_idx: u32, + offset: u32, + buf_ptr: Pointer, + buf_len: u32, + ) -> u32 { + self.sandbox() + .memory_get(memory_idx, offset, buf_ptr, buf_len) + .expect("Failed to get memory with sandbox") + } - /// Conduct two XX hashes to give a 128-bit result. - fn twox_128(data: &[u8]) -> [u8; 16]; + /// Set the memory in the given `memory_idx` to the given value at `offset`. + fn memory_set( + &mut self, + memory_idx: u32, + offset: u32, + val_ptr: Pointer, + val_len: u32, + ) -> u32 { + self.sandbox() + .memory_set(memory_idx, offset, val_ptr, val_len) + .expect("Failed to set memory with sandbox") + } - /// Conduct two XX hashes to give a 64-bit result. - fn twox_64(data: &[u8]) -> [u8; 8]; + /// Teardown the memory instance with the given `memory_idx`. + fn memory_teardown(&mut self, memory_idx: u32) { + self.sandbox().memory_teardown(memory_idx).expect("Failed to teardown memory with sandbox") } -} -export_api! { - pub(crate) trait OffchainApi { - /// Returns if the local node is a potential validator. - /// - /// Even if this function returns `true`, it does not mean that any keys are configured - /// and that the validator is registered in the chain. - fn is_validator() -> bool; - - /// Submit transaction to the pool. - /// - /// The transaction will end up in the pool. - fn submit_transaction(data: Vec) -> Result<(), ()>; - - /// Returns information about the local node's network state. - fn network_state() -> Result; - - /// Returns current UNIX timestamp (in millis) - fn timestamp() -> Timestamp; - - /// Pause the execution until `deadline` is reached. - fn sleep_until(deadline: Timestamp); - - /// Returns a random seed. - /// - /// This is a trully random non deterministic seed generated by host environment. - /// Obviously fine in the off-chain worker context. - fn random_seed() -> [u8; 32]; - - /// Sets a value in the local storage. - /// - /// Note this storage is not part of the consensus, it's only accessible by - /// offchain worker tasks running on the same machine. It IS persisted between runs. - fn local_storage_set(kind: StorageKind, key: &[u8], value: &[u8]); - - /// Sets a value in the local storage if it matches current value. - /// - /// Since multiple offchain workers may be running concurrently, to prevent - /// data races use CAS to coordinate between them. - /// - /// Returns `true` if the value has been set, `false` otherwise. - /// - /// Note this storage is not part of the consensus, it's only accessible by - /// offchain worker tasks running on the same machine. It IS persisted between runs. - fn local_storage_compare_and_set( - kind: StorageKind, - key: &[u8], - old_value: Option<&[u8]>, - new_value: &[u8], - ) -> bool; - - /// Gets a value from the local storage. - /// - /// If the value does not exist in the storage `None` will be returned. - /// Note this storage is not part of the consensus, it's only accessible by - /// offchain worker tasks running on the same machine. It IS persisted between runs. - fn local_storage_get(kind: StorageKind, key: &[u8]) -> Option>; - - /// Initiates a http request given HTTP verb and the URL. - /// - /// Meta is a future-reserved field containing additional, parity-scale-codec encoded parameters. - /// Returns the id of newly started request. - fn http_request_start( - method: &str, - uri: &str, - meta: &[u8], - ) -> Result; - - /// Append header to the request. - fn http_request_add_header( - request_id: HttpRequestId, - name: &str, - value: &str, - ) -> Result<(), ()>; - - /// Write a chunk of request body. - /// - /// Writing an empty chunks finalises the request. - /// Passing `None` as deadline blocks forever. - /// - /// Returns an error in case deadline is reached or the chunk couldn't be written. - fn http_request_write_body( - request_id: HttpRequestId, - chunk: &[u8], - deadline: Option, - ) -> Result<(), HttpError>; - - /// Block and wait for the responses for given requests. - /// - /// Returns a vector of request statuses (the len is the same as ids). - /// Note that if deadline is not provided the method will block indefinitely, - /// otherwise unready responses will produce `DeadlineReached` status. - /// - /// Passing `None` as deadline blocks forever. - fn http_response_wait( - ids: &[HttpRequestId], - deadline: Option, - ) -> Vec; - - /// Read all response headers. - /// - /// Returns a vector of pairs `(HeaderKey, HeaderValue)`. - /// NOTE response headers have to be read before response body. - fn http_response_headers(request_id: HttpRequestId) -> Vec<(Vec, Vec)>; - - /// Read a chunk of body response to given buffer. - /// - /// Returns the number of bytes written or an error in case a deadline - /// is reached or server closed the connection. - /// If `0` is returned it means that the response has been fully consumed - /// and the `request_id` is now invalid. - /// NOTE this implies that response headers must be read before draining the body. - /// Passing `None` as a deadline blocks forever. - fn http_response_read_body( - request_id: HttpRequestId, - buffer: &mut [u8], - deadline: Option, - ) -> Result; + /// Teardown the sandbox instance with the given `instance_idx`. + fn instance_teardown(&mut self, instance_idx: u32) { + self.sandbox().instance_teardown(instance_idx).expect("Failed to teardown sandbox instance") } } -/// API trait that should cover all other APIs. -/// -/// Implement this to make sure you implement all APIs. -trait Api: StorageApi + OtherApi + CryptoApi + HashingApi + OffchainApi {} +/// Allocator used by Substrate when executing the Wasm runtime. +#[cfg(not(feature = "std"))] +struct WasmAllocator; + +#[cfg(all(not(feature = "disable_global_allocator"), not(feature = "std")))] +#[global_allocator] +static ALLOCATOR: WasmAllocator = WasmAllocator; -mod imp { +#[cfg(not(feature = "std"))] +mod allocator_impl { use super::*; + use core::alloc::{GlobalAlloc, Layout}; - #[cfg(feature = "std")] - include!("../with_std.rs"); + unsafe impl GlobalAlloc for WasmAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + allocator::malloc(layout.size() as u32) + } - #[cfg(not(feature = "std"))] - include!("../without_std.rs"); + unsafe fn dealloc(&self, ptr: *mut u8, _: Layout) { + allocator::free(ptr) + } + } } -#[cfg(feature = "std")] -pub use self::imp::{StorageOverlay, ChildrenStorageOverlay, with_storage}; -#[cfg(not(feature = "std"))] -pub use self::imp::ext::*; +#[cfg(all(not(feature = "disable_panic_handler"), not(feature = "std")))] +#[panic_handler] +#[no_mangle] +pub fn panic(info: &core::panic::PanicInfo) -> ! { + unsafe { + let message = rstd::alloc::format!("{}", info); + misc::print_utf8(message.as_bytes()); + core::intrinsics::abort() + } +} + +#[cfg(all(not(feature = "disable_oom"), not(feature = "std")))] +#[alloc_error_handler] +pub extern fn oom(_: core::alloc::Layout) -> ! { + static OOM_MSG: &str = "Runtime memory exhausted. Aborting"; + + unsafe { + misc::print_utf8(OOM_MSG.as_bytes()); + core::intrinsics::abort(); + } +} /// Type alias for Externalities implementation used in tests. #[cfg(feature = "std")] -pub type TestExternalities = self::imp::TestExternalities; +pub type TestExternalities = substrate_state_machine::TestExternalities; + +/// The host functions Substrate provides for the Wasm runtime environment. +/// +/// All these host functions will be callable from inside the Wasm environment. +#[cfg(feature = "std")] +pub type SubstrateHostFunctions = ( + storage::HostFunctions, + misc::HostFunctions, + offchain::HostFunctions, + crypto::HostFunctions, + hashing::HostFunctions, + allocator::HostFunctions, + logging::HostFunctions, + sandbox::HostFunctions, +); + +#[cfg(test)] +mod tests { + use super::*; + use primitives::map; + use substrate_state_machine::BasicExternalities; + + #[test] + fn storage_works() { + let mut t = BasicExternalities::default(); + t.execute_with(|| { + assert_eq!(storage::get(b"hello"), None); + storage::set(b"hello", b"world"); + assert_eq!(storage::get(b"hello"), Some(b"world".to_vec())); + assert_eq!(storage::get(b"foo"), None); + storage::set(b"foo", &[1, 2, 3][..]); + }); + + t = BasicExternalities::new(map![b"foo".to_vec() => b"bar".to_vec()], map![]); + + t.execute_with(|| { + assert_eq!(storage::get(b"hello"), None); + assert_eq!(storage::get(b"foo"), Some(b"bar".to_vec())); + }); + } + + #[test] + fn read_storage_works() { + let mut t = BasicExternalities::new( + map![b":test".to_vec() => b"\x0b\0\0\0Hello world".to_vec()], + map![], + ); + + t.execute_with(|| { + let mut v = [0u8; 4]; + assert!(storage::read(b":test", &mut v[..], 0).unwrap() >= 4); + assert_eq!(v, [11u8, 0, 0, 0]); + let mut w = [0u8; 11]; + assert!(storage::read(b":test", &mut w[..], 4).unwrap() >= 11); + assert_eq!(&w, b"Hello world"); + }); + } + + #[test] + fn clear_prefix_works() { + let mut t = BasicExternalities::new( + map![ + b":a".to_vec() => b"\x0b\0\0\0Hello world".to_vec(), + b":abcd".to_vec() => b"\x0b\0\0\0Hello world".to_vec(), + b":abc".to_vec() => b"\x0b\0\0\0Hello world".to_vec(), + b":abdd".to_vec() => b"\x0b\0\0\0Hello world".to_vec() + ], + map![], + ); + + t.execute_with(|| { + storage::clear_prefix(b":abc"); + + assert!(storage::get(b":a").is_some()); + assert!(storage::get(b":abdd").is_some()); + assert!(storage::get(b":abcd").is_none()); + assert!(storage::get(b":abc").is_none()); + }); + } +} diff --git a/core/sr-io/with_std.rs b/core/sr-io/with_std.rs deleted file mode 100644 index 7e0504c37aafc..0000000000000 --- a/core/sr-io/with_std.rs +++ /dev/null @@ -1,553 +0,0 @@ -// Copyright 2017-2019 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 primitives::{ - blake2_128, blake2_256, twox_128, twox_256, twox_64, ed25519, Blake2Hasher, sr25519, Pair, H256, - traits::KeystoreExt, storage::ChildStorageKey, hexdisplay::HexDisplay, Hasher, - offchain::{self, OffchainExt}, -}; -// Switch to this after PoC-3 -// pub use primitives::BlakeHasher; -pub use substrate_state_machine::{BasicExternalities, TestExternalities}; - -use trie::{TrieConfiguration, trie_types::Layout}; - -use std::{collections::HashMap, convert::TryFrom}; - -use externalities::{with_externalities, set_and_run_with_externalities, ExternalitiesExt}; - -/// Additional bounds for `Hasher` trait for with_std. -pub trait HasherBounds {} -impl HasherBounds for T {} - -/// Returns a `ChildStorageKey` if the given `storage_key` slice is a valid storage -/// key or panics otherwise. -/// -/// Panicking here is aligned with what the `without_std` environment would do -/// in the case of an invalid child storage key. -fn child_storage_key_or_panic(storage_key: &[u8]) -> ChildStorageKey { - match ChildStorageKey::from_slice(storage_key) { - Some(storage_key) => storage_key, - None => panic!("child storage key is invalid"), - } -} - -impl StorageApi for () { - fn storage(key: &[u8]) -> Option> { - with_externalities(|ext| ext.storage(key).map(|s| s.to_vec())) - .expect("storage cannot be called outside of an Externalities-provided environment.") - } - - fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option { - with_externalities(|ext| ext.storage(key).map(|value| { - let data = &value[value_offset.min(value.len())..]; - let written = std::cmp::min(data.len(), value_out.len()); - value_out[..written].copy_from_slice(&data[..written]); - value.len() - })).expect("read_storage cannot be called outside of an Externalities-provided environment.") - } - - fn child_storage(storage_key: &[u8], key: &[u8]) -> Option> { - with_externalities(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.child_storage(storage_key, key).map(|s| s.to_vec()) - }) - .expect("storage cannot be called outside of an Externalities-provided environment.") - } - - fn set_storage(key: &[u8], value: &[u8]) { - with_externalities(|ext| - ext.set_storage(key.to_vec(), value.to_vec()) - ); - } - - fn read_child_storage( - storage_key: &[u8], - key: &[u8], - value_out: &mut [u8], - value_offset: usize, - ) -> Option { - with_externalities(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.child_storage(storage_key, key) - .map(|value| { - let data = &value[value_offset.min(value.len())..]; - let written = std::cmp::min(data.len(), value_out.len()); - value_out[..written].copy_from_slice(&data[..written]); - value.len() - }) - }) - .expect("read_child_storage cannot be called outside of an Externalities-provided environment.") - } - - fn set_child_storage(storage_key: &[u8], key: &[u8], value: &[u8]) { - with_externalities(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.set_child_storage(storage_key, key.to_vec(), value.to_vec()) - }); - } - - fn clear_storage(key: &[u8]) { - with_externalities(|ext| - ext.clear_storage(key) - ); - } - - fn clear_child_storage(storage_key: &[u8], key: &[u8]) { - with_externalities(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.clear_child_storage(storage_key, key) - }); - } - - fn kill_child_storage(storage_key: &[u8]) { - with_externalities(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.kill_child_storage(storage_key) - }); - } - - fn exists_storage(key: &[u8]) -> bool { - with_externalities(|ext| - ext.exists_storage(key) - ).unwrap_or(false) - } - - fn exists_child_storage(storage_key: &[u8], key: &[u8]) -> bool { - with_externalities(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.exists_child_storage(storage_key, key) - }).unwrap_or(false) - } - - fn clear_prefix(prefix: &[u8]) { - with_externalities(|ext| ext.clear_prefix(prefix)); - } - - fn clear_child_prefix(storage_key: &[u8], prefix: &[u8]) { - with_externalities(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.clear_child_prefix(storage_key, prefix) - }); - } - - fn storage_root() -> [u8; 32] { - with_externalities(|ext| - ext.storage_root() - ).unwrap_or(H256::zero()).into() - } - - fn child_storage_root(storage_key: &[u8]) -> Vec { - with_externalities(|ext| { - let storage_key = child_storage_key_or_panic(storage_key); - ext.child_storage_root(storage_key) - }).expect("child_storage_root cannot be called outside of an Externalities-provided environment.") - } - - fn storage_changes_root(parent_hash: [u8; 32]) -> Option<[u8; 32]> { - with_externalities(|ext| - ext.storage_changes_root(parent_hash.into()).map(|h| h.map(|h| h.into())) - ).unwrap_or(Ok(None)).expect("Invalid parent hash passed to storage_changes_root") - } - - fn blake2_256_trie_root(input: Vec<(Vec, Vec)>) -> H256 { - Layout::::trie_root(input) - } - - fn blake2_256_ordered_trie_root(input: Vec>) -> H256 { - Layout::::ordered_trie_root(input) - } -} - -impl OtherApi for () { - fn chain_id() -> u64 { - with_externalities(|ext| - ext.chain_id() - ).unwrap_or(0) - } - - fn print_num(val: u64) { - log::debug!(target: "runtime", "{}", val); - } - - fn print_utf8(utf8: &[u8]) { - if let Ok(data) = std::str::from_utf8(utf8) { - log::debug!(target: "runtime", "{}", data) - } - } - - fn print_hex(data: &[u8]) { - log::debug!(target: "runtime", "{}", HexDisplay::from(&data)); - } - - fn log( - level: LogLevel, - target: &[u8], - message: &[u8], - ) { - let target = std::str::from_utf8(target).unwrap_or("invalid utf8"); - let msg = std::str::from_utf8(message).unwrap_or("invalid utf8"); - - log::log!( - target: target, - log::Level::from(level), - "{}", - msg, - ) - } -} - -impl CryptoApi for () { - fn ed25519_public_keys(id: KeyTypeId) -> Vec { - with_externalities(|ext| { - ext.extension::() - .expect("No `keystore` associated for the current context!") - .read() - .ed25519_public_keys(id) - }).expect("`ed25519_public_keys` cannot be called outside of an Externalities-provided environment.") - } - - fn ed25519_generate(id: KeyTypeId, seed: Option<&str>) -> ed25519::Public { - with_externalities(|ext| { - ext.extension::() - .expect("No `keystore` associated for the current context!") - .write() - .ed25519_generate_new(id, seed) - .expect("`ed25519_generate` failed") - }).expect("`ed25519_generate` cannot be called outside of an Externalities-provided environment.") - } - - fn ed25519_sign( - id: KeyTypeId, - pubkey: &ed25519::Public, - msg: &[u8], - ) -> Option { - let pub_key = ed25519::Public::try_from(pubkey.as_ref()).ok()?; - - with_externalities(|ext| { - ext.extension::() - .expect("No `keystore` associated for the current context!") - .read() - .ed25519_key_pair(id, &pub_key) - .map(|k| k.sign(msg)) - }).expect("`ed25519_sign` cannot be called outside of an Externalities-provided environment.") - } - - fn ed25519_verify(sig: &ed25519::Signature, msg: &[u8], pubkey: &ed25519::Public) -> bool { - ed25519::Pair::verify(sig, msg, pubkey) - } - - fn sr25519_public_keys(id: KeyTypeId) -> Vec { - with_externalities(|ext| { - ext.extension::() - .expect("No `keystore` associated for the current context!") - .read() - .sr25519_public_keys(id) - }).expect("`sr25519_public_keys` cannot be called outside of an Externalities-provided environment.") - } - - fn sr25519_generate(id: KeyTypeId, seed: Option<&str>) -> sr25519::Public { - with_externalities(|ext| { - ext.extension::() - .expect("No `keystore` associated for the current context!") - .write() - .sr25519_generate_new(id, seed) - .expect("`sr25519_generate` failed") - }).expect("`sr25519_generate` cannot be called outside of an Externalities-provided environment.") - } - - fn sr25519_sign( - id: KeyTypeId, - pubkey: &sr25519::Public, - msg: &[u8], - ) -> Option { - let pub_key = sr25519::Public::try_from(pubkey.as_ref()).ok()?; - - with_externalities(|ext| { - ext.extension::() - .expect("No `keystore` associated for the current context!") - .read() - .sr25519_key_pair(id, &pub_key) - .map(|k| k.sign(msg)) - }).expect("`sr25519_sign` cannot be called outside of an Externalities-provided environment.") - } - - fn sr25519_verify(sig: &sr25519::Signature, msg: &[u8], pubkey: &sr25519::Public) -> bool { - sr25519::Pair::verify(sig, msg, pubkey) - } - - fn secp256k1_ecdsa_recover(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 64], EcdsaVerifyError> { - let rs = secp256k1::Signature::parse_slice(&sig[0..64]) - .map_err(|_| EcdsaVerifyError::BadRS)?; - let v = secp256k1::RecoveryId::parse(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8) - .map_err(|_| EcdsaVerifyError::BadV)?; - let pubkey = secp256k1::recover(&secp256k1::Message::parse(msg), &rs, &v) - .map_err(|_| EcdsaVerifyError::BadSignature)?; - let mut res = [0u8; 64]; - res.copy_from_slice(&pubkey.serialize()[1..65]); - Ok(res) - } - - fn secp256k1_ecdsa_recover_compressed(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 33], EcdsaVerifyError> { - let rs = secp256k1::Signature::parse_slice(&sig[0..64]) - .map_err(|_| EcdsaVerifyError::BadRS)?; - let v = secp256k1::RecoveryId::parse(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8) - .map_err(|_| EcdsaVerifyError::BadV)?; - let pubkey = secp256k1::recover(&secp256k1::Message::parse(msg), &rs, &v) - .map_err(|_| EcdsaVerifyError::BadSignature)?; - Ok(pubkey.serialize_compressed()) - } -} - -impl HashingApi for () { - fn keccak_256(data: &[u8]) -> [u8; 32] { - tiny_keccak::keccak256(data) - } - - fn blake2_128(data: &[u8]) -> [u8; 16] { - blake2_128(data) - } - - fn blake2_256(data: &[u8]) -> [u8; 32] { - blake2_256(data) - } - - fn twox_256(data: &[u8]) -> [u8; 32] { - twox_256(data) - } - - fn twox_128(data: &[u8]) -> [u8; 16] { - twox_128(data) - } - - fn twox_64(data: &[u8]) -> [u8; 8] { - twox_64(data) - } -} - -fn with_offchain(f: impl FnOnce(&mut dyn offchain::Externalities) -> R, msg: &'static str) -> R { - with_externalities(|ext| ext - .extension::() - .map(|ext| f(&mut **ext)) - .expect(msg) - ).expect("offchain-worker functions cannot be called outside of an Externalities-provided environment.") -} - -impl OffchainApi for () { - fn is_validator() -> bool { - with_offchain(|ext| { - ext.is_validator() - }, "is_validator can be called only in the offchain worker context") - } - - fn submit_transaction(data: Vec) -> Result<(), ()> { - with_offchain(|ext| { - ext.submit_transaction(data) - }, "submit_transaction can be called only in the offchain worker context") - } - - fn network_state() -> Result { - with_offchain(|ext| { - ext.network_state() - }, "network_state can be called only in the offchain worker context") - } - - fn timestamp() -> offchain::Timestamp { - with_offchain(|ext| { - ext.timestamp() - }, "timestamp can be called only in the offchain worker context") - } - - fn sleep_until(deadline: offchain::Timestamp) { - with_offchain(|ext| { - ext.sleep_until(deadline) - }, "sleep_until can be called only in the offchain worker context") - } - - fn random_seed() -> [u8; 32] { - with_offchain(|ext| { - ext.random_seed() - }, "random_seed can be called only in the offchain worker context") - } - - fn local_storage_set(kind: offchain::StorageKind, key: &[u8], value: &[u8]) { - with_offchain(|ext| { - ext.local_storage_set(kind, key, value) - }, "local_storage_set can be called only in the offchain worker context") - } - - fn local_storage_compare_and_set( - kind: offchain::StorageKind, - key: &[u8], - old_value: Option<&[u8]>, - new_value: &[u8], - ) -> bool { - with_offchain(|ext| { - ext.local_storage_compare_and_set(kind, key, old_value, new_value) - }, "local_storage_compare_and_set can be called only in the offchain worker context") - } - - fn local_storage_get(kind: offchain::StorageKind, key: &[u8]) -> Option> { - with_offchain(|ext| { - ext.local_storage_get(kind, key) - }, "local_storage_get can be called only in the offchain worker context") - } - - fn http_request_start( - method: &str, - uri: &str, - meta: &[u8], - ) -> Result { - with_offchain(|ext| { - ext.http_request_start(method, uri, meta) - }, "http_request_start can be called only in the offchain worker context") - } - - fn http_request_add_header( - request_id: offchain::HttpRequestId, - name: &str, - value: &str, - ) -> Result<(), ()> { - with_offchain(|ext| { - ext.http_request_add_header(request_id, name, value) - }, "http_request_add_header can be called only in the offchain worker context") - } - - fn http_request_write_body( - request_id: offchain::HttpRequestId, - chunk: &[u8], - deadline: Option, - ) -> Result<(), offchain::HttpError> { - with_offchain(|ext| { - ext.http_request_write_body(request_id, chunk, deadline) - }, "http_request_write_body can be called only in the offchain worker context") - } - - fn http_response_wait( - ids: &[offchain::HttpRequestId], - deadline: Option, - ) -> Vec { - with_offchain(|ext| { - ext.http_response_wait(ids, deadline) - }, "http_response_wait can be called only in the offchain worker context") - } - - fn http_response_headers( - request_id: offchain::HttpRequestId, - ) -> Vec<(Vec, Vec)> { - with_offchain(|ext| { - ext.http_response_headers(request_id) - }, "http_response_headers can be called only in the offchain worker context") - } - - fn http_response_read_body( - request_id: offchain::HttpRequestId, - buffer: &mut [u8], - deadline: Option, - ) -> Result { - with_offchain(|ext| { - ext.http_response_read_body(request_id, buffer, deadline) - }, "http_response_read_body can be called only in the offchain worker context") - } -} - -impl Api for () {} - -/// A set of key value pairs for storage. -pub type StorageOverlay = HashMap, Vec>; - -/// A set of key value pairs for children storage; -pub type ChildrenStorageOverlay = HashMap, StorageOverlay>; - -/// Execute the given closure with global functions available whose functionality routes into -/// externalities that draw from and populate `storage` and `children_storage`. -/// Forwards the value that the closure returns. -pub fn with_storage R>( - storage: &mut (StorageOverlay, ChildrenStorageOverlay), - f: F -) -> R { - let mut alt_storage = Default::default(); - rstd::mem::swap(&mut alt_storage, storage); - - let mut ext = BasicExternalities::new(alt_storage.0, alt_storage.1); - let r = set_and_run_with_externalities(&mut ext, f); - - *storage = ext.into_storages(); - - r -} - -#[cfg(test)] -mod std_tests { - use super::*; - use primitives::map; - - #[test] - fn storage_works() { - let mut t = BasicExternalities::default(); - assert!(set_and_run_with_externalities(&mut t, || { - assert_eq!(storage(b"hello"), None); - set_storage(b"hello", b"world"); - assert_eq!(storage(b"hello"), Some(b"world".to_vec())); - assert_eq!(storage(b"foo"), None); - set_storage(b"foo", &[1, 2, 3][..]); - true - })); - - t = BasicExternalities::new(map![b"foo".to_vec() => b"bar".to_vec()], map![]); - - assert!(!set_and_run_with_externalities(&mut t, || { - assert_eq!(storage(b"hello"), None); - assert_eq!(storage(b"foo"), Some(b"bar".to_vec())); - false - })); - } - - #[test] - fn read_storage_works() { - let mut t = BasicExternalities::new(map![ - b":test".to_vec() => b"\x0b\0\0\0Hello world".to_vec() - ], map![]); - - set_and_run_with_externalities(&mut t, || { - let mut v = [0u8; 4]; - assert!(read_storage(b":test", &mut v[..], 0).unwrap() >= 4); - assert_eq!(v, [11u8, 0, 0, 0]); - let mut w = [0u8; 11]; - assert!(read_storage(b":test", &mut w[..], 4).unwrap() >= 11); - assert_eq!(&w, b"Hello world"); - }); - } - - #[test] - fn clear_prefix_works() { - let mut t = BasicExternalities::new(map![ - b":a".to_vec() => b"\x0b\0\0\0Hello world".to_vec(), - b":abcd".to_vec() => b"\x0b\0\0\0Hello world".to_vec(), - b":abc".to_vec() => b"\x0b\0\0\0Hello world".to_vec(), - b":abdd".to_vec() => b"\x0b\0\0\0Hello world".to_vec() - ], map![]); - - set_and_run_with_externalities(&mut t, || { - clear_prefix(b":abc"); - - assert!(storage(b":a").is_some()); - assert!(storage(b":abdd").is_some()); - assert!(storage(b":abcd").is_none()); - assert!(storage(b":abc").is_none()); - }); - } -} diff --git a/core/sr-io/without_std.rs b/core/sr-io/without_std.rs deleted file mode 100644 index c3f7d62031b93..0000000000000 --- a/core/sr-io/without_std.rs +++ /dev/null @@ -1,1241 +0,0 @@ -// Copyright 2017-2019 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 . - -#[doc(hidden)] -pub use rstd; -pub use rstd::{mem, slice}; - -use core::{intrinsics, panic::PanicInfo}; -use rstd::{vec::Vec, cell::Cell, convert::TryInto}; -use primitives::offchain; -use codec::Decode; - -#[cfg(not(feature = "no_panic_handler"))] -#[panic_handler] -#[no_mangle] -pub fn panic(info: &PanicInfo) -> ! { - unsafe { - let message = rstd::alloc::format!("{}", info); - extern_functions_host_impl::ext_print_utf8(message.as_ptr() as *const u8, message.len() as u32); - intrinsics::abort() - } -} - -#[cfg(not(feature = "no_oom"))] -#[alloc_error_handler] -pub extern fn oom(_: core::alloc::Layout) -> ! { - static OOM_MSG: &str = "Runtime memory exhausted. Aborting"; - - unsafe { - extern_functions_host_impl::ext_print_utf8(OOM_MSG.as_ptr(), OOM_MSG.len() as u32); - intrinsics::abort(); - } -} - -/// External (Host) APIs -pub mod ext { - use super::*; - - /// The state of an exchangeable function. - #[derive(Clone, Copy)] - enum ExchangeableFunctionState { - /// Original function is present - Original, - /// The function has been replaced. - Replaced, - } - - /// A function which implementation can be exchanged. - /// - /// Internally this works by swapping function pointers. - pub struct ExchangeableFunction(Cell<(T, ExchangeableFunctionState)>); - - impl ExchangeableFunction { - /// Create a new instance of `ExchangeableFunction`. - pub const fn new(impl_: T) -> Self { - Self(Cell::new((impl_, ExchangeableFunctionState::Original))) - } - } - - impl ExchangeableFunction { - /// Replace the implementation with `new_impl`. - /// - /// # Panics - /// - /// Panics when trying to replace an already replaced implementation. - /// - /// # Returns - /// - /// Returns the original implementation wrapped in [`RestoreImplementation`]. - pub fn replace_implementation(&'static self, new_impl: T) -> RestoreImplementation { - if let ExchangeableFunctionState::Replaced = self.0.get().1 { - panic!("Trying to replace an already replaced implementation!") - } - - let old = self.0.replace((new_impl, ExchangeableFunctionState::Replaced)); - - RestoreImplementation(self, Some(old.0)) - } - - /// Restore the original implementation. - fn restore_orig_implementation(&self, orig: T) { - self.0.set((orig, ExchangeableFunctionState::Original)); - } - - /// Returns the internal function pointer. - pub fn get(&self) -> T { - self.0.get().0 - } - } - - // WASM does not support threads, so this is safe; qed. - unsafe impl Sync for ExchangeableFunction {} - - /// Restores a function implementation on drop. - /// - /// Stores a static reference to the function object and the original implementation. - pub struct RestoreImplementation(&'static ExchangeableFunction, Option); - - impl Drop for RestoreImplementation { - fn drop(&mut self) { - self.0.restore_orig_implementation(self.1.take().expect("Value is only taken on drop; qed")); - } - } - - /// Declare extern functions - macro_rules! extern_functions { - ( - $( - $( #[$attr:meta] )* - fn $name:ident ( $( $arg:ident : $arg_ty:ty ),* $(,)? ) $( -> $ret:ty )?; - )* - ) => { - $( - $( #[$attr] )* - #[allow(non_upper_case_globals)] - pub static $name: ExchangeableFunction $ret )?> = - ExchangeableFunction::new(extern_functions_host_impl::$name); - )* - - /// The exchangeable extern functions host implementations. - pub(crate) mod extern_functions_host_impl { - $( - pub unsafe fn $name ( $( $arg : $arg_ty ),* ) $( -> $ret )? { - implementation::$name ( $( $arg ),* ) - } - )* - - mod implementation { - extern "C" { - $( - pub fn $name ( $( $arg : $arg_ty ),* ) $( -> $ret )?; - )* - } - } - } - }; - } - - /// Host functions, provided by the executor. - /// A WebAssembly runtime module would "import" these to access the execution environment - /// (most importantly, storage) or perform heavy hash calculations. - /// See also "ext_" functions in sr-sandbox and sr-std - extern_functions! { - /// Host functions for printing, useful for debugging. - fn ext_print_utf8(utf8_data: *const u8, utf8_len: u32); - /// Print data as hex. - fn ext_print_hex(data: *const u8, len: u32); - /// Print a number - fn ext_print_num(value: u64); - /// Print a log line if logging for given level and target is enabled. - fn ext_log( - level: u32, - target_data: *const u8, - target_len: u32, - message_data: *const u8, - message_len: u32, - ); - - /// Set value for key in storage. - fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32); - /// Remove key and value from storage. - fn ext_clear_storage(key_data: *const u8, key_len: u32); - /// Checks if the given key exists in the storage. - /// - /// # Returns - /// - /// - `1` if the value exists. - /// - `0` if the value does not exists. - fn ext_exists_storage(key_data: *const u8, key_len: u32) -> u32; - /// Remove storage entries which key starts with given prefix. - fn ext_clear_prefix(prefix_data: *const u8, prefix_len: u32); - /// Remove child storage entries which key starts with given prefix. - fn ext_clear_child_prefix( - storage_key_data: *const u8, - storage_key_len: u32, - prefix_data: *const u8, - prefix_len: u32, - ); - /// Gets the value of the given key from storage. - /// - /// The host allocates the memory for storing the value. - /// - /// # Returns - /// - /// - `0` if no value exists to the given key. `written_out` is set to `u32::max_value()`. - /// - Otherwise, pointer to the value in memory. `written_out` contains the length of the value. - fn ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8; - /// Gets the value of the given key from storage. - /// - /// The value is written into `value` starting at `value_offset`. - /// - /// If the value length is greater than `value_len - value_offset`, the value is written partially. - /// - /// # Returns - /// - /// - `u32::max_value()` if the value does not exists. - /// - /// - Otherwise, the number of bytes written for value. - fn ext_get_storage_into( - key_data: *const u8, - key_len: u32, - value_data: *mut u8, - value_len: u32, - value_offset: u32, - ) -> u32; - /// Gets the trie root of the storage. - fn ext_storage_root(result: *mut u8); - /// Get the change trie root of the current storage overlay at a block with given parent. - /// - /// # Returns - /// - /// - `1` if the change trie root was found. - /// - `0` if the change trie root was not found. - fn ext_storage_changes_root( - parent_hash_data: *const u8, - parent_hash_len: u32, - result: *mut u8, - ) -> u32; - - /// A child storage function. - /// - /// See [`ext_set_storage`] for details. - /// - /// A child storage is used e.g. by a contract. - fn ext_set_child_storage( - storage_key_data: *const u8, - storage_key_len: u32, - key_data: *const u8, - key_len: u32, - value_data: *const u8, - value_len: u32, - ); - /// A child storage function. - /// - /// See [`ext_clear_storage`] for details. - /// - /// A child storage is used e.g. by a contract. - fn ext_clear_child_storage( - storage_key_data: *const u8, - storage_key_len: u32, - key_data: *const u8, - key_len: u32, - ); - /// A child storage function. - /// - /// See [`ext_exists_storage`] for details. - /// - /// A child storage is used e.g. by a contract. - fn ext_exists_child_storage( - storage_key_data: *const u8, - storage_key_len: u32, - key_data: *const u8, - key_len: u32, - ) -> u32; - /// A child storage function. - /// - /// See [`ext_kill_storage`] for details. - /// - /// A child storage is used e.g. by a contract. - fn ext_kill_child_storage(storage_key_data: *const u8, storage_key_len: u32); - /// A child storage function. - /// - /// See [`ext_get_allocated_storage`] for details. - /// - /// A child storage is used e.g. by a contract. - fn ext_get_allocated_child_storage( - storage_key_data: *const u8, - storage_key_len: u32, - key_data: *const u8, - key_len: u32, - written_out: *mut u32, - ) -> *mut u8; - /// A child storage function. - /// - /// See [`ext_get_storage_into`] for details. - /// - /// A child storage is used e.g. by a contract. - fn ext_get_child_storage_into( - storage_key_data: *const u8, - storage_key_len: u32, - key_data: *const u8, - key_len: u32, - value_data: *mut u8, - value_len: u32, - value_offset: u32, - ) -> u32; - /// Commits all changes and calculates the child-storage root. - /// - /// A child storage is used e.g. by a contract. - /// - /// # Returns - /// - /// - The pointer to the result vector and `written_out` contains its length. - fn ext_child_storage_root( - storage_key_data: *const u8, - storage_key_len: u32, - written_out: *mut u32 - ) -> *mut u8; - - /// The current relay chain identifier. - fn ext_chain_id() -> u64; - - /// Calculate a blake2_256 merkle trie root. - fn ext_blake2_256_enumerated_trie_root( - values_data: *const u8, - lens_data: *const u32, - lens_len: u32, - result: *mut u8 - ); - /// BLAKE2_128 hash - fn ext_blake2_128(data: *const u8, len: u32, out: *mut u8); - /// BLAKE2_256 hash - fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8); - /// XX64 hash - fn ext_twox_64(data: *const u8, len: u32, out: *mut u8); - /// XX128 hash - fn ext_twox_128(data: *const u8, len: u32, out: *mut u8); - /// XX256 hash - fn ext_twox_256(data: *const u8, len: u32, out: *mut u8); - /// Keccak256 hash - fn ext_keccak_256(data: *const u8, len: u32, out: *mut u8); - - /// Returns all `ed25519` public keys for the given key type from the keystore. - fn ext_ed25519_public_keys(id: *const u8, result_len: *mut u32) -> *mut u8; - - /// Note: `ext_ed25519_verify` returns `0` if the signature is correct, nonzero otherwise. - fn ext_ed25519_verify( - msg_data: *const u8, - msg_len: u32, - sig_data: *const u8, - pubkey_data: *const u8, - ) -> u32; - - /// Generate an `ed25519` key pair for the given key type id and store the public key - /// in `out`. - fn ext_ed25519_generate(id: *const u8, seed: *const u8, seed_len: u32, out: *mut u8); - - /// Sign the given `msg` with the `ed25519` key pair that corresponds to then given key - /// type id and public key. The raw signature is stored in `out`. - /// - /// # Returns - /// - /// - `0` on success - /// - nonezero if something failed, e.g. retrieving of the key. - fn ext_ed25519_sign( - id: *const u8, - pubkey: *const u8, - msg: *const u8, - msg_len: u32, - out: *mut u8, - ) -> u32; - - /// Returns all `sr25519` public keys for the given key type from the keystore. - fn ext_sr25519_public_keys(id: *const u8, result_len: *mut u32) -> *mut u8; - - /// Note: `ext_sr25519_verify` returns 0 if the signature is correct, nonzero otherwise. - fn ext_sr25519_verify( - msg_data: *const u8, - msg_len: u32, - sig_data: *const u8, - pubkey_data: *const u8, - ) -> u32; - - /// Generate an `sr25519` key pair for the given key type id and store the public - /// key in `out`. - fn ext_sr25519_generate(id: *const u8, seed: *const u8, seed_len: u32, out: *mut u8); - - /// Sign the given `msg` with the `sr25519` key pair that corresponds to then given key - /// type id and public key. The raw signature is stored in `out`. - /// - /// # Returns - /// - /// - `0` on success - /// - nonezero if something failed, e.g. retrieving of the key. - fn ext_sr25519_sign( - id: *const u8, - pubkey: *const u8, - msg: *const u8, - msg_len: u32, - out: *mut u8, - ) -> u32; - - /// Note: ext_secp256k1_ecdsa_recover returns 0 if the signature is correct, nonzero otherwise. - /// - /// pubkey_data must point to 64 bytes. - fn ext_secp256k1_ecdsa_recover( - msg_data: *const u8, - sig_data: *const u8, - pubkey_data: *mut u8, - ) -> u32; - - /// Note: ext_secp256k1_ecdsa_recover_compressed returns 0 if the signature is correct, nonzero otherwise. - /// - /// pubkey_data must point to 33 bytes. - fn ext_secp256k1_ecdsa_recover_compressed( - msg_data: *const u8, - sig_data: *const u8, - pubkey_data: *mut u8, - ) -> u32; - - //================================ - // Offchain-worker Context - //================================ - - /// Returns if the local node is a potential validator. - /// - /// - `1` == `true` - /// - `0` == `false` - fn ext_is_validator() -> u32; - - /// Submit transaction. - /// - /// # Returns - /// - /// - 0 if it was successfuly added to the pool - /// - nonzero otherwise. - fn ext_submit_transaction(data: *const u8, len: u32) -> u32; - - /// Returns information about the local node's network state. - /// - /// # Returns - /// - /// The encoded `Result`. - /// `written_out` contains the length of the message. - /// - /// The ownership of the returned buffer is transferred to the runtime - /// code and the runtime is responsible for freeing it. This is always - /// a properly allocated pointer (which cannot be NULL), hence the - /// runtime code can always rely on it. - fn ext_network_state(written_out: *mut u32) -> *mut u8; - - /// Returns current UNIX timestamp (milliseconds) - fn ext_timestamp() -> u64; - - /// Pause execution until given timestamp (milliseconds; `deadline`) is reached. - /// - /// The deadline is obtained by querying the current timestamp via `ext_timestamp` - /// and then adding some time to it. - fn ext_sleep_until(deadline: u64); - - /// Generate a random seed - /// - /// `data` has to be a pointer to a slice of 32 bytes. - fn ext_random_seed(data: *mut u8); - - /// Write a value to local storage. - fn ext_local_storage_set(kind: u32, key: *const u8, key_len: u32, value: *const u8, value_len: u32); - - /// Write a value to local storage in atomic fashion. - /// - /// # Returns - /// - `0` in case the value has been set - /// - `1` if the `old_value` didn't match - fn ext_local_storage_compare_and_set( - kind: u32, - key: *const u8, - key_len: u32, - old_value: *const u8, - old_value_len: u32, - new_value: *const u8, - new_value_len: u32, - ) -> u32; - - /// Read a value from local storage. - /// - /// - /// # Returns - /// - /// - 0 if the value has not been found, the `value_len` is set to `u32::max_value`. - /// - Otherwise, pointer to the value in memory. `value_len` contains the length of the value. - fn ext_local_storage_get(kind: u32, key: *const u8, key_len: u32, value_len: *mut u32) -> *mut u8; - - /// Initiates a http request. - /// - /// `meta` is parity-scale-codec encoded additional parameters to the request (like redirection policy, - /// timeouts, certificates policy, etc). The format is not yet specified and the field is currently - /// only reserved for future use. - /// - /// # Returns - /// - /// `RequestId(u16)` of initiated request, any value beyond `u16::max_value` - /// signifies an error. - fn ext_http_request_start( - method: *const u8, - method_len: u32, - url: *const u8, - url_len: u32, - meta: *const u8, - meta_len: u32, - ) -> u32; - - /// Add a header to the request. - /// - /// # Returns - /// - /// - `0` if successful (and the request id exists) - /// - nonzero otherwise - fn ext_http_request_add_header( - request_id: u32, - name: *const u8, - name_len: u32, - value: *const u8, - value_len: u32, - ) -> u32; - - /// Write a chunk of request body. - /// - /// Writing an empty chunks finalises the request. - /// Passing `0` as deadline blocks forever. - /// - /// # Returns - /// - /// - `0` if successful, - /// - nonzero otherwise (see HttpError for the codes) - fn ext_http_request_write_body( - request_id: u32, - chunk: *const u8, - chunk_len: u32, - deadline: u64, - ) -> u32; - - /// Block and wait for the responses for given requests. - /// - /// Note that if deadline is 0 the method will block indefinitely, - /// otherwise unready responses will produce `DeadlineReached` status. - /// (see #primitives::offchain::HttpRequestStatus) - /// - /// Make sure that `statuses` have the same length as ids. - fn ext_http_response_wait( - ids: *const u32, - ids_len: u32, - statuses: *mut u32, - deadline: u64, - ); - - /// Read all response headers. - /// - /// Note the headers are only available before response body is fully consumed. - /// - /// # Returns - /// - /// - A pointer to parity-scale-codec encoded vector of pairs `(HeaderKey, HeaderValue)`. - /// - In case invalid `id` is passed it returns a pointer to parity-encoded empty vector. - fn ext_http_response_headers( - id: u32, - written_out: *mut u32, - ) -> *mut u8; - - /// Read a chunk of body response to given buffer. - /// - /// Passing `0` as deadline blocks forever. - /// - /// # Returns - /// - /// The number of bytes written if successful, - /// - if it's `0` it means response has been fully consumed, - /// - if it's greater than `u32::max_value() - 255` it means reading body failed. - /// - /// In case of failure, the error code should be mapped to `HttpError` - /// in a following manner: - /// - `u32::max_value()` HttpError code 1 (DeadlineReached) - /// - `u32::max_value() - 1` HttpError code 2 (IoError) - /// The rest is reserved for potential future errors. - fn ext_http_response_read_body( - id: u32, - buffer: *mut u8, - buffer_len: u32, - deadline: u64, - ) -> u32; - } -} - -pub use self::ext::*; - -impl StorageApi for () { - fn storage(key: &[u8]) -> Option> { - let mut length: u32 = 0; - unsafe { - let ptr = ext_get_allocated_storage.get()(key.as_ptr(), key.len() as u32, &mut length); - from_raw_parts(ptr, length) - } - } - - fn child_storage(storage_key: &[u8], key: &[u8]) -> Option> { - let mut length: u32 = 0; - unsafe { - let ptr = ext_get_allocated_child_storage.get()( - storage_key.as_ptr(), - storage_key.len() as u32, - key.as_ptr(), - key.len() as u32, - &mut length - ); - from_raw_parts(ptr, length) - } - } - - fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option { - unsafe { - match ext_get_storage_into.get()( - key.as_ptr(), - key.len() as u32, - value_out.as_mut_ptr(), - value_out.len() as u32, - value_offset as u32, - ) { - none if none == u32::max_value() => None, - length => Some(length as usize), - } - } - } - - fn read_child_storage(storage_key: &[u8], key: &[u8], value_out: &mut [u8], value_offset: usize) -> Option { - unsafe { - match ext_get_child_storage_into.get()( - storage_key.as_ptr(), storage_key.len() as u32, - key.as_ptr(), key.len() as u32, - value_out.as_mut_ptr(), value_out.len() as u32, - value_offset as u32 - ) { - none if none == u32::max_value() => None, - length => Some(length as usize), - } - } - } - - fn set_storage(key: &[u8], value: &[u8]) { - unsafe { - ext_set_storage.get()( - key.as_ptr(), key.len() as u32, - value.as_ptr(), value.len() as u32 - ); - } - } - - fn set_child_storage(storage_key: &[u8], key: &[u8], value: &[u8]) { - unsafe { - ext_set_child_storage.get()( - storage_key.as_ptr(), storage_key.len() as u32, - key.as_ptr(), key.len() as u32, - value.as_ptr(), value.len() as u32 - ); - } - } - - fn clear_storage(key: &[u8]) { - unsafe { - ext_clear_storage.get()( - key.as_ptr(), key.len() as u32 - ); - } - } - - fn clear_child_storage(storage_key: &[u8], key: &[u8]) { - unsafe { - ext_clear_child_storage.get()( - storage_key.as_ptr(), storage_key.len() as u32, - key.as_ptr(), key.len() as u32 - ); - } - } - - fn exists_storage(key: &[u8]) -> bool { - unsafe { - ext_exists_storage.get()( - key.as_ptr(), key.len() as u32 - ) != 0 - } - } - - fn exists_child_storage(storage_key: &[u8], key: &[u8]) -> bool { - unsafe { - ext_exists_child_storage.get()( - storage_key.as_ptr(), storage_key.len() as u32, - key.as_ptr(), key.len() as u32 - ) != 0 - } - } - - fn clear_prefix(prefix: &[u8]) { - unsafe { - ext_clear_prefix.get()( - prefix.as_ptr(), - prefix.len() as u32 - ); - } - } - - fn clear_child_prefix(storage_key: &[u8], prefix: &[u8]) { - unsafe { - ext_clear_child_prefix.get()( - storage_key.as_ptr(), storage_key.len() as u32, - prefix.as_ptr(), prefix.len() as u32 - ); - } - } - - fn kill_child_storage(storage_key: &[u8]) { - unsafe { - ext_kill_child_storage.get()( - storage_key.as_ptr(), - storage_key.len() as u32 - ); - } - } - - fn storage_root() -> [u8; 32] { - let mut result: [u8; 32] = Default::default(); - unsafe { - ext_storage_root.get()(result.as_mut_ptr()); - } - result - } - - fn child_storage_root(storage_key: &[u8]) -> Vec { - let mut length: u32 = 0; - unsafe { - let ptr = ext_child_storage_root.get()( - storage_key.as_ptr(), - storage_key.len() as u32, - &mut length - ); - from_raw_parts(ptr, length).expect("ext_child_storage_root never returns u32::max_value; qed") - } - } - - fn storage_changes_root(parent_hash: [u8; 32]) -> Option<[u8; 32]> { - let mut result: [u8; 32] = Default::default(); - let is_set = unsafe { - ext_storage_changes_root.get()(parent_hash.as_ptr(), parent_hash.len() as u32, result.as_mut_ptr()) - }; - - if is_set != 0 { - Some(result) - } else { - None - } - } - - - fn blake2_256_trie_root(_input: Vec<(Vec, Vec)>) -> H256 { - unimplemented!() - } - - fn blake2_256_ordered_trie_root(input: Vec>) -> H256 { - let mut values = Vec::with_capacity(input.len()); - let mut lengths = Vec::with_capacity(input.len()); - for v in input { - values.extend_from_slice(&v); - lengths.push((v.len() as u32).to_le()); - } - let mut result: [u8; 32] = Default::default(); - unsafe { - ext_blake2_256_enumerated_trie_root.get()( - values.as_ptr(), - lengths.as_ptr(), - lengths.len() as u32, - result.as_mut_ptr(), - ); - } - result.into() - } -} - -impl OtherApi for () { - fn chain_id() -> u64 { - unsafe { - ext_chain_id.get()() - } - } - - fn print_num(val: u64) { - unsafe { - ext_print_num.get()(val); - } - } - - fn print_utf8(utf8: &[u8]) { - unsafe { - ext_print_utf8.get()(utf8.as_ptr(), utf8.len() as u32); - } - } - - fn print_hex(data: &[u8]) { - unsafe { - ext_print_hex.get()(data.as_ptr(), data.len() as u32); - } - } - - fn log( - level: LogLevel, - target: &[u8], - message: &[u8] - ) { - unsafe { - ext_log.get()( - level as u32, - target.as_ptr(), - target.len() as u32, - message.as_ptr(), - message.len() as u32, - ) - } - } -} - -impl HashingApi for () { - fn keccak_256(data: &[u8]) -> [u8; 32] { - let mut result: [u8; 32] = Default::default(); - unsafe { - ext_keccak_256.get()(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); - } - result - } - - fn blake2_128(data: &[u8]) -> [u8; 16] { - let mut result: [u8; 16] = Default::default(); - unsafe { - ext_blake2_128.get()(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); - } - result - } - - fn blake2_256(data: &[u8]) -> [u8; 32] { - let mut result: [u8; 32] = Default::default(); - unsafe { - ext_blake2_256.get()(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); - } - result - } - - fn twox_256(data: &[u8]) -> [u8; 32] { - let mut result: [u8; 32] = Default::default(); - unsafe { - ext_twox_256.get()(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); - } - result - } - - fn twox_128(data: &[u8]) -> [u8; 16] { - let mut result: [u8; 16] = Default::default(); - unsafe { - ext_twox_128.get()(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); - } - result - } - - fn twox_64(data: &[u8]) -> [u8; 8] { - let mut result: [u8; 8] = Default::default(); - unsafe { - ext_twox_64.get()(data.as_ptr(), data.len() as u32, result.as_mut_ptr()); - } - result - } -} - -impl CryptoApi for () { - fn ed25519_public_keys(id: KeyTypeId) -> Vec { - let mut res_len = 0u32; - unsafe { - let res_ptr = ext_ed25519_public_keys.get()(id.0.as_ptr(), &mut res_len); - Vec::decode(&mut rstd::slice::from_raw_parts(res_ptr, res_len as usize)).unwrap_or_default() - } - } - - fn ed25519_generate(id: KeyTypeId, seed: Option<&str>) -> ed25519::Public { - let mut res = [0u8; 32]; - let seed = seed.as_ref().map(|s| s.as_bytes()).unwrap_or(&[]); - unsafe { - ext_ed25519_generate.get()(id.0.as_ptr(), seed.as_ptr(), seed.len() as u32, res.as_mut_ptr()) - }; - ed25519::Public(res) - } - - fn ed25519_sign( - id: KeyTypeId, - pubkey: &ed25519::Public, - msg: &[u8], - ) -> Option { - let mut res = [0u8; 64]; - let success = unsafe { - ext_ed25519_sign.get()( - id.0.as_ptr(), - pubkey.0.as_ptr(), - msg.as_ptr(), - msg.len() as u32, - res.as_mut_ptr(), - ) == 0 - }; - - if success { - Some(ed25519::Signature(res)) - } else { - None - } - } - - fn ed25519_verify(sig: &ed25519::Signature, msg: &[u8], pubkey: &ed25519::Public) -> bool { - unsafe { - ext_ed25519_verify.get()( - msg.as_ptr(), - msg.len() as u32, - sig.0.as_ptr(), - pubkey.0.as_ptr(), - ) == 0 - } - } - - fn sr25519_public_keys(id: KeyTypeId) -> Vec { - let mut res_len = 0u32; - unsafe { - let res_ptr = ext_sr25519_public_keys.get()(id.0.as_ptr(), &mut res_len); - Vec::decode(&mut rstd::slice::from_raw_parts(res_ptr, res_len as usize)).unwrap_or_default() - } - } - - fn sr25519_generate(id: KeyTypeId, seed: Option<&str>) -> sr25519::Public { - let mut res = [0u8;32]; - let seed = seed.as_ref().map(|s| s.as_bytes()).unwrap_or(&[]); - unsafe { - ext_sr25519_generate.get()(id.0.as_ptr(), seed.as_ptr(), seed.len() as u32, res.as_mut_ptr()) - }; - sr25519::Public(res) - } - - fn sr25519_sign( - id: KeyTypeId, - pubkey: &sr25519::Public, - msg: &[u8], - ) -> Option { - let mut res = [0u8; 64]; - let success = unsafe { - ext_sr25519_sign.get()( - id.0.as_ptr(), - pubkey.0.as_ptr(), - msg.as_ptr(), - msg.len() as u32, - res.as_mut_ptr(), - ) == 0 - }; - - if success { - Some(sr25519::Signature(res)) - } else { - None - } - } - - fn sr25519_verify(sig: &sr25519::Signature, msg: &[u8], pubkey: &sr25519::Public) -> bool { - unsafe { - ext_sr25519_verify.get()( - msg.as_ptr(), - msg.len() as u32, - sig.0.as_ptr(), - pubkey.0.as_ptr(), - ) == 0 - } - } - - fn secp256k1_ecdsa_recover(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 64], EcdsaVerifyError> { - let mut pubkey = [0u8; 64]; - match unsafe { - ext_secp256k1_ecdsa_recover.get()(msg.as_ptr(), sig.as_ptr(), pubkey.as_mut_ptr()) - } { - 0 => Ok(pubkey), - 1 => Err(EcdsaVerifyError::BadRS), - 2 => Err(EcdsaVerifyError::BadV), - 3 => Err(EcdsaVerifyError::BadSignature), - _ => unreachable!("`ext_secp256k1_ecdsa_recover` only returns 0, 1, 2 or 3; qed"), - } - } - - fn secp256k1_ecdsa_recover_compressed(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 33], EcdsaVerifyError> { - let mut pubkey = [0u8; 33]; - match unsafe { - ext_secp256k1_ecdsa_recover_compressed.get()(msg.as_ptr(), sig.as_ptr(), pubkey.as_mut_ptr()) - } { - 0 => Ok(pubkey), - 1 => Err(EcdsaVerifyError::BadRS), - 2 => Err(EcdsaVerifyError::BadV), - 3 => Err(EcdsaVerifyError::BadSignature), - _ => unreachable!("`ext_secp256k1_ecdsa_recover_compressed` only returns 0, 1, 2 or 3; qed"), - } - } -} - -impl OffchainApi for () { - fn is_validator() -> bool { - unsafe { ext_is_validator.get()() == 1 } - } - - fn submit_transaction(data: Vec) -> Result<(), ()> { - let ret = unsafe { - ext_submit_transaction.get()(data.as_ptr(), data.len() as u32) - }; - - if ret == 0 { - Ok(()) - } else { - Err(()) - } - } - - fn network_state() -> Result { - let mut len = 0_u32; - let raw_result = unsafe { - let ptr = ext_network_state.get()(&mut len); - - from_raw_parts(ptr, len) - }; - - match raw_result { - Some(raw_result) => codec::Decode::decode(&mut &*raw_result).unwrap_or(Err(())), - None => Err(()) - } - } - - fn timestamp() -> offchain::Timestamp { - offchain::Timestamp::from_unix_millis(unsafe { - ext_timestamp.get()() - }) - } - - fn sleep_until(deadline: offchain::Timestamp) { - unsafe { - ext_sleep_until.get()(deadline.unix_millis()) - } - } - - fn random_seed() -> [u8; 32] { - let mut result = [0_u8; 32]; - unsafe { - ext_random_seed.get()(result.as_mut_ptr()) - } - result - } - - fn local_storage_set(kind: offchain::StorageKind, key: &[u8], value: &[u8]) { - unsafe { - ext_local_storage_set.get()( - kind.into(), - key.as_ptr(), - key.len() as u32, - value.as_ptr(), - value.len() as u32, - ) - } - } - - fn local_storage_compare_and_set( - kind: offchain::StorageKind, - key: &[u8], - old_value: Option<&[u8]>, - new_value: &[u8], - ) -> bool { - let (ptr, len) = match old_value { - Some(old_value) => ( - old_value.as_ptr(), - old_value.len() as u32, - ), - None => (0 as *const u8, u32::max_value()), - }; - - unsafe { - ext_local_storage_compare_and_set.get()( - kind.into(), - key.as_ptr(), - key.len() as u32, - ptr, - len, - new_value.as_ptr(), - new_value.len() as u32, - ) == 0 - } - } - - fn local_storage_get(kind: offchain::StorageKind, key: &[u8]) -> Option> { - let mut len = 0u32; - unsafe { - let ptr = ext_local_storage_get.get()( - kind.into(), - key.as_ptr(), - key.len() as u32, - &mut len, - ); - - from_raw_parts(ptr, len) - } - } - - fn http_request_start(method: &str, url: &str, meta: &[u8]) -> Result { - let method = method.as_bytes(); - let url = url.as_bytes(); - - let result = unsafe { - ext_http_request_start.get()( - method.as_ptr(), - method.len() as u32, - url.as_ptr(), - url.len() as u32, - meta.as_ptr(), - meta.len() as u32, - ) - }; - - if result > u16::max_value() as u32 { - Err(()) - } else { - Ok(offchain::HttpRequestId(result as u16)) - } - } - - fn http_request_add_header(request_id: offchain::HttpRequestId, name: &str, value: &str) -> Result<(), ()> { - let name = name.as_bytes(); - let value = value.as_bytes(); - - let result = unsafe { - ext_http_request_add_header.get()( - request_id.into(), - name.as_ptr(), - name.len() as u32, - value.as_ptr(), - value.len() as u32, - ) - }; - - if result == 0 { - Ok(()) - } else { - Err(()) - } - } - - fn http_request_write_body( - request_id: offchain::HttpRequestId, - chunk: &[u8], - deadline: Option - ) -> Result<(), offchain::HttpError> { - let res = unsafe { - ext_http_request_write_body.get()( - request_id.into(), - chunk.as_ptr(), - chunk.len() as u32, - deadline.map_or(0, |x| x.unix_millis()), - ) - }; - - if res == 0 { - Ok(()) - } else { - Err(res.try_into().unwrap_or(offchain::HttpError::IoError)) - } - } - - fn http_response_wait( - ids: &[offchain::HttpRequestId], - deadline: Option - ) -> Vec { - let ids = ids.iter().map(|x| x.0 as u32).collect::>(); - let mut statuses = Vec::new(); - statuses.resize(ids.len(), 0u32); - - unsafe { - ext_http_response_wait.get()( - ids.as_ptr(), - ids.len() as u32, - statuses.as_mut_ptr(), - deadline.map_or(0, |x| x.unix_millis()), - ) - } - - statuses - .into_iter() - .map(|status| status.try_into().unwrap_or(offchain::HttpRequestStatus::Invalid)) - .collect() - } - - fn http_response_headers( - request_id: offchain::HttpRequestId, - ) -> Vec<(Vec, Vec)> { - let mut len = 0u32; - let raw_result = unsafe { - let ptr = ext_http_response_headers.get()( - request_id.into(), - &mut len, - ); - - from_raw_parts(ptr, len).expect("ext_http_response_headers never return u32::max_value; qed") - }; - - codec::Decode::decode(&mut &*raw_result).unwrap_or_default() - } - - fn http_response_read_body( - request_id: offchain::HttpRequestId, - buffer: &mut [u8], - deadline: Option, - ) -> Result { - let res = unsafe { - ext_http_response_read_body.get()( - request_id.into(), - buffer.as_mut_ptr(), - buffer.len() as u32, - deadline.map_or(0, |x| x.unix_millis()), - ) - }; - - if res >= u32::max_value() - 255 { - let code = (u32::max_value() - res) + 1; - code.try_into().map_err(|_| offchain::HttpError::IoError) - } else { - Ok(res as usize) - } - } -} - -unsafe fn from_raw_parts(ptr: *mut u8, len: u32) -> Option> { - if len == u32::max_value() { - None - } else { - // Invariants required by Vec::from_raw_parts are not formally fulfilled. - // We don't allocate via String/Vec, but use a custom allocator instead. - // See #300 for more details. - Some(>::from_raw_parts(ptr, len as usize, len as usize)) - } -} - -impl Api for () {} diff --git a/core/sr-primitives/src/generic/unchecked_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_extrinsic.rs index befa857dffd64..79d86a0756d3c 100644 --- a/core/sr-primitives/src/generic/unchecked_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_extrinsic.rs @@ -16,9 +16,8 @@ //! Generic implementation of an unchecked (pre-verification) extrinsic. -use rstd::prelude::*; -use rstd::fmt; -use runtime_io::blake2_256; +use rstd::{fmt, prelude::*}; +use runtime_io::hashing::blake2_256; use codec::{Decode, Encode, EncodeLike, Input, Error}; use crate::{ traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic, IdentifyAccount}, @@ -295,7 +294,7 @@ where #[cfg(test)] mod tests { use super::*; - use runtime_io::blake2_256; + use runtime_io::hashing::blake2_256; use crate::codec::{Encode, Decode}; use crate::traits::{SignedExtension, IdentifyAccount, IdentityLookup}; use serde::{Serialize, Deserialize}; diff --git a/core/sr-primitives/src/lib.rs b/core/sr-primitives/src/lib.rs index fce9f7def05b3..4213f152e9386 100644 --- a/core/sr-primitives/src/lib.rs +++ b/core/sr-primitives/src/lib.rs @@ -38,7 +38,7 @@ pub use paste; pub use app_crypto; #[cfg(feature = "std")] -pub use runtime_io::{StorageOverlay, ChildrenStorageOverlay}; +pub use primitives::storage::{StorageOverlay, ChildrenStorageOverlay}; use rstd::prelude::*; use rstd::convert::TryFrom; @@ -66,10 +66,7 @@ pub use app_crypto::{RuntimeAppPublic, BoundToRuntimeAppPublic}; pub use primitives::RuntimeDebug; /// Re-export top-level arithmetic stuff. -pub use arithmetic::{ - Perquintill, Perbill, Permill, Percent, - Rational128, Fixed64 -}; +pub use arithmetic::{Perquintill, Perbill, Permill, Percent, Rational128, Fixed64}; /// Re-export 128 bit helpers. pub use arithmetic::helpers_128bit; /// Re-export big_uint stuff. @@ -244,7 +241,7 @@ impl traits::IdentifyAccount for MultiSigner { match self { MultiSigner::Ed25519(who) => <[u8; 32]>::from(who).into(), MultiSigner::Sr25519(who) => <[u8; 32]>::from(who).into(), - MultiSigner::Ecdsa(who) => runtime_io::blake2_256(who.as_ref()).into(), + MultiSigner::Ecdsa(who) => runtime_io::hashing::blake2_256(who.as_ref()).into(), } } } @@ -307,9 +304,11 @@ impl Verify for MultiSignature { (MultiSignature::Ed25519(ref sig), who) => sig.verify(msg, &ed25519::Public::from_slice(who.as_ref())), (MultiSignature::Sr25519(ref sig), who) => sig.verify(msg, &sr25519::Public::from_slice(who.as_ref())), (MultiSignature::Ecdsa(ref sig), who) => { - let m = runtime_io::blake2_256(msg.get()); - match runtime_io::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) { - Ok(pubkey) => &runtime_io::blake2_256(pubkey.as_ref()) == >::as_ref(who), + let m = runtime_io::hashing::blake2_256(msg.get()); + match runtime_io::crypto::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) { + Ok(pubkey) => + &runtime_io::hashing::blake2_256(pubkey.as_ref()) + == >::as_ref(who), _ => false, } } diff --git a/core/sr-primitives/src/offchain/http.rs b/core/sr-primitives/src/offchain/http.rs index 77e514d65342b..8024437075924 100644 --- a/core/sr-primitives/src/offchain/http.rs +++ b/core/sr-primitives/src/offchain/http.rs @@ -221,11 +221,15 @@ impl<'a, I: AsRef<[u8]>, T: IntoIterator> Request<'a, T> { let meta = &[]; // start an http request. - let id = runtime_io::http_request_start(self.method.as_ref(), self.url, meta).map_err(|_| HttpError::IoError)?; + let id = runtime_io::offchain::http_request_start( + self.method.as_ref(), + self.url, + meta, + ).map_err(|_| HttpError::IoError)?; // add custom headers for header in &self.headers { - runtime_io::http_request_add_header( + runtime_io::offchain::http_request_add_header( id, header.name(), header.value(), @@ -234,11 +238,11 @@ impl<'a, I: AsRef<[u8]>, T: IntoIterator> Request<'a, T> { // write body for chunk in self.body { - runtime_io::http_request_write_body(id, chunk.as_ref(), self.deadline)?; + runtime_io::offchain::http_request_write_body(id, chunk.as_ref(), self.deadline)?; } // finalise the request - runtime_io::http_request_write_body(id, &[], self.deadline)?; + runtime_io::offchain::http_request_write_body(id, &[], self.deadline)?; Ok(PendingRequest { id, @@ -303,7 +307,7 @@ impl PendingRequest { deadline: impl Into> ) -> Vec> { let ids = requests.iter().map(|r| r.id).collect::>(); - let statuses = runtime_io::http_response_wait(&ids, deadline.into()); + let statuses = runtime_io::offchain::http_response_wait(&ids, deadline.into()); statuses .into_iter() @@ -341,7 +345,9 @@ impl Response { /// Retrieve the headers for this response. pub fn headers(&mut self) -> &Headers { if self.headers.is_none() { - self.headers = Some(Headers { raw: runtime_io::http_response_headers(self.id) }); + self.headers = Some( + Headers { raw: runtime_io::offchain::http_response_headers(self.id) }, + ); } self.headers.as_ref().expect("Headers were just set; qed") } @@ -420,7 +426,10 @@ impl Iterator for ResponseBody { } if self.filled_up_to.is_none() { - let result = runtime_io::http_response_read_body(self.id, &mut self.buffer, self.deadline); + let result = runtime_io::offchain::http_response_read_body( + self.id, + &mut self.buffer, + self.deadline); match result { Err(e) => { self.error = Some(e); @@ -431,7 +440,7 @@ impl Iterator for ResponseBody { } Ok(size) => { self.position = 0; - self.filled_up_to = Some(size); + self.filled_up_to = Some(size as usize); } } } diff --git a/core/sr-primitives/src/testing.rs b/core/sr-primitives/src/testing.rs index b1bd94a5461b1..c0b3eb3902de7 100644 --- a/core/sr-primitives/src/testing.rs +++ b/core/sr-primitives/src/testing.rs @@ -90,7 +90,7 @@ impl app_crypto::RuntimeAppPublic for UintAuthorityId { ALL_KEYS.with(|l| l.borrow().clone()) } - fn generate_pair(_: Option<&str>) -> Self { + fn generate_pair(_: Option>) -> Self { use rand::RngCore; UintAuthorityId(rand::thread_rng().next_u64()) } diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index 195fca26fad19..8805611d1435b 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -85,21 +85,24 @@ pub trait Verify { impl Verify for primitives::ed25519::Signature { type Signer = primitives::ed25519::Public; fn verify>(&self, mut msg: L, signer: &primitives::ed25519::Public) -> bool { - runtime_io::ed25519_verify(self, msg.get(), signer) + runtime_io::crypto::ed25519_verify(self, msg.get(), signer) } } impl Verify for primitives::sr25519::Signature { type Signer = primitives::sr25519::Public; fn verify>(&self, mut msg: L, signer: &primitives::sr25519::Public) -> bool { - runtime_io::sr25519_verify(self, msg.get(), signer) + runtime_io::crypto::sr25519_verify(self, msg.get(), signer) } } impl Verify for primitives::ecdsa::Signature { type Signer = primitives::ecdsa::Public; fn verify>(&self, mut msg: L, signer: &primitives::ecdsa::Public) -> bool { - match runtime_io::secp256k1_ecdsa_recover_compressed(self.as_ref(), &runtime_io::blake2_256(msg.get())) { + match runtime_io::crypto::secp256k1_ecdsa_recover_compressed( + self.as_ref(), + &runtime_io::hashing::blake2_256(msg.get()), + ) { Ok(pubkey) => >::as_ref(signer) == &pubkey[..], _ => false, } @@ -399,23 +402,23 @@ impl Hash for BlakeTwo256 { type Output = primitives::H256; type Hasher = Blake2Hasher; fn hash(s: &[u8]) -> Self::Output { - runtime_io::blake2_256(s).into() + runtime_io::hashing::blake2_256(s).into() } fn trie_root(input: Vec<(Vec, Vec)>) -> Self::Output { - runtime_io::blake2_256_trie_root(input) + runtime_io::storage::blake2_256_trie_root(input) } fn ordered_trie_root(input: Vec>) -> Self::Output { - runtime_io::blake2_256_ordered_trie_root(input) + runtime_io::storage::blake2_256_ordered_trie_root(input) } fn storage_root() -> Self::Output { - runtime_io::storage_root().into() + runtime_io::storage::root().into() } fn storage_changes_root(parent_hash: Self::Output) -> Option { - runtime_io::storage_changes_root(parent_hash.into()).map(Into::into) + runtime_io::storage::changes_root(parent_hash.into()).map(Into::into) } } @@ -1152,14 +1155,14 @@ macro_rules! impl_opaque_keys { /// The generated key pairs are stored in the keystore. /// /// Returns the concatenated SCALE encoded public keys. - pub fn generate(seed: Option<&str>) -> $crate::rstd::vec::Vec { + pub fn generate(seed: Option<$crate::rstd::vec::Vec>) -> $crate::rstd::vec::Vec { let keys = Self{ $( $field: < < $type as $crate::BoundToRuntimeAppPublic >::Public as $crate::RuntimeAppPublic - >::generate_pair(seed), + >::generate_pair(seed.clone()), )* }; $crate::codec::Encode::encode(&keys) @@ -1224,19 +1227,19 @@ impl Printable for usize { impl Printable for u64 { fn print(&self) { - runtime_io::print_num(*self); + runtime_io::misc::print_num(*self); } } impl Printable for &[u8] { fn print(&self) { - runtime_io::print_hex(self); + runtime_io::misc::print_hex(self); } } impl Printable for &str { fn print(&self) { - runtime_io::print_utf8(self.as_bytes()); + runtime_io::misc::print_utf8(self.as_bytes()); } } diff --git a/core/sr-sandbox/Cargo.toml b/core/sr-sandbox/Cargo.toml index 87b4e742a0413..20d569f043c60 100755 --- a/core/sr-sandbox/Cargo.toml +++ b/core/sr-sandbox/Cargo.toml @@ -2,16 +2,13 @@ name = "sr-sandbox" version = "2.0.0" authors = ["Parity Technologies "] -build = "build.rs" edition = "2018" -[build-dependencies] -rustc_version = "0.2.3" - [dependencies] wasmi = { version = "0.5.1", optional = true } primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } rstd = { package = "sr-std", path = "../sr-std", default-features = false } +runtime-io = { package = "sr-io", path = "../sr-io", default-features = false } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } [dev-dependencies] @@ -25,6 +22,6 @@ std = [ "primitives/std", "rstd/std", "codec/std", + "runtime-io/std", ] -nightly = [] strict = [] diff --git a/core/sr-sandbox/build.rs b/core/sr-sandbox/build.rs deleted file mode 100755 index 5b5d06b65a253..0000000000000 --- a/core/sr-sandbox/build.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Set a nightly feature - -use rustc_version::{version, version_meta, Channel}; - -fn main() { - // Assert we haven't traveled back in time - assert!(version().unwrap().major >= 1); - - // Set cfg flags depending on release channel - if let Channel::Nightly = version_meta().unwrap().channel { - println!("cargo:rustc-cfg=feature=\"nightly\""); - } -} diff --git a/core/sr-sandbox/src/lib.rs b/core/sr-sandbox/src/lib.rs index c9f9135661586..4639cf983af44 100755 --- a/core/sr-sandbox/src/lib.rs +++ b/core/sr-sandbox/src/lib.rs @@ -197,7 +197,7 @@ impl Instance { /// - Trap occured at the execution time. pub fn invoke( &mut self, - name: &[u8], + name: &str, args: &[TypedValue], state: &mut T, ) -> Result { diff --git a/core/sr-sandbox/with_std.rs b/core/sr-sandbox/with_std.rs index ea7ce818350d0..afc092686eeca 100755 --- a/core/sr-sandbox/with_std.rs +++ b/core/sr-sandbox/with_std.rs @@ -257,7 +257,11 @@ pub struct Instance { } impl Instance { - pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder, state: &mut T) -> Result, Error> { + pub fn new( + code: &[u8], + env_def_builder: &EnvironmentDefinitionBuilder, + state: &mut T, + ) -> Result, Error> { let module = Module::from_buffer(code).map_err(|_| Error::Module)?; let not_started_instance = ModuleInstance::new(&module, env_def_builder) .map_err(|_| Error::Module)?; @@ -269,7 +273,8 @@ impl Instance { state, defined_host_functions: &defined_host_functions, }; - let instance = not_started_instance.run_start(&mut externals).map_err(|_| Error::Execution)?; + let instance = not_started_instance.run_start(&mut externals) + .map_err(|_| Error::Execution)?; instance }; @@ -282,13 +287,12 @@ impl Instance { pub fn invoke( &mut self, - name: &[u8], + name: &str, args: &[TypedValue], state: &mut T, ) -> Result { let args = args.iter().cloned().map(Into::into).collect::>(); - let name = ::std::str::from_utf8(name).map_err(|_| Error::Execution)?; let mut externals = GuestExternals { state, defined_host_functions: &self.defined_host_functions, @@ -350,7 +354,7 @@ mod tests { env_builder.add_host_func("env", "polymorphic_id", env_polymorphic_id); let mut instance = Instance::new(code, &env_builder, &mut state)?; - let result = instance.invoke(b"call", args, &mut state); + let result = instance.invoke("call", args, &mut state); result.map_err(|_| HostError) } @@ -474,7 +478,7 @@ mod tests { // But this fails since we imported a function that returns i32 as if it returned i64. assert_matches!( - instance.invoke(b"call", &[], &mut ()), + instance.invoke("call", &[], &mut ()), Err(Error::Execution) ); } diff --git a/core/sr-sandbox/without_std.rs b/core/sr-sandbox/without_std.rs index ee5f7697fe71d..d7fffbf88b27f 100755 --- a/core/sr-sandbox/without_std.rs +++ b/core/sr-sandbox/without_std.rs @@ -14,12 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use rstd::prelude::*; -use rstd::{slice, marker, mem, vec}; -use rstd::rc::Rc; +use rstd::{prelude::*, slice, marker, mem, vec, rc::Rc}; use codec::{Decode, Encode}; use primitives::sandbox as sandbox_primitives; use super::{Error, TypedValue, ReturnValue, HostFuncType}; +use runtime_io::sandbox; mod ffi { use rstd::mem; @@ -43,51 +42,6 @@ mod ffi { assert!(mem::size_of::() == mem::size_of::>()); mem::transmute::>(idx) } - - extern "C" { - pub fn ext_sandbox_instantiate( - dispatch_thunk: extern "C" fn( - serialized_args_ptr: *const u8, - serialized_args_len: usize, - state: usize, - f: HostFuncIndex, - ) -> u64, - wasm_ptr: *const u8, - wasm_len: usize, - imports_ptr: *const u8, - imports_len: usize, - state: usize, - ) -> u32; - pub fn ext_sandbox_invoke( - instance_idx: u32, - export_ptr: *const u8, - export_len: usize, - args_ptr: *const u8, - args_len: usize, - return_val_ptr: *mut u8, - return_val_len: usize, - state: usize, - ) -> u32; - pub fn ext_sandbox_memory_new(initial: u32, maximum: u32) -> u32; - pub fn ext_sandbox_memory_get( - memory_idx: u32, - offset: u32, - buf_ptr: *mut u8, - buf_len: usize, - ) -> u32; - pub fn ext_sandbox_memory_set( - memory_idx: u32, - offset: u32, - val_ptr: *const u8, - val_len: usize, - ) -> u32; - pub fn ext_sandbox_memory_teardown( - memory_idx: u32, - ); - pub fn ext_sandbox_instance_teardown( - instance_idx: u32, - ); - } } struct MemoryHandle { @@ -96,9 +50,7 @@ struct MemoryHandle { impl Drop for MemoryHandle { fn drop(&mut self) { - unsafe { - ffi::ext_sandbox_memory_teardown(self.memory_idx); - } + sandbox::memory_teardown(self.memory_idx); } } @@ -111,15 +63,13 @@ pub struct Memory { impl Memory { pub fn new(initial: u32, maximum: Option) -> Result { - let result = unsafe { - let maximum = if let Some(maximum) = maximum { - maximum - } else { - sandbox_primitives::MEM_UNLIMITED - }; - ffi::ext_sandbox_memory_new(initial, maximum) + let maximum = if let Some(maximum) = maximum { + maximum + } else { + sandbox_primitives::MEM_UNLIMITED }; - match result { + + match sandbox::memory_new(initial, maximum) { sandbox_primitives::ERR_MODULE => Err(Error::Module), memory_idx => Ok(Memory { handle: Rc::new(MemoryHandle { memory_idx, }), @@ -128,7 +78,12 @@ impl Memory { } pub fn get(&self, offset: u32, buf: &mut [u8]) -> Result<(), Error> { - let result = unsafe { ffi::ext_sandbox_memory_get(self.handle.memory_idx, offset, buf.as_mut_ptr(), buf.len()) }; + let result = sandbox::memory_get( + self.handle.memory_idx, + offset, + buf.as_mut_ptr(), + buf.len() as u32, + ); match result { sandbox_primitives::ERR_OK => Ok(()), sandbox_primitives::ERR_OUT_OF_BOUNDS => Err(Error::OutOfBounds), @@ -137,7 +92,12 @@ impl Memory { } pub fn set(&self, offset: u32, val: &[u8]) -> Result<(), Error> { - let result = unsafe { ffi::ext_sandbox_memory_set(self.handle.memory_idx, offset, val.as_ptr(), val.len()) }; + let result = sandbox::memory_set( + self.handle.memory_idx, + offset, + val.as_ptr() as _ , + val.len() as u32, + ); match result { sandbox_primitives::ERR_OK => Ok(()), sandbox_primitives::ERR_OUT_OF_BOUNDS => Err(Error::OutOfBounds), @@ -251,26 +211,27 @@ extern "C" fn dispatch_thunk( } impl Instance { - pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder, state: &mut T) -> Result, Error> { + pub fn new( + code: &[u8], + env_def_builder: &EnvironmentDefinitionBuilder, + state: &mut T, + ) -> Result, Error> { let serialized_env_def: Vec = env_def_builder.env_def.encode(); - let result = unsafe { - // It's very important to instantiate thunk with the right type. - let dispatch_thunk = dispatch_thunk::; - - ffi::ext_sandbox_instantiate( - dispatch_thunk, - code.as_ptr(), - code.len(), - serialized_env_def.as_ptr(), - serialized_env_def.len(), - state as *const T as usize, - ) - }; + // It's very important to instantiate thunk with the right type. + let dispatch_thunk = dispatch_thunk::; + let result = sandbox::instantiate( + dispatch_thunk as u32, + code, + &serialized_env_def, + state as *const T as _, + ); + let instance_idx = match result { sandbox_primitives::ERR_MODULE => return Err(Error::Module), sandbox_primitives::ERR_EXECUTION => return Err(Error::Execution), instance_idx => instance_idx, }; + // We need to retain memories to keep them alive while the Instance is alive. let retained_memories = env_def_builder.retained_memories.clone(); Ok(Instance { @@ -282,25 +243,22 @@ impl Instance { pub fn invoke( &mut self, - name: &[u8], + name: &str, args: &[TypedValue], state: &mut T, ) -> Result { let serialized_args = args.to_vec().encode(); let mut return_val = vec![0u8; sandbox_primitives::ReturnValue::ENCODED_MAX_SIZE]; - let result = unsafe { - ffi::ext_sandbox_invoke( - self.instance_idx, - name.as_ptr(), - name.len(), - serialized_args.as_ptr(), - serialized_args.len(), - return_val.as_mut_ptr(), - return_val.len(), - state as *const T as usize, - ) - }; + let result = sandbox::invoke( + self.instance_idx, + name, + &serialized_args, + return_val.as_mut_ptr() as _, + return_val.len() as u32, + state as *const T as _, + ); + match result { sandbox_primitives::ERR_OK => { let return_val = sandbox_primitives::ReturnValue::decode(&mut &return_val[..]) @@ -315,8 +273,6 @@ impl Instance { impl Drop for Instance { fn drop(&mut self) { - unsafe { - ffi::ext_sandbox_instance_teardown(self.instance_idx); - } + sandbox::instance_teardown(self.instance_idx); } } diff --git a/core/sr-std/Cargo.toml b/core/sr-std/Cargo.toml index 2a8b7d37ca272..77021af935ae1 100644 --- a/core/sr-std/Cargo.toml +++ b/core/sr-std/Cargo.toml @@ -2,15 +2,8 @@ name = "sr-std" version = "2.0.0" authors = ["Parity Technologies "] -build = "build.rs" edition = "2018" -[build-dependencies] -rustc_version = "0.2.3" - [features] default = ["std"] std = [] -nightly = [] -strict = [] -no_global_allocator = [] diff --git a/core/sr-std/build.rs b/core/sr-std/build.rs deleted file mode 100644 index af9c91db877dd..0000000000000 --- a/core/sr-std/build.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Set a nightly feature - -use rustc_version::{version, version_meta, Channel}; - -fn main() { - // Assert we haven't traveled back in time - assert!(version().unwrap().major >= 1); - - // Set cfg flags depending on release channel - if let Channel::Nightly = version_meta().unwrap().channel { - println!("cargo:rustc-cfg=feature=\"nightly\""); - } -} diff --git a/core/sr-std/with_std.rs b/core/sr-std/with_std.rs index 0d5ee04ed51ab..e41a9d7ad6c5c 100644 --- a/core/sr-std/with_std.rs +++ b/core/sr-std/with_std.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . +pub use std::alloc; +pub use std::any; pub use std::borrow; pub use std::boxed; pub use std::cell; diff --git a/core/sr-std/without_std.rs b/core/sr-std/without_std.rs index 9762c74367198..a35e1395a543e 100755 --- a/core/sr-std/without_std.rs +++ b/core/sr-std/without_std.rs @@ -14,40 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -#[doc(hidden)] pub extern crate alloc; -extern "C" { - fn ext_malloc(size: u32) -> *mut u8; - fn ext_free(ptr: *mut u8); -} - -/// Wasm allocator -pub struct WasmAllocator; - -#[cfg(not(feature = "no_global_allocator"))] -#[global_allocator] -static ALLOCATOR: WasmAllocator = WasmAllocator; - -mod __impl { - use core::alloc::{GlobalAlloc, Layout}; - - use super::WasmAllocator; - - unsafe impl GlobalAlloc for WasmAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - super::ext_malloc(layout.size() as u32) as *mut u8 - } - - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - super::ext_free(ptr as *mut u8) - } - } -} - pub use alloc::boxed; pub use alloc::rc; pub use alloc::vec; +pub use core::any; pub use core::cell; pub use core::clone; pub use core::cmp; diff --git a/core/state-machine/src/basic.rs b/core/state-machine/src/basic.rs index c2d1a0e3950d0..e758b3dd3b814 100644 --- a/core/state-machine/src/basic.rs +++ b/core/state-machine/src/basic.rs @@ -22,7 +22,10 @@ use hash_db::Hasher; use trie::{TrieConfiguration, default_child_trie_root}; use trie::trie_types::Layout; use primitives::{ - storage::{well_known_keys::is_child_storage_key, ChildStorageKey}, + storage::{ + well_known_keys::is_child_storage_key, ChildStorageKey, StorageOverlay, + ChildrenStorageOverlay + }, traits::Externalities, Blake2Hasher, hash::H256, }; use log::warn; @@ -30,16 +33,13 @@ use log::warn; /// Simple HashMap-based Externalities impl. #[derive(Debug)] pub struct BasicExternalities { - top: HashMap, Vec>, - children: HashMap, HashMap, Vec>>, + top: StorageOverlay, + children: ChildrenStorageOverlay, } impl BasicExternalities { /// Create a new instance of `BasicExternalities` - pub fn new( - top: HashMap, Vec>, - children: HashMap, HashMap, Vec>>, - ) -> Self { + pub fn new(top: StorageOverlay, children: ChildrenStorageOverlay) -> Self { BasicExternalities { top, children, @@ -58,6 +58,32 @@ impl BasicExternalities { ) { (self.top, self.children) } + + /// Execute the given closure `f` with the externalities set and initialized with `storage`. + /// + /// Returns the result of the closure and updates `storage` with all changes. + pub fn execute_with_storage( + storage: &mut (StorageOverlay, ChildrenStorageOverlay), + f: impl FnOnce() -> R, + ) -> R { + let mut ext = Self { + top: storage.0.drain().collect(), + children: storage.1.drain().collect(), + }; + + let r = ext.execute_with(f); + + *storage = ext.into_storages(); + + r + } + + /// Execute the given closure while `self` is set as externalities. + /// + /// Returns the result of the given closure. + pub fn execute_with(&mut self, f: impl FnOnce() -> R) -> R { + externalities::set_and_run_with_externalities(self, f) + } } impl PartialEq for BasicExternalities { diff --git a/core/state-machine/src/lib.rs b/core/state-machine/src/lib.rs index 1da9cfb4e7dbe..b4465df31d71f 100644 --- a/core/state-machine/src/lib.rs +++ b/core/state-machine/src/lib.rs @@ -83,7 +83,7 @@ pub enum ExecutionStrategy { NativeWhenPossible, /// Use the given wasm module. AlwaysWasm, - /// Run with both the wasm and the native variant (if compatible). Report any discrepency as an error. + /// Run with both the wasm and the native variant (if compatible). Report any discrepancy as an error. Both, /// First native, then if that fails or is not possible, wasm. NativeElseWasm, @@ -109,7 +109,7 @@ pub enum ExecutionManager { /// trusted to provide all storage or not (i.e. the light client cannot be trusted to provide /// for all storage queries since the storage entries it has come from an external node). AlwaysWasm(BackendTrustLevel), - /// Run with both the wasm and the native variant (if compatible). Call `F` in the case of any discrepency. + /// Run with both the wasm and the native variant (if compatible). Call `F` in the case of any discrepancy. Both(F), /// First native, then if that fails or is not possible, wasm. NativeElseWasm, diff --git a/core/state-machine/src/testing.rs b/core/state-machine/src/testing.rs index 8253f20c8bd01..61b338bc81acd 100644 --- a/core/state-machine/src/testing.rs +++ b/core/state-machine/src/testing.rs @@ -46,7 +46,6 @@ pub struct TestExternalities=Blake2Hasher, N: ChangesTrieBlo } impl, N: ChangesTrieBlockNumber> TestExternalities { - /// Get externalities implementation. pub fn ext(&mut self) -> Ext, ChangesTrieInMemoryStorage> { Ext::new( diff --git a/core/test-runtime/Cargo.toml b/core/test-runtime/Cargo.toml index 4e7c3f8bca450..189a46eb7eebe 100644 --- a/core/test-runtime/Cargo.toml +++ b/core/test-runtime/Cargo.toml @@ -26,6 +26,7 @@ substrate-trie = { path = "../trie", default-features = false } trie-db = { version = "0.15.2", default-features = false } memory-db = { version = "0.15.2", default-features = false } offchain-primitives = { package = "substrate-offchain-primitives", path = "../offchain/primitives", default-features = false} +runtime-interface = { package = "substrate-runtime-interface", path = "../runtime-interface", default-features = false} executive = { package = "srml-executive", path = "../../srml/executive", default-features = false } cfg-if = "0.1.10" srml-babe = { path = "../../srml/babe", default-features = false } @@ -72,4 +73,5 @@ std = [ "srml-system-rpc-runtime-api/std", "app-crypto/std", "session/std", + "runtime-interface/std", ] diff --git a/core/test-runtime/src/genesismap.rs b/core/test-runtime/src/genesismap.rs index 909da32ee08b7..79cba52323e58 100644 --- a/core/test-runtime/src/genesismap.rs +++ b/core/test-runtime/src/genesismap.rs @@ -17,7 +17,7 @@ //! Tool for creating the genesis block. use std::collections::HashMap; -use runtime_io::{blake2_256, twox_128}; +use runtime_io::hashing::{blake2_256, twox_128}; use super::{AuthorityId, AccountId, WASM_BINARY, system}; use codec::{Encode, KeyedVec, Joiner}; use primitives::{ChangesTrieConfiguration, map, storage::well_known_keys}; diff --git a/core/test-runtime/src/lib.rs b/core/test-runtime/src/lib.rs index 7fac0e702e6ca..24443233720a1 100644 --- a/core/test-runtime/src/lib.rs +++ b/core/test-runtime/src/lib.rs @@ -408,7 +408,8 @@ fn benchmark_add_one(i: u64) -> u64 { /// The `benchmark_add_one` function as function pointer. #[cfg(not(feature = "std"))] -static BENCHMARK_ADD_ONE: runtime_io::ExchangeableFunction u64> = runtime_io::ExchangeableFunction::new(benchmark_add_one); +static BENCHMARK_ADD_ONE: runtime_interface::wasm::ExchangeableFunction u64> = + runtime_interface::wasm::ExchangeableFunction::new(benchmark_add_one); fn code_using_trie() -> u64 { let pairs = [ @@ -626,7 +627,7 @@ cfg_if! { impl offchain_primitives::OffchainWorkerApi for Runtime { fn offchain_worker(block: u64) { let ex = Extrinsic::IncludeData(block.encode()); - runtime_io::submit_transaction(ex.encode()).unwrap(); + runtime_io::offchain::submit_transaction(ex.encode()).unwrap(); } } @@ -842,7 +843,7 @@ cfg_if! { impl offchain_primitives::OffchainWorkerApi for Runtime { fn offchain_worker(block: u64) { let ex = Extrinsic::IncludeData(block.encode()); - runtime_io::submit_transaction(ex.encode()).unwrap() + runtime_io::offchain::submit_transaction(ex.encode()).unwrap() } } @@ -893,10 +894,10 @@ fn test_sr25519_crypto() -> (sr25519::AppSignature, sr25519::AppPublic) { fn test_read_storage() { const KEY: &[u8] = b":read_storage"; - runtime_io::set_storage(KEY, b"test"); + runtime_io::storage::set(KEY, b"test"); let mut v = [0u8; 4]; - let r = runtime_io::read_storage( + let r = runtime_io::storage::read( KEY, &mut v, 0 @@ -905,7 +906,7 @@ fn test_read_storage() { assert_eq!(&v, b"test"); let mut v = [0u8; 4]; - let r = runtime_io::read_storage(KEY, &mut v, 8); + let r = runtime_io::storage::read(KEY, &mut v, 8); assert_eq!(r, Some(4)); assert_eq!(&v, &[0, 0, 0, 0]); } @@ -913,10 +914,10 @@ fn test_read_storage() { fn test_read_child_storage() { const CHILD_KEY: &[u8] = b":child_storage:default:read_child_storage"; const KEY: &[u8] = b":read_child_storage"; - runtime_io::set_child_storage(CHILD_KEY, KEY, b"test"); + runtime_io::storage::child_set(CHILD_KEY, KEY, b"test"); let mut v = [0u8; 4]; - let r = runtime_io::read_child_storage( + let r = runtime_io::storage::child_read( CHILD_KEY, KEY, &mut v, @@ -926,7 +927,7 @@ fn test_read_child_storage() { assert_eq!(&v, b"test"); let mut v = [0u8; 4]; - let r = runtime_io::read_child_storage(CHILD_KEY, KEY, &mut v, 8); + let r = runtime_io::storage::child_read(CHILD_KEY, KEY, &mut v, 8); assert_eq!(r, Some(4)); assert_eq!(&v, &[0, 0, 0, 0]); } diff --git a/core/test-runtime/src/system.rs b/core/test-runtime/src/system.rs index ba0f25590d793..dcb9aa4f32583 100644 --- a/core/test-runtime/src/system.rs +++ b/core/test-runtime/src/system.rs @@ -18,8 +18,11 @@ //! and depositing logs. use rstd::prelude::*; -use runtime_io::{storage_root, storage_changes_root, blake2_256}; -use runtime_support::storage::{self, StorageMap}; +use runtime_io::{ + storage::root as storage_root, storage::changes_root as storage_changes_root, + hashing::blake2_256, +}; +use runtime_support::storage; use runtime_support::{decl_storage, decl_module}; use sr_primitives::{ traits::{Hash as HashT, BlakeTwo256, Header as _}, generic, ApplyError, ApplyResult, @@ -327,7 +330,7 @@ mod tests { use crate::{Header, Transfer, WASM_BINARY}; use primitives::{NeverNativeValue, map, traits::CodeExecutor}; use substrate_executor::{NativeExecutor, WasmExecutionMethod, native_executor_instance}; - use runtime_io::twox_128; + use runtime_io::hashing::twox_128; // Declare an instance of the native executor dispatch for the test runtime. native_executor_instance!( diff --git a/core/wasm-interface/Cargo.toml b/core/wasm-interface/Cargo.toml index b8169031d0bb6..dcda5061c91a1 100644 --- a/core/wasm-interface/Cargo.toml +++ b/core/wasm-interface/Cargo.toml @@ -6,3 +6,4 @@ edition = "2018" [dependencies] wasmi = "0.5.1" +impl-trait-for-tuples = "0.1.2" diff --git a/core/wasm-interface/src/lib.rs b/core/wasm-interface/src/lib.rs index 83689ddc819a8..b2d57d080d533 100644 --- a/core/wasm-interface/src/lib.rs +++ b/core/wasm-interface/src/lib.rs @@ -131,6 +131,12 @@ impl From> for u32 { } } +impl From> for u64 { + fn from(ptr: Pointer) -> Self { + u64::from(ptr.ptr) + } +} + impl From> for usize { fn from(ptr: Pointer) -> Self { ptr.ptr as _ @@ -183,7 +189,7 @@ impl Signature { } /// Something that provides a function implementation on the host for a wasm function. -pub trait Function { +pub trait Function: std::panic::RefUnwindSafe + Send + Sync { /// Returns the name of this function. fn name(&self) -> &str; /// Returns the signature of this function. @@ -196,6 +202,12 @@ pub trait Function { ) -> Result>; } +impl PartialEq for dyn Function { + fn eq(&self, other: &Self) -> bool { + other.name() == self.name() && other.signature() == self.signature() + } +} + /// Context used by `Function` to interact with the allocator and the memory of the wasm instance. pub trait FunctionContext { /// Read memory from `address` into a vector. @@ -266,9 +278,20 @@ pub trait Sandbox { } /// Something that provides implementations for host functions. -pub trait HostFunctions { - /// Returns all host functions. - fn functions() -> &'static [&'static dyn Function]; +pub trait HostFunctions: 'static { + /// Returns the host functions `Self` provides. + fn host_functions() -> Vec<&'static dyn Function>; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl HostFunctions for Tuple { + fn host_functions() -> Vec<&'static dyn Function> { + let mut host_functions = Vec::new(); + + for_tuples!( #( host_functions.extend(Tuple::host_functions()); )* ); + + host_functions + } } /// Something that can be converted into a wasm compatible `Value`. @@ -311,12 +334,58 @@ macro_rules! impl_into_and_from_value { } impl_into_and_from_value! { + u8, I32, + u16, I32, u32, I32, - i32, I32, u64, I64, + i8, I32, + i16, I32, + i32, I32, i64, I64, } +/// Something that can write a primitive to wasm memory location. +pub trait WritePrimitive { + /// Write the given value `t` to the given memory location `ptr`. + fn write_primitive(&mut self, ptr: Pointer, t: T) -> Result<()>; +} + +impl WritePrimitive for &mut dyn FunctionContext { + fn write_primitive(&mut self, ptr: Pointer, t: u32) -> Result<()> { + let r = t.to_le_bytes(); + self.write_memory(ptr.cast(), &r) + } +} + +impl WritePrimitive for &mut dyn FunctionContext { + fn write_primitive(&mut self, ptr: Pointer, t: u64) -> Result<()> { + let r = t.to_le_bytes(); + self.write_memory(ptr.cast(), &r) + } +} + +/// Something that can read a primitive from a wasm memory location. +pub trait ReadPrimitive { + /// Read a primitive from the given memory location `ptr`. + fn read_primitive(&self, ptr: Pointer) -> Result; +} + +impl ReadPrimitive for &mut dyn FunctionContext { + fn read_primitive(&self, ptr: Pointer) -> Result { + let mut r = [0u8; 4]; + self.read_memory_into(ptr.cast(), &mut r)?; + Ok(u32::from_le_bytes(r)) + } +} + +impl ReadPrimitive for &mut dyn FunctionContext { + fn read_primitive(&self, ptr: Pointer) -> Result { + let mut r = [0u8; 8]; + self.read_memory_into(ptr.cast(), &mut r)?; + Ok(u64::from_le_bytes(r)) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/node-template/runtime/src/lib.rs b/node-template/runtime/src/lib.rs index 071e07a52e8b5..512f0020a5967 100644 --- a/node-template/runtime/src/lib.rs +++ b/node-template/runtime/src/lib.rs @@ -349,7 +349,6 @@ impl_runtime_apis! { impl substrate_session::SessionKeys for Runtime { fn generate_session_keys(seed: Option>) -> Vec { - let seed = seed.as_ref().map(|s| rstd::str::from_utf8(&s).expect("Seed is an utf8 string")); opaque::SessionKeys::generate(seed) } } diff --git a/node/cli/Cargo.toml b/node/cli/Cargo.toml index 931488d3bcaeb..20379d52e768d 100644 --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -39,7 +39,7 @@ babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../ grandpa_primitives = { package = "substrate-finality-grandpa-primitives", path = "../../core/finality-grandpa/primitives" } # core dependencies -sr-io = { path = "../../core/sr-io" } +runtime-io = { package = "sr-io", path = "../../core/sr-io" } client = { package = "substrate-client", path = "../../core/client" } inherents = { package = "substrate-inherents", path = "../../core/inherents" } chain-spec = { package = "substrate-chain-spec", path = "../../core/chain-spec" } diff --git a/node/cli/src/factory_impl.rs b/node/cli/src/factory_impl.rs index 48fb7b237f1e6..aaf74a0602082 100644 --- a/node/cli/src/factory_impl.rs +++ b/node/cli/src/factory_impl.rs @@ -247,7 +247,7 @@ fn sign( let payload = (xt.function, extra.clone(), additional_signed); let signature = payload.using_encoded(|b| { if b.len() > 256 { - key.sign(&sr_io::blake2_256(b)) + key.sign(&runtime_io::hashing::blake2_256(b)) } else { key.sign(b) } diff --git a/node/cli/src/service.rs b/node/cli/src/service.rs index c5780d9f3556c..52ad35fe7b315 100644 --- a/node/cli/src/service.rs +++ b/node/cli/src/service.rs @@ -390,7 +390,7 @@ mod tests { origin: BlockOrigin::File, justification: Vec::new(), internal_justification: Vec::new(), - finalized: true, + finalized: false, body: Some(block.extrinsics), header: block.header, auxiliary: Vec::new(), @@ -520,7 +520,7 @@ mod tests { justification: None, post_digests: vec![item], body: Some(new_body), - finalized: true, + finalized: false, auxiliary: Vec::new(), fork_choice: ForkChoiceStrategy::LongestChain, allow_missing_state: false, diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 8dfa52b23f3b8..344c7ef731fcb 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -698,8 +698,6 @@ impl_runtime_apis! { impl substrate_session::SessionKeys for Runtime { fn generate_session_keys(seed: Option>) -> Vec { - let seed = seed.as_ref().map(|s| rstd::str::from_utf8(&s) - .expect("Seed is an utf8 string")); SessionKeys::generate(seed) } } diff --git a/node/testing/Cargo.toml b/node/testing/Cargo.toml index 8a4c08ed11bd2..a3382cd53540e 100644 --- a/node/testing/Cargo.toml +++ b/node/testing/Cargo.toml @@ -17,7 +17,7 @@ node-primitives = { path = "../primitives" } node-runtime = { path = "../runtime" } codec = { package = "parity-scale-codec", version = "1.0.0" } primitives = { package = "substrate-primitives", path = "../../core/primitives" } -sr-io = { path = "../../core/sr-io" } +runtime-io = { package = "sr-io", path = "../../core/sr-io" } sr-primitives = { path = "../../core/sr-primitives" } runtime_support = { package = "srml-support", path = "../../srml/support" } session = { package = "srml-session", path = "../../srml/session" } diff --git a/node/testing/src/keyring.rs b/node/testing/src/keyring.rs index ca44a53880fc1..618c813fb529a 100644 --- a/node/testing/src/keyring.rs +++ b/node/testing/src/keyring.rs @@ -83,7 +83,7 @@ pub fn sign(xt: CheckedExtrinsic, version: u32, genesis_hash: [u8; 32]) -> Unche let key = AccountKeyring::from_account_id(&signed).unwrap(); let signature = payload.using_encoded(|b| { if b.len() > 256 { - key.sign(&sr_io::blake2_256(b)) + key.sign(&runtime_io::hashing::blake2_256(b)) } else { key.sign(b) } diff --git a/srml/babe/src/lib.rs b/srml/babe/src/lib.rs index e12e123daf6f2..c5541258db6cf 100644 --- a/srml/babe/src/lib.rs +++ b/srml/babe/src/lib.rs @@ -597,7 +597,7 @@ fn compute_randomness( s.extend_from_slice(&vrf_output[..]); } - runtime_io::blake2_256(&s) + runtime_io::hashing::blake2_256(&s) } impl ProvideInherent for Module { diff --git a/srml/contracts/src/account_db.rs b/srml/contracts/src/account_db.rs index 50bd1fd40e97d..5aa3a64fd9bc8 100644 --- a/srml/contracts/src/account_db.rs +++ b/srml/contracts/src/account_db.rs @@ -24,7 +24,7 @@ use crate::exec::StorageKey; use rstd::cell::RefCell; use rstd::collections::btree_map::{BTreeMap, Entry}; use rstd::prelude::*; -use runtime_io::blake2_256; +use runtime_io::hashing::blake2_256; use sr_primitives::traits::{Bounded, Zero}; use support::traits::{Currency, Get, Imbalance, SignedImbalance, UpdateBalanceOutcome}; use support::{storage::child, StorageMap}; diff --git a/srml/contracts/src/lib.rs b/srml/contracts/src/lib.rs index df38747cc5d52..05e22aeae2fad 100644 --- a/srml/contracts/src/lib.rs +++ b/srml/contracts/src/lib.rs @@ -111,7 +111,7 @@ use serde::{Serialize, Deserialize}; use primitives::crypto::UncheckedFrom; use rstd::{prelude::*, marker::PhantomData, fmt::Debug}; use codec::{Codec, Encode, Decode}; -use runtime_io::blake2_256; +use runtime_io::hashing::blake2_256; use sr_primitives::{ traits::{Hash, StaticLookup, Zero, MaybeSerializeDeserialize, Member, SignedExtension}, weights::DispatchInfo, @@ -803,7 +803,7 @@ impl Module { let tombstone = >::new( // This operation is cheap enough because last_write (delta not included) // is not this block as it has been checked earlier. - &runtime_io::child_storage_root(&origin_contract.trie_id)[..], + &runtime_io::storage::child_root(&origin_contract.trie_id)[..], code_hash, ); diff --git a/srml/contracts/src/rent.rs b/srml/contracts/src/rent.rs index 6647f8963165d..e286ce307fcc8 100644 --- a/srml/contracts/src/rent.rs +++ b/srml/contracts/src/rent.rs @@ -99,7 +99,7 @@ fn try_evict_or_and_pay_rent( if balance < subsistence_threshold { // The contract cannot afford to leave a tombstone, so remove the contract info altogether. >::remove(account); - runtime_io::kill_child_storage(&contract.trie_id); + runtime_io::storage::child_storage_kill(&contract.trie_id); return (RentOutcome::Evicted, None); } @@ -146,7 +146,7 @@ fn try_evict_or_and_pay_rent( // threshold, so it leaves a tombstone. // Note: this operation is heavy. - let child_storage_root = runtime_io::child_storage_root(&contract.trie_id); + let child_storage_root = runtime_io::storage::child_root(&contract.trie_id); let tombstone = >::new( &child_storage_root[..], @@ -155,7 +155,7 @@ fn try_evict_or_and_pay_rent( let tombstone_info = ContractInfo::Tombstone(tombstone); >::insert(account, &tombstone_info); - runtime_io::kill_child_storage(&contract.trie_id); + runtime_io::storage::child_storage_kill(&contract.trie_id); return (RentOutcome::Evicted, Some(tombstone_info)); } diff --git a/srml/contracts/src/wasm/mod.rs b/srml/contracts/src/wasm/mod.rs index 4fa9412bc1769..42ee6ee0a2c5a 100644 --- a/srml/contracts/src/wasm/mod.rs +++ b/srml/contracts/src/wasm/mod.rs @@ -58,7 +58,7 @@ pub struct PrefabWasmModule { /// Wasm executable loaded by `WasmLoader` and executed by `WasmVm`. pub struct WasmExecutable { - entrypoint_name: &'static [u8], + entrypoint_name: &'static str, prefab_module: PrefabWasmModule, } @@ -79,14 +79,14 @@ impl<'a, T: Trait> crate::exec::Loader for WasmLoader<'a> { fn load_init(&self, code_hash: &CodeHash) -> Result { let prefab_module = load_code::(code_hash, self.schedule)?; Ok(WasmExecutable { - entrypoint_name: b"deploy", + entrypoint_name: "deploy", prefab_module, }) } fn load_main(&self, code_hash: &CodeHash) -> Result { let prefab_module = load_code::(code_hash, self.schedule)?; Ok(WasmExecutable { - entrypoint_name: b"call", + entrypoint_name: "call", prefab_module, }) } @@ -406,7 +406,7 @@ mod tests { let exec = WasmExecutable { // Use a "call" convention. - entrypoint_name: b"call", + entrypoint_name: "call", prefab_module, }; diff --git a/srml/evm/src/backend.rs b/srml/evm/src/backend.rs index 6de5429dde478..1f3dfe309b4ee 100644 --- a/srml/evm/src/backend.rs +++ b/srml/evm/src/backend.rs @@ -96,7 +96,7 @@ impl<'vicinity, T: Trait> BackendT for Backend<'vicinity, T> { } fn chain_id(&self) -> U256 { - U256::from(runtime_io::chain_id()) + U256::from(runtime_io::misc::chain_id()) } fn exists(&self, _address: H160) -> bool { diff --git a/srml/im-online/src/lib.rs b/srml/im-online/src/lib.rs index 3d8cf99bc5fca..1a32f1098af62 100644 --- a/srml/im-online/src/lib.rs +++ b/srml/im-online/src/lib.rs @@ -285,7 +285,7 @@ decl_module! { debug::RuntimeLogger::init(); // Only send messages if we are a potential validator. - if runtime_io::is_validator() { + if runtime_io::offchain::is_validator() { Self::offchain(now); } } @@ -408,7 +408,8 @@ impl Module { continue; } - let network_state = runtime_io::network_state().map_err(|_| OffchainErr::NetworkState)?; + let network_state = runtime_io::offchain::network_state() + .map_err(|_| OffchainErr::NetworkState)?; let heartbeat_data = Heartbeat { block_number, network_state, @@ -452,10 +453,10 @@ impl Module { done, gossipping_at, }; - runtime_io::local_storage_compare_and_set( + runtime_io::offchain::local_storage_compare_and_set( StorageKind::PERSISTENT, DB_KEY, - curr_worker_status.as_ref().map(Vec::as_slice), + curr_worker_status, &enc.encode() ) } @@ -468,8 +469,7 @@ impl Module { done, gossipping_at, }; - runtime_io::local_storage_set( - StorageKind::PERSISTENT, DB_KEY, &enc.encode()); + runtime_io::offchain::local_storage_set(StorageKind::PERSISTENT, DB_KEY, &enc.encode()); } // Checks if a heartbeat gossip already occurred at this block number. @@ -479,7 +479,7 @@ impl Module { now: T::BlockNumber, next_gossip: T::BlockNumber, ) -> Result<(Option>, bool), OffchainErr> { - let last_gossip = runtime_io::local_storage_get(StorageKind::PERSISTENT, DB_KEY); + let last_gossip = runtime_io::offchain::local_storage_get(StorageKind::PERSISTENT, DB_KEY); match last_gossip { Some(last) => { let worker_status: WorkerStatus = Decode::decode(&mut &last[..]) diff --git a/srml/im-online/src/tests.rs b/srml/im-online/src/tests.rs index f3a0895d5fce6..382eb4f1d1f04 100644 --- a/srml/im-online/src/tests.rs +++ b/srml/im-online/src/tests.rs @@ -213,7 +213,7 @@ fn should_generate_heartbeats() { assert_eq!(heartbeat, Heartbeat { block_number: 2, - network_state: runtime_io::network_state().unwrap(), + network_state: runtime_io::offchain::network_state().unwrap(), session_index: 2, authority_index: 2, }); @@ -316,7 +316,7 @@ fn should_not_send_a_report_if_already_online() { assert_eq!(heartbeat, Heartbeat { block_number: 4, - network_state: runtime_io::network_state().unwrap(), + network_state: runtime_io::offchain::network_state().unwrap(), session_index: 2, authority_index: 0, }); diff --git a/srml/support/Cargo.toml b/srml/support/Cargo.toml index 46ebebc7d3bf4..55ea65d55f853 100644 --- a/srml/support/Cargo.toml +++ b/srml/support/Cargo.toml @@ -17,6 +17,7 @@ inherents = { package = "substrate-inherents", path = "../../core/inherents", de srml-support-procedural = { package = "srml-support-procedural", path = "./procedural" } paste = "0.1.6" once_cell = { version = "0.2.4", default-features = false, optional = true } +state-machine = { package = "substrate-state-machine", path = "../../core/state-machine", optional = true } bitmask = { version = "0.5.0", default-features = false } impl-trait-for-tuples = "0.1.3" @@ -36,6 +37,7 @@ std = [ "sr-primitives/std", "srml-metadata/std", "inherents/std", + "state-machine", ] nightly = [] strict = [] diff --git a/srml/support/procedural/src/storage/genesis_config/mod.rs b/srml/support/procedural/src/storage/genesis_config/mod.rs index 109957926a775..c222644f78ae3 100644 --- a/srml/support/procedural/src/storage/genesis_config/mod.rs +++ b/srml/support/procedural/src/storage/genesis_config/mod.rs @@ -158,7 +158,7 @@ fn impl_build_storage( #scrate::sr_primitives::ChildrenStorageOverlay, ), ) -> std::result::Result<(), String> #fn_where_clause { - #scrate::with_storage(tuple_storage, || { + #scrate::BasicExternalities::execute_with_storage(tuple_storage, || { #( #builder_blocks )* Ok(()) }) diff --git a/srml/support/src/debug.rs b/srml/support/src/debug.rs index b8c7457eb237c..ee187edb396d8 100644 --- a/srml/support/src/debug.rs +++ b/srml/support/src/debug.rs @@ -155,7 +155,7 @@ impl fmt::Write for Writer { impl Writer { /// Print the content of this `Writer` out. pub fn print(&self) { - runtime_io::print_utf8(&self.0) + runtime_io::misc::print_utf8(&self.0) } } @@ -204,9 +204,9 @@ impl log::Log for RuntimeLogger { let mut w = Writer::default(); let _ = core::write!(&mut w, "{}", record.args()); - runtime_io::log( + runtime_io::logging::log( record.level().into(), - record.target().as_bytes(), + record.target(), &w.0, ); } diff --git a/srml/support/src/hash.rs b/srml/support/src/hash.rs index cbd78f603241e..c2b63a84db346 100644 --- a/srml/support/src/hash.rs +++ b/srml/support/src/hash.rs @@ -18,7 +18,7 @@ use codec::Codec; use rstd::prelude::Vec; -use runtime_io::{blake2_128, blake2_256, twox_64, twox_128, twox_256}; +use runtime_io::hashing::{blake2_128, blake2_256, twox_64, twox_128, twox_256}; // This trait must be kept coherent with srml-support-procedural HasherKind usage pub trait Hashable: Sized { diff --git a/srml/support/src/lib.rs b/srml/support/src/lib.rs index cfe6487203ddd..c4a4911158f30 100644 --- a/srml/support/src/lib.rs +++ b/srml/support/src/lib.rs @@ -37,9 +37,9 @@ pub use once_cell; pub use paste; #[cfg(feature = "std")] #[doc(hidden)] -pub use runtime_io::with_storage; +pub use state_machine::BasicExternalities; #[doc(hidden)] -pub use runtime_io::storage_root; +pub use runtime_io::storage::root as storage_root; #[doc(hidden)] pub use sr_primitives::RuntimeDebug; diff --git a/srml/support/src/storage/child.rs b/srml/support/src/storage/child.rs index 1d6ee7a6f1a61..d43c2e896f3d8 100644 --- a/srml/support/src/storage/child.rs +++ b/srml/support/src/storage/child.rs @@ -26,7 +26,7 @@ use codec::{Codec, Encode, Decode}; /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. pub fn get(storage_key: &[u8], key: &[u8]) -> Option { - runtime_io::child_storage(storage_key, key).map(|v| { + runtime_io::storage::child_get(storage_key, key).map(|v| { Decode::decode(&mut &v[..]).expect("storage is not null, therefore must be a valid type") }) } @@ -45,13 +45,17 @@ pub fn get_or(storage_key: &[u8], key: &[u8], default_value: /// Return the value of the item in storage under `key`, or `default_value()` if there is no /// explicit entry. -pub fn get_or_else T>(storage_key: &[u8], key: &[u8], default_value: F) -> T { +pub fn get_or_else T>( + storage_key: &[u8], + key: &[u8], + default_value: F, +) -> T { get(storage_key, key).unwrap_or_else(default_value) } /// Put `value` in storage under `key`. pub fn put(storage_key: &[u8], key: &[u8], value: &T) { - value.using_encoded(|slice| runtime_io::set_child_storage(storage_key, key, slice)); + value.using_encoded(|slice| runtime_io::storage::child_set(storage_key, key, slice)); } /// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise. @@ -77,31 +81,35 @@ pub fn take_or(storage_key: &[u8],key: &[u8], default_value: T /// Return the value of the item in storage under `key`, or `default_value()` if there is no /// explicit entry. Ensure there is no explicit entry on return. -pub fn take_or_else T>(storage_key: &[u8], key: &[u8], default_value: F) -> T { +pub fn take_or_else T>( + storage_key: &[u8], + key: &[u8], + default_value: F, +) -> T { take(storage_key, key).unwrap_or_else(default_value) } /// Check to see if `key` has an explicit entry in storage. pub fn exists(storage_key: &[u8], key: &[u8]) -> bool { - runtime_io::read_child_storage(storage_key, key, &mut [0;0][..], 0).is_some() + runtime_io::storage::child_read(storage_key, key, &mut [0;0][..], 0).is_some() } /// Remove all `storage_key` key/values pub fn kill_storage(storage_key: &[u8]) { - runtime_io::kill_child_storage(storage_key) + runtime_io::storage::child_storage_kill(storage_key) } /// Ensure `key` has no explicit entry in storage. pub fn kill(storage_key: &[u8], key: &[u8]) { - runtime_io::clear_child_storage(storage_key, key); + runtime_io::storage::child_clear(storage_key, key); } /// Get a Vec of bytes from storage. pub fn get_raw(storage_key: &[u8], key: &[u8]) -> Option> { - runtime_io::child_storage(storage_key, key) + runtime_io::storage::child_get(storage_key, key) } /// Put a raw byte slice into storage. pub fn put_raw(storage_key: &[u8], key: &[u8], value: &[u8]) { - runtime_io::set_child_storage(storage_key, key, value) + runtime_io::storage::child_set(storage_key, key, value) } diff --git a/srml/support/src/storage/unhashed.rs b/srml/support/src/storage/unhashed.rs index 6397fd39fcd2d..803a512b2a2c4 100644 --- a/srml/support/src/storage/unhashed.rs +++ b/srml/support/src/storage/unhashed.rs @@ -21,7 +21,7 @@ use codec::{Encode, Decode}; /// Return the value of the item in storage under `key`, or `None` if there is no explicit entry. pub fn get(key: &[u8]) -> Option { - runtime_io::storage(key).map(|val| { + runtime_io::storage::get(key).map(|val| { Decode::decode(&mut &val[..]).expect("storage is not null, therefore must be a valid type") }) } @@ -46,7 +46,7 @@ pub fn get_or_else T>(key: &[u8], default_valu /// Put `value` in storage under `key`. pub fn put(key: &[u8], value: &T) { - value.using_encoded(|slice| runtime_io::set_storage(key, slice)); + value.using_encoded(|slice| runtime_io::storage::set(key, slice)); } /// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise. @@ -78,25 +78,25 @@ pub fn take_or_else T>(key: &[u8], default_val /// Check to see if `key` has an explicit entry in storage. pub fn exists(key: &[u8]) -> bool { - runtime_io::read_storage(key, &mut [0;0][..], 0).is_some() + runtime_io::storage::read(key, &mut [0;0][..], 0).is_some() } /// Ensure `key` has no explicit entry in storage. pub fn kill(key: &[u8]) { - runtime_io::clear_storage(key); + runtime_io::storage::clear(key); } /// Ensure keys with the given `prefix` have no entries in storage. pub fn kill_prefix(prefix: &[u8]) { - runtime_io::clear_prefix(prefix); + runtime_io::storage::clear_prefix(prefix); } /// Get a Vec of bytes from storage. pub fn get_raw(key: &[u8]) -> Option> { - runtime_io::storage(key) + runtime_io::storage::get(key) } /// Put a raw byte slice into storage. pub fn put_raw(key: &[u8], value: &[u8]) { - runtime_io::set_storage(key, value) + runtime_io::storage::set(key, value) } diff --git a/srml/support/test/Cargo.toml b/srml/support/test/Cargo.toml index a5e375b5c6b93..cbca8a859c187 100644 --- a/srml/support/test/Cargo.toml +++ b/srml/support/test/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" serde = { version = "1.0.101", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } runtime-io ={ package = "sr-io", path = "../../../core/sr-io", default-features = false } +state-machine ={ package = "substrate-state-machine", path = "../../../core/state-machine", optional = true } support = { package = "srml-support", version = "2", path = "../", default-features = false } inherents = { package = "substrate-inherents", path = "../../../core/inherents", default-features = false } sr-primitives = { package = "sr-primitives", path = "../../../core/sr-primitives", default-features = false } @@ -25,4 +26,5 @@ std = [ "inherents/std", "primitives/std", "sr-primitives/std", + "state-machine", ] diff --git a/srml/support/test/tests/decl_storage.rs b/srml/support/test/tests/decl_storage.rs index c9dd96791b164..396288d3be31d 100644 --- a/srml/support/test/tests/decl_storage.rs +++ b/srml/support/test/tests/decl_storage.rs @@ -19,9 +19,8 @@ #[allow(dead_code)] mod tests { use support::metadata::*; - use support::metadata::StorageHasher; - use support::rstd::marker::PhantomData; - use support::codec::{Encode, Decode, EncodeLike}; + use std::marker::PhantomData; + use codec::{Encode, Decode, EncodeLike}; support::decl_module! { pub struct Module for enum Call where origin: T::Origin {} diff --git a/srml/support/test/tests/final_keys.rs b/srml/support/test/tests/final_keys.rs index 44a6b540a7a0e..56049ecf378cd 100644 --- a/srml/support/test/tests/final_keys.rs +++ b/srml/support/test/tests/final_keys.rs @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use runtime_io::with_storage; use support::storage::unhashed; use codec::Encode; use support::{StorageDoubleMap, StorageLinkedMap, StorageMap, StorageValue}; +use runtime_io::{TestExternalities, hashing}; mod no_instance { use codec::{Encode, Decode, EncodeLike}; @@ -87,141 +87,141 @@ mod instance { #[test] fn final_keys_no_instance() { - with_storage(&mut Default::default(), || { + TestExternalities::default().execute_with(|| { no_instance::Value::put(1); - assert_eq!(unhashed::get::(&runtime_io::twox_128(b"FinalKeysNone Value")), Some(1u32)); + assert_eq!(unhashed::get::(&hashing::twox_128(b"FinalKeysNone Value")), Some(1u32)); no_instance::Map::insert(1, 2); let mut k = b"FinalKeysNone Map".to_vec(); k.extend(1u32.encode()); - assert_eq!(unhashed::get::(&runtime_io::blake2_256(&k)), Some(2u32)); + assert_eq!(unhashed::get::(&hashing::blake2_256(&k)), Some(2u32)); no_instance::Map2::insert(1, 2); let mut k = b"FinalKeysNone Map2".to_vec(); k.extend(1u32.encode()); - assert_eq!(unhashed::get::(&runtime_io::twox_128(&k)), Some(2u32)); + assert_eq!(unhashed::get::(&hashing::twox_128(&k)), Some(2u32)); let head = b"head of FinalKeysNone LinkedMap".to_vec(); - assert_eq!(unhashed::get::(&runtime_io::blake2_256(&head)), None); + assert_eq!(unhashed::get::(&hashing::blake2_256(&head)), None); no_instance::LinkedMap::insert(1, 2); let mut k = b"FinalKeysNone LinkedMap".to_vec(); k.extend(1u32.encode()); - assert_eq!(unhashed::get::(&runtime_io::blake2_256(&k)), Some(2u32)); - assert_eq!(unhashed::get::(&runtime_io::blake2_256(&head)), Some(1u32)); + assert_eq!(unhashed::get::(&hashing::blake2_256(&k)), Some(2u32)); + assert_eq!(unhashed::get::(&hashing::blake2_256(&head)), Some(1u32)); no_instance::LinkedMap2::insert(1, 2); let mut k = b"FinalKeysNone LinkedMap2".to_vec(); k.extend(1u32.encode()); - assert_eq!(unhashed::get::(&runtime_io::twox_128(&k)), Some(2u32)); + assert_eq!(unhashed::get::(&hashing::twox_128(&k)), Some(2u32)); no_instance::DoubleMap::insert(&1, &2, &3); let mut k = b"FinalKeysNone DoubleMap".to_vec(); k.extend(1u32.encode()); - let mut k = runtime_io::blake2_256(&k).to_vec(); - k.extend(&runtime_io::blake2_256(&2u32.encode())); + let mut k = hashing::blake2_256(&k).to_vec(); + k.extend(&hashing::blake2_256(&2u32.encode())); assert_eq!(unhashed::get::(&k), Some(3u32)); no_instance::DoubleMap2::insert(&1, &2, &3); let mut k = b"FinalKeysNone DoubleMap2".to_vec(); k.extend(1u32.encode()); - let mut k = runtime_io::twox_128(&k).to_vec(); - k.extend(&runtime_io::blake2_128(&2u32.encode())); + let mut k = hashing::twox_128(&k).to_vec(); + k.extend(&hashing::blake2_128(&2u32.encode())); assert_eq!(unhashed::get::(&k), Some(3u32)); }); } #[test] fn final_keys_default_instance() { - with_storage(&mut Default::default(), || { + TestExternalities::default().execute_with(|| { >::put(1); - assert_eq!(unhashed::get::(&runtime_io::twox_128(b"FinalKeysSome Value")), Some(1u32)); + assert_eq!(unhashed::get::(&hashing::twox_128(b"FinalKeysSome Value")), Some(1u32)); >::insert(1, 2); let mut k = b"FinalKeysSome Map".to_vec(); k.extend(1u32.encode()); - assert_eq!(unhashed::get::(&runtime_io::blake2_256(&k)), Some(2u32)); + assert_eq!(unhashed::get::(&hashing::blake2_256(&k)), Some(2u32)); >::insert(1, 2); let mut k = b"FinalKeysSome Map2".to_vec(); k.extend(1u32.encode()); - assert_eq!(unhashed::get::(&runtime_io::twox_128(&k)), Some(2u32)); + assert_eq!(unhashed::get::(&hashing::twox_128(&k)), Some(2u32)); let head = b"head of FinalKeysSome LinkedMap".to_vec(); - assert_eq!(unhashed::get::(&runtime_io::blake2_256(&head)), None); + assert_eq!(unhashed::get::(&hashing::blake2_256(&head)), None); >::insert(1, 2); let mut k = b"FinalKeysSome LinkedMap".to_vec(); k.extend(1u32.encode()); - assert_eq!(unhashed::get::(&runtime_io::blake2_256(&k)), Some(2u32)); - assert_eq!(unhashed::get::(&runtime_io::blake2_256(&head)), Some(1u32)); + assert_eq!(unhashed::get::(&hashing::blake2_256(&k)), Some(2u32)); + assert_eq!(unhashed::get::(&hashing::blake2_256(&head)), Some(1u32)); < instance::LinkedMap2>::insert(1, 2); let mut k = b"FinalKeysSome LinkedMap2".to_vec(); k.extend(1u32.encode()); - assert_eq!(unhashed::get::(&runtime_io::twox_128(&k)), Some(2u32)); + assert_eq!(unhashed::get::(&hashing::twox_128(&k)), Some(2u32)); >::insert(&1, &2, &3); let mut k = b"FinalKeysSome DoubleMap".to_vec(); k.extend(1u32.encode()); - let mut k = runtime_io::blake2_256(&k).to_vec(); - k.extend(&runtime_io::blake2_256(&2u32.encode())); + let mut k = hashing::blake2_256(&k).to_vec(); + k.extend(&hashing::blake2_256(&2u32.encode())); assert_eq!(unhashed::get::(&k), Some(3u32)); >::insert(&1, &2, &3); let mut k = b"FinalKeysSome DoubleMap2".to_vec(); k.extend(1u32.encode()); - let mut k = runtime_io::twox_128(&k).to_vec(); - k.extend(&runtime_io::blake2_128(&2u32.encode())); + let mut k = hashing::twox_128(&k).to_vec(); + k.extend(&hashing::blake2_128(&2u32.encode())); assert_eq!(unhashed::get::(&k), Some(3u32)); }); } #[test] fn final_keys_instance_2() { - with_storage(&mut Default::default(), || { + TestExternalities::default().execute_with(|| { >::put(1); assert_eq!( - unhashed::get::(&runtime_io::twox_128(b"Instance2FinalKeysSome Value")), + unhashed::get::(&hashing::twox_128(b"Instance2FinalKeysSome Value")), Some(1u32) ); >::insert(1, 2); let mut k = b"Instance2FinalKeysSome Map".to_vec(); k.extend(1u32.encode()); - assert_eq!(unhashed::get::(&runtime_io::blake2_256(&k)), Some(2u32)); + assert_eq!(unhashed::get::(&hashing::blake2_256(&k)), Some(2u32)); >::insert(1, 2); let mut k = b"Instance2FinalKeysSome Map2".to_vec(); k.extend(1u32.encode()); - assert_eq!(unhashed::get::(&runtime_io::twox_128(&k)), Some(2u32)); + assert_eq!(unhashed::get::(&hashing::twox_128(&k)), Some(2u32)); let head = b"head of Instance2FinalKeysSome LinkedMap".to_vec(); - assert_eq!(unhashed::get::(&runtime_io::blake2_256(&head)), None); + assert_eq!(unhashed::get::(&hashing::blake2_256(&head)), None); >::insert(1, 2); let mut k = b"Instance2FinalKeysSome LinkedMap".to_vec(); k.extend(1u32.encode()); - assert_eq!(unhashed::get::(&runtime_io::blake2_256(&k)), Some(2u32)); - assert_eq!(unhashed::get::(&runtime_io::blake2_256(&head)), Some(1u32)); + assert_eq!(unhashed::get::(&hashing::blake2_256(&k)), Some(2u32)); + assert_eq!(unhashed::get::(&hashing::blake2_256(&head)), Some(1u32)); >::insert(1, 2); let mut k = b"Instance2FinalKeysSome LinkedMap2".to_vec(); k.extend(1u32.encode()); - assert_eq!(unhashed::get::(&runtime_io::twox_128(&k)), Some(2u32)); + assert_eq!(unhashed::get::(&hashing::twox_128(&k)), Some(2u32)); >::insert(&1, &2, &3); let mut k = b"Instance2FinalKeysSome DoubleMap".to_vec(); k.extend(1u32.encode()); - let mut k = runtime_io::blake2_256(&k).to_vec(); - k.extend(&runtime_io::blake2_256(&2u32.encode())); + let mut k = hashing::blake2_256(&k).to_vec(); + k.extend(&hashing::blake2_256(&2u32.encode())); assert_eq!(unhashed::get::(&k), Some(3u32)); >::insert(&1, &2, &3); let mut k = b"Instance2FinalKeysSome DoubleMap2".to_vec(); k.extend(1u32.encode()); - let mut k = runtime_io::twox_128(&k).to_vec(); - k.extend(&runtime_io::blake2_128(&2u32.encode())); + let mut k = hashing::twox_128(&k).to_vec(); + k.extend(&hashing::blake2_128(&2u32.encode())); assert_eq!(unhashed::get::(&k), Some(3u32)); }); } diff --git a/srml/support/test/tests/instance.rs b/srml/support/test/tests/instance.rs index 282fb9a6e299f..80e7e526e596d 100644 --- a/srml/support/test/tests/instance.rs +++ b/srml/support/test/tests/instance.rs @@ -303,7 +303,7 @@ fn new_test_ext() -> runtime_io::TestExternalities { #[test] fn storage_instance_independance() { let mut storage = (std::collections::HashMap::new(), std::collections::HashMap::new()); - runtime_io::with_storage(&mut storage, || { + state_machine::BasicExternalities::execute_with_storage(&mut storage, || { module2::Value::::put(0); module2::Value::::put(0); module2::Value::::put(0); diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index e07b937751267..3ffbf9c23d701 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -424,11 +424,11 @@ decl_storage! { build(|config: &GenesisConfig| { use codec::Encode; - runtime_io::set_storage(well_known_keys::CODE, &config.code); - runtime_io::set_storage(well_known_keys::EXTRINSIC_INDEX, &0u32.encode()); + runtime_io::storage::set(well_known_keys::CODE, &config.code); + runtime_io::storage::set(well_known_keys::EXTRINSIC_INDEX, &0u32.encode()); if let Some(ref changes_trie_config) = config.changes_trie_config { - runtime_io::set_storage( + runtime_io::storage::set( well_known_keys::CHANGES_TRIE_CONFIG, &changes_trie_config.encode(), ); diff --git a/srml/system/src/offchain.rs b/srml/system/src/offchain.rs index 11f7e234f7197..3d44746bfd1f4 100644 --- a/srml/system/src/offchain.rs +++ b/srml/system/src/offchain.rs @@ -114,7 +114,7 @@ where ::create_transaction::(call, public, id, expected) .ok_or(())?; let xt = Self::Extrinsic::new(call, Some(signature_data)).ok_or(())?; - runtime_io::submit_transaction(xt.encode()) + runtime_io::offchain::submit_transaction(xt.encode()) } } @@ -129,7 +129,7 @@ pub trait SubmitUnsignedTransaction { /// and `Err` if transaction was rejected from the pool. fn submit_unsigned(call: impl Into) -> Result<(), ()> { let xt = Self::Extrinsic::new(call.into(), None).ok_or(())?; - runtime_io::submit_transaction(xt.encode()) + runtime_io::offchain::submit_transaction(xt.encode()) } }