mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 18:07:58 +00:00
Substrate runtime interface 2.0 (#4057)
* Adds first version of traits for generating the host functions * First steps of the procedural macro * Implements generation of the host extern functions * Prefix ext host function with snake case trait name * Implement host functions implementation on the host * Change `HostFunctions` interface * Implement `HostFunctions` for tuples * Make `WasmExecutor` generic over the host functions * Begin to add a test and make it compile * Make the test succeed * Add test to ensure that host functions are not found * It's alive! Make the `set_storage` test work * Add test for mutable references * Code cleanup and documentation etc * Add marker trait for types that should be passed as SCALE encoded * Inherit the visibility from the trait and more improvements * More impls and move them into their own file * Code simplification by dropping one trait * Give it a better name * Implement traits for arrays * Refactor code to support pass by codec/inner * Docs * Implement pass by inner for some crypto types and add a test * Implement exchangeable function support * Rewrite sr-io with as runtime interface * Start reworking after master merge * Adds `PassByCodec` derive * Adds `PassByInner` derive * Fix compilation errors * More implementations * Implement runtime interface traits for `str` * Make `sr-io` compile again * Fix more compilation errors * More progress on getting stuff back to compile * More compilation fixes * Fix warnings * Remove le conversions * Add support for `wasm_only` interfaces * Implement `Allocator` interface * Improve error message * Move `WasmAllocator` to `sr-io` and more clean ups * Use correct function signature for wasm functions * Store the host functions with the Wasm runtime * Docs update * Fix compilation after master merge * Remove `sr-io/without_std` * Make `srml-support` tests run again * More compilation error fixes * Use correct doc syntax * Fix test-runtime * Fix compilation * Catch native panics when executing the wasm runtime As with the native runtime, we now catch all native panics when we execute the wasm runtime. The panics inside the wasm runtime were already catched before by the wasm executor automatically, but any panic in the host functions could bring down the node. The recent switch to execute the native counterpart of the host function in `sr-io`, makes this change required. The native `sr-io` functions just `panic` when something is not provided or any other error occured. * Fix compilation * Don't panic in a panic * Move `sr-sandbox` to new runtime interface * Fixes tests after sandbox changes * Make sure we detect invalid utf8 * Fixes after master merge * Adds pass by enum strategy * Fix wasmtime integration * Some macro structure clean up * Rework and test exchangebale host functions * PassBy derive macros documentation * Docs for `runtime_interface` macro * Support wild card argument names * Adds ui tests * Make sure that we are backwards compatible to the old runtime interfaces * Documentation * Fixes after latest master merge * Make `wasmtime` happy * Make `full_crypto` work * Make the new interface versionable * Rename `Sanboxing` to `Sandbox` * Don't finalize in test while importing * Fix Performance regression * Fix test
This commit is contained in:
Generated
+80
-15
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<Self> {
|
||||
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<Vec<u8>>) -> Self {
|
||||
runtime_io::crypto::ed25519_generate(key_type, seed)
|
||||
}
|
||||
|
||||
fn sign<M: AsRef<[u8]>>(&self, key_type: KeyTypeId, msg: &M) -> Option<Self::Signature> {
|
||||
runtime_io::ed25519_sign(key_type, self, msg.as_ref())
|
||||
runtime_io::crypto::ed25519_sign(key_type, self, msg.as_ref())
|
||||
}
|
||||
|
||||
fn verify<M: AsRef<[u8]>>(&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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<u8>>) -> Self {
|
||||
Self(<$public as $crate::RuntimePublic>::generate_pair($key_type, seed))
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Self> {
|
||||
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<Vec<u8>>) -> Self {
|
||||
runtime_io::crypto::sr25519_generate(key_type, seed)
|
||||
}
|
||||
|
||||
fn sign<M: AsRef<[u8]>>(&self, key_type: KeyTypeId, msg: &M) -> Option<Self::Signature> {
|
||||
runtime_io::sr25519_sign(key_type, self, msg.as_ref())
|
||||
runtime_io::crypto::sr25519_sign(key_type, self, msg.as_ref())
|
||||
}
|
||||
|
||||
fn verify<M: AsRef<[u8]>>(&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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Self>;
|
||||
|
||||
/// 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<Vec<u8>>) -> 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<Self>;
|
||||
|
||||
/// 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<Vec<u8>>) -> Self;
|
||||
|
||||
/// Sign the given message with the corresponding private key of this public key.
|
||||
///
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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<u8>) -> Vec<u8> {
|
||||
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<u8>) -> Vec<u8> {
|
||||
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()));
|
||||
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::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()));
|
||||
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)
|
||||
}
|
||||
|
||||
+55
-84
@@ -14,9 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<u8>, 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<u8>, 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<u8>, 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<u8>, 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<u8>, 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<u8>, 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<u8>) {
|
||||
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<u8> {
|
||||
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<u8>, len: WordSize, out: Pointer<u8>) {
|
||||
@@ -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<u8>, 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<u32>) -> Pointer<u8> {
|
||||
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<u8>) {
|
||||
// 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::<std::result::Result<Vec<_>, _>>()?;
|
||||
|
||||
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<u8> {
|
||||
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<T: PointerType> {
|
||||
fn write_primitive(&mut self, ptr: Pointer<T>, t: T) -> WResult<()>;
|
||||
}
|
||||
|
||||
impl WritePrimitive<u32> for &mut dyn FunctionContext {
|
||||
fn write_primitive(&mut self, ptr: Pointer<u32>, t: u32) -> WResult<()> {
|
||||
let r = t.to_le_bytes();
|
||||
self.write_memory(ptr.cast(), &r)
|
||||
}
|
||||
}
|
||||
|
||||
trait ReadPrimitive<T: PointerType> {
|
||||
fn read_primitive(&self, offset: Pointer<T>) -> WResult<T>;
|
||||
}
|
||||
|
||||
impl ReadPrimitive<u32> for &mut dyn FunctionContext {
|
||||
fn read_primitive(&self, ptr: Pointer<u32>) -> WResult<u32> {
|
||||
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<offchain::Timestamp> {
|
||||
if deadline == 0 {
|
||||
None
|
||||
@@ -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<Blake2Hasher, u64>;
|
||||
|
||||
fn call_in_wasm<E: Externalities>(
|
||||
function: &str,
|
||||
call_data: &[u8],
|
||||
execution_method: WasmExecutionMethod,
|
||||
ext: &mut E,
|
||||
code: &[u8],
|
||||
heap_pages: u64,
|
||||
) -> crate::error::Result<Vec<u8>> {
|
||||
crate::call_in_wasm::<E, runtime_io::SubstrateHostFunctions>(
|
||||
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) {
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)]
|
||||
|
||||
@@ -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<u8>` that contains the return value of the function.
|
||||
pub fn call_in_wasm<E: Externalities>(
|
||||
pub fn call_in_wasm<E: Externalities, HF: wasm_interface::HostFunctions>(
|
||||
function: &str,
|
||||
call_data: &[u8],
|
||||
execution_method: WasmExecutionMethod,
|
||||
@@ -76,6 +76,7 @@ pub fn call_in_wasm<E: Externalities>(
|
||||
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,
|
||||
|
||||
@@ -14,15 +14,23 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<RuntimesCache> = 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<D> {
|
||||
/// Dummy field to avoid the compiler complaining about us not using `D`.
|
||||
_dummy: std::marker::PhantomData<D>,
|
||||
@@ -72,6 +79,8 @@ pub struct NativeExecutor<D> {
|
||||
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<D: NativeExecutionDispatch> NativeExecutor<D> {
|
||||
@@ -84,11 +93,18 @@ impl<D: NativeExecutionDispatch> NativeExecutor<D> {
|
||||
/// `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<u64>) -> 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<D: NativeExecutionDispatch> NativeExecutor<D> {
|
||||
ext,
|
||||
self.fallback_method,
|
||||
self.default_heap_pages,
|
||||
&self.host_functions,
|
||||
)?;
|
||||
|
||||
let runtime = AssertUnwindSafe(runtime);
|
||||
@@ -143,6 +160,7 @@ impl<D: NativeExecutionDispatch> Clone for NativeExecutor<D> {
|
||||
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<D: NativeExecutionDispatch> CodeExecutor for NativeExecutor<D> {
|
||||
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<D: NativeExecutionDispatch> CodeExecutor for NativeExecutor<D> {
|
||||
target: "executor",
|
||||
"Request for native execution with native call succeeded (native: {}, chain: {}).",
|
||||
self.native_version.runtime_version,
|
||||
onchain_version
|
||||
onchain_version,
|
||||
);
|
||||
|
||||
used_native = true;
|
||||
|
||||
@@ -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<Vec<u8>, 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<Box<dyn WasmRuntime>, 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<dyn WasmRuntime> { 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<dyn WasmRuntime> { Box::new(runtime) }),
|
||||
}
|
||||
}
|
||||
@@ -196,11 +227,12 @@ fn create_versioned_wasm_runtime<E: Externalities>(
|
||||
ext: &mut E,
|
||||
wasm_method: WasmExecutionMethod,
|
||||
heap_pages: u64,
|
||||
host_functions: Vec<&'static dyn Function>,
|
||||
) -> Result<VersionedRuntime, WasmError> {
|
||||
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<E: Externalities>(
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 )* } )*
|
||||
|
||||
@@ -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<wasmi::FuncRef>,
|
||||
heap: allocator::FreeingBumpHeapAllocator,
|
||||
memory: MemoryRef,
|
||||
table: Option<TableRef>,
|
||||
host_functions: &'a [&'static dyn Function],
|
||||
}
|
||||
|
||||
impl FunctionExecutor {
|
||||
fn new(m: MemoryRef, heap_base: u32, t: Option<TableRef>) -> Result<Self, Error> {
|
||||
impl<'a> FunctionExecutor<'a> {
|
||||
fn new(
|
||||
m: MemoryRef,
|
||||
heap_base: u32,
|
||||
t: Option<TableRef>,
|
||||
host_functions: &'a [&'static dyn Function],
|
||||
) -> Result<Self, Error> {
|
||||
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<Self::SupervisorFuncRef> {
|
||||
@@ -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<u8>, 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<wasmi::FuncRef, wasmi::Error>
|
||||
{
|
||||
let signature = wasm_interface::Signature::from(signature);
|
||||
struct Resolver<'a>(&'a[&'static dyn Function]);
|
||||
|
||||
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(),
|
||||
)
|
||||
))
|
||||
}
|
||||
impl<'a> wasmi::ModuleImportResolver for Resolver<'a> {
|
||||
fn resolve_func(&self, name: &str, signature: &wasmi::Signature)
|
||||
-> std::result::Result<wasmi::FuncRef, wasmi::Error>
|
||||
{
|
||||
let signature = 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<Option<wasmi::RuntimeValue>, 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<Vec<u8>, 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<u32, Error>) -> Result<Vec<RuntimeValue>, Error>,
|
||||
FR: FnOnce(Option<RuntimeValue>, &MemoryRef) -> Result<Option<R>, Error>,
|
||||
R,
|
||||
>(
|
||||
ext: &mut dyn Externalities,
|
||||
module_instance: &ModuleRef,
|
||||
method: &str,
|
||||
create_parameters: F,
|
||||
filter_result: FR,
|
||||
) -> Result<R, Error> {
|
||||
// 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<ModuleRef, Error> {
|
||||
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<Vec<u8>, Error>
|
||||
{
|
||||
fn host_functions(&self) -> &[&'static dyn Function] {
|
||||
&self.host_functions
|
||||
}
|
||||
|
||||
fn call(
|
||||
&mut self,
|
||||
ext: &mut dyn Externalities,
|
||||
method: &str,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, 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<WasmiRuntime, WasmError> {
|
||||
pub fn create_instance(
|
||||
code: &[u8],
|
||||
heap_pages: u64,
|
||||
host_functions: Vec<&'static dyn Function>,
|
||||
) -> Result<WasmiRuntime, WasmError> {
|
||||
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<WasmiRuntime, Was
|
||||
let data_segments = extract_data_segments(&code)?;
|
||||
|
||||
// Instantiate this module.
|
||||
let instance = instantiate_module(heap_pages as usize, &module)
|
||||
let instance = instantiate_module(heap_pages as usize, &module, &host_functions)
|
||||
.map_err(|e| WasmError::Instantiation(e.to_string()))?;
|
||||
|
||||
// Take state snapshot before executing anything.
|
||||
@@ -617,6 +603,7 @@ pub fn create_instance(code: &[u8], heap_pages: u64) -> Result<WasmiRuntime, Was
|
||||
Ok(WasmiRuntime {
|
||||
instance,
|
||||
state_snapshot,
|
||||
host_functions,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
//! Defines the compiled Wasm runtime that uses Wasmtime internally.
|
||||
|
||||
use crate::error::{Error, Result, WasmError};
|
||||
use crate::host_interface::SubstrateExternals;
|
||||
use crate::wasm_runtime::WasmRuntime;
|
||||
use crate::wasm_utils::interpret_runtime_api_result;
|
||||
use crate::wasmtime::function_executor::FunctionExecutorState;
|
||||
@@ -34,7 +33,7 @@ use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::rc::Rc;
|
||||
use wasm_interface::{HostFunctions, Pointer, WordSize};
|
||||
use wasm_interface::{Pointer, WordSize, Function};
|
||||
use wasmtime_environ::{Module, translate_signature};
|
||||
use wasmtime_jit::{
|
||||
ActionOutcome, ActionError, CodeMemory, CompilationStrategy, CompiledModule, Compiler, Context,
|
||||
@@ -49,6 +48,8 @@ pub struct WasmtimeRuntime {
|
||||
context: Context,
|
||||
max_heap_pages: Option<u32>,
|
||||
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<Vec<u8>> {
|
||||
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<WasmtimeRuntime, WasmError>
|
||||
{
|
||||
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<WasmtimeRuntime, WasmError> {
|
||||
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<RefCell<HashMap<String, Option<Export>>>>,
|
||||
compiler: Compiler,
|
||||
host_functions: &[&'static dyn Function],
|
||||
) -> std::result::Result<InstanceHandle, WasmError>
|
||||
{
|
||||
let isa = target_isa()?;
|
||||
@@ -206,7 +214,7 @@ fn instantiate_env_module(
|
||||
let mut finished_functions = <PrimaryMap<DefinedFuncIndex, *const VMFunctionBody>>::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::<SubstrateExternals>(code_memory, compiler);
|
||||
let env_state = EnvState::new(code_memory, compiler, host_functions);
|
||||
|
||||
let result = InstanceHandle::new(
|
||||
Rc::new(module),
|
||||
|
||||
@@ -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<HF: HostFunctions>(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::<EnvState>() {
|
||||
if let Some(state) = (*vmctx).host_state().downcast_mut::<EnvState>() {
|
||||
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 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));
|
||||
let mut context = AssertUnwindSafe(&mut context);
|
||||
|
||||
// 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);
|
||||
}
|
||||
let return_val = panic::catch_unwind(move || {
|
||||
let signature = func.signature();
|
||||
|
||||
Ok(())
|
||||
// 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));
|
||||
|
||||
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(())
|
||||
},
|
||||
Err(e) => {
|
||||
let message = if let Some(err) = e.downcast_ref::<String>() {
|
||||
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.
|
||||
|
||||
@@ -132,8 +132,8 @@ pub trait ExternalitiesExt {
|
||||
fn extension<T: Any + Extension>(&mut self) -> Option<&mut T>;
|
||||
}
|
||||
|
||||
impl<T: ExtensionStore + ?Sized> ExternalitiesExt for T {
|
||||
fn extension<A: Any + Extension>(&mut self) -> Option<&mut A> {
|
||||
self.extension_by_type_id(TypeId::of::<A>()).and_then(Any::downcast_mut)
|
||||
impl ExternalitiesExt for &mut dyn Externalities {
|
||||
fn extension<T: Any + Extension>(&mut self) -> Option<&mut T> {
|
||||
self.extension_by_type_id(TypeId::of::<T>()).and_then(Any::downcast_mut)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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<u32> for KeyTypeId {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
//! Simple ECDSA API.
|
||||
// end::description[]
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
use rstd::vec::Vec;
|
||||
|
||||
use rstd::cmp::Ordering;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<StorageKind> 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<HttpRequestId> 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<HttpError> 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<u32> 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<u8>);
|
||||
|
||||
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<u8>);
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -40,6 +40,14 @@ pub struct StorageData(
|
||||
pub Vec<u8>,
|
||||
);
|
||||
|
||||
/// A set of key value pairs for storage.
|
||||
#[cfg(feature = "std")]
|
||||
pub type StorageOverlay = std::collections::HashMap<Vec<u8>, Vec<u8>>;
|
||||
|
||||
/// A set of key value pairs for children storage;
|
||||
#[cfg(feature = "std")]
|
||||
pub type ChildrenStorageOverlay = std::collections::HashMap<Vec<u8>, StorageOverlay>;
|
||||
|
||||
/// Storage change set
|
||||
#[derive(RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, PartialEq, Eq))]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
[package]
|
||||
name = "substrate-runtime-interface"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
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 = []
|
||||
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "substrate-runtime-interface-proc-macro"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
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 = []
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<u8> {
|
||||
/// // 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<Vec<u8>>) {
|
||||
/// 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<u8>;
|
||||
/// fn set_or_clear(&mut self, optional: Option<Vec<u8>>);
|
||||
/// }
|
||||
///
|
||||
/// impl Interface for &mut dyn externalities::Externalities {
|
||||
/// fn call_some_complex_code(data: &[u8]) -> Vec<u8> { Vec::new() }
|
||||
/// fn set_or_clear(&mut self, optional: Option<Vec<u8>>) {
|
||||
/// 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<u8> {
|
||||
/// <&mut dyn externalities::Externalities as Interface>::call_some_complex_code(data)
|
||||
/// }
|
||||
///
|
||||
/// pub fn set_or_clear(optional: Option<Vec<u8>>) {
|
||||
/// 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
|
||||
/// /// `<ARGUMENT_TYPE as RIType>::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<u8> {
|
||||
/// // 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<Vec<u8>>) {
|
||||
/// // 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::keywords::wasm_only>);
|
||||
|
||||
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<u8>,
|
||||
/// 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<u8>` and `From<Self> 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()
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<TokenStream> {
|
||||
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)));
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Derive macro implementation of `PassBy` with the associated type set to `Enum`.
|
||||
//!
|
||||
//! Besides `PassBy`, `TryFrom<u8>` and `From<Self> 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<TokenStream> {
|
||||
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::<Result<Vec<_>>>()?;
|
||||
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<u8> for #ident {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(inner: u8) -> #crate_::rstd::result::Result<Self, ()> {
|
||||
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<impl Iterator<Item = Result<&'a Ident>>> {
|
||||
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."))
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<TokenStream> {
|
||||
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<Ident>)> {
|
||||
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`!",
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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;
|
||||
+186
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<TokenStream> {
|
||||
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<TokenStream> {
|
||||
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<TokenStream> {
|
||||
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<TokenStream> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
+415
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<TokenStream> {
|
||||
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<TokenStream> {
|
||||
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<TokenStream> {
|
||||
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<TokenStream> {
|
||||
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::<Result<Vec<_>>>()?;
|
||||
|
||||
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<TokenStream> {
|
||||
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::<Result<Vec<_>>>()?;
|
||||
let ffi_to_host_values = generate_ffi_to_host_value(&method.sig).collect::<Result<Vec<_>>>()?;
|
||||
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<Item = #crate_::wasm_interface::Value>,
|
||||
) -> std::result::Result<Option<#crate_::wasm_interface::Value>, 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<TokenStream> {
|
||||
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<Item = Result<TokenStream>> + '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<Item = Result<TokenStream>> + '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<Ident> {
|
||||
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<TokenStream> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<TokenStream> {
|
||||
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)
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<TokenStream> {
|
||||
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<Error>,
|
||||
}
|
||||
|
||||
impl ToEssentialTraitDef {
|
||||
/// Convert the given trait definition to the essential trait definition.
|
||||
fn convert(trait_def: ItemTrait) -> Result<ItemTrait> {
|
||||
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<S: Spanned>(&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<TokenStream> {
|
||||
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 )*
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
|
||||
//! 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<Item = PatType> + '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<Item = Box<Pat>> + '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<Item = Box<Type>> + '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<Item = Box<Type>> + '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<Item = (Box<Pat>, Box<Type>)> + '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<Item = Option<(token::And, Option<token::Mut>)>> + '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<Item = &'a TraitItemMethod> {
|
||||
trait_def
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|i| match i {
|
||||
TraitItem::Method(ref method) => Some(method),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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");
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
use runtime_interface::runtime_interface;
|
||||
|
||||
#[runtime_interface]
|
||||
trait Test<T> {
|
||||
fn test<R>() {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,11 @@
|
||||
error: Generic parameters not supported.
|
||||
--> $DIR/no_generic_parameters.rs:5:10
|
||||
|
|
||||
5 | fn test<R>() {}
|
||||
| ^
|
||||
|
||||
error: Generic parameters not supported.
|
||||
--> $DIR/no_generic_parameters.rs:4:12
|
||||
|
|
||||
4 | trait Test<T> {
|
||||
| ^
|
||||
@@ -0,0 +1,8 @@
|
||||
use runtime_interface::runtime_interface;
|
||||
|
||||
#[runtime_interface]
|
||||
trait Test {
|
||||
fn test();
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Methods need to have an implementation.
|
||||
--> $DIR/no_method_implementation.rs:5:2
|
||||
|
|
||||
5 | fn test();
|
||||
| ^^
|
||||
@@ -0,0 +1,6 @@
|
||||
use runtime_interface::pass_by::PassByEnum;
|
||||
|
||||
#[derive(PassByEnum)]
|
||||
struct Test;
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: `PassByEnum` only supports enums as input type.
|
||||
--> $DIR/pass_by_enum_with_struct.rs:3:10
|
||||
|
|
||||
3 | #[derive(PassByEnum)]
|
||||
| ^^^^^^^^^^
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
use runtime_interface::pass_by::PassByEnum;
|
||||
|
||||
#[derive(PassByEnum)]
|
||||
enum Test {
|
||||
Var0(u32),
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
error: `PassByEnum` only supports unit variants.
|
||||
--> $DIR/pass_by_enum_with_value_variant.rs:3:10
|
||||
|
|
||||
3 | #[derive(PassByEnum)]
|
||||
| ^^^^^^^^^^
|
||||
@@ -0,0 +1,9 @@
|
||||
use runtime_interface::pass_by::PassByInner;
|
||||
|
||||
#[derive(PassByInner)]
|
||||
struct Test {
|
||||
data: u32,
|
||||
data2: u32,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
+5
@@ -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)]
|
||||
| ^^^^^^^^^^^
|
||||
@@ -0,0 +1,8 @@
|
||||
use runtime_interface::runtime_interface;
|
||||
|
||||
#[runtime_interface]
|
||||
trait Test {
|
||||
fn test(self) {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Taking `Self` by value is not allowed.
|
||||
--> $DIR/take_self_by_value.rs:5:10
|
||||
|
|
||||
5 | fn test(self) {}
|
||||
| ^^^^
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<Self::FFIType>;
|
||||
}
|
||||
|
||||
/// 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<Self::SelfInstance>;
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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::<usize>());
|
||||
|
||||
(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::<usize>());
|
||||
|
||||
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<u8> {
|
||||
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<bool> {
|
||||
Ok(arg == 1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl IntoFFIValue for bool {
|
||||
fn into_ffi_value(self, _: &mut dyn FunctionContext) -> Result<u8> {
|
||||
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<T> RIType for Vec<T> {
|
||||
type FFIType = u64;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: 'static + Encode> IntoFFIValue for Vec<T> {
|
||||
fn into_ffi_value(self, context: &mut dyn FunctionContext) -> Result<u64> {
|
||||
let vec: Cow<'_, [u8]> = if TypeId::of::<T>() == TypeId::of::<u8>() {
|
||||
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<T: 'static + Decode> FromFFIValue for Vec<T> {
|
||||
type SelfInstance = Vec<T>;
|
||||
|
||||
fn from_ffi_value(context: &mut dyn FunctionContext, arg: u64) -> Result<Vec<T>> {
|
||||
<[T] as FromFFIValue>::from_ffi_value(context, arg)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: 'static + Encode> IntoFFIValue for Vec<T> {
|
||||
type Owned = Vec<u8>;
|
||||
|
||||
fn into_ffi_value(&self) -> WrappedFFIValue<u64, Vec<u8>> {
|
||||
self[..].into_ffi_value()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: 'static + Decode> FromFFIValue for Vec<T> {
|
||||
fn from_ffi_value(arg: u64) -> Vec<T> {
|
||||
let (ptr, len) = pointer_and_len_from_u64(arg);
|
||||
let len = len as usize;
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<u8>() {
|
||||
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<T> RIType for [T] {
|
||||
type FFIType = u64;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: 'static + Decode> FromFFIValue for [T] {
|
||||
type SelfInstance = Vec<T>;
|
||||
|
||||
fn from_ffi_value(context: &mut dyn FunctionContext, arg: u64) -> Result<Vec<T>> {
|
||||
let (ptr, len) = pointer_and_len_from_u64(arg);
|
||||
|
||||
let vec = context.read_memory(Pointer::new(ptr), len)?;
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<u8>() {
|
||||
Ok(unsafe { mem::transmute(vec) })
|
||||
} else {
|
||||
Ok(Vec::<T>::decode(&mut &vec[..]).expect("Wasm to host values are encoded correctly; qed"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl IntoPreallocatedFFIValue for [u8] {
|
||||
type SelfInstance = Vec<u8>;
|
||||
|
||||
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<T: 'static + Encode> IntoFFIValue for [T] {
|
||||
type Owned = Vec<u8>;
|
||||
|
||||
fn into_ffi_value(&self) -> WrappedFFIValue<u64, Vec<u8>> {
|
||||
if TypeId::of::<T>() == TypeId::of::<u8>() {
|
||||
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<u32> {
|
||||
(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<u32> {
|
||||
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<T: codec::Codec, E: codec::Codec> PassBy for rstd::result::Result<T, E> {
|
||||
type PassBy = Codec<Self>;
|
||||
}
|
||||
|
||||
impl<T: codec::Codec> PassBy for Option<T> {
|
||||
type PassBy = Codec<Self>;
|
||||
}
|
||||
|
||||
/// 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<Self, [u8; $n]>;
|
||||
}
|
||||
|
||||
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<String> {
|
||||
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<u64, ()> {
|
||||
let bytes = self.as_bytes();
|
||||
pointer_and_len_to_u64(bytes.as_ptr() as u32, bytes.len() as u32).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: wasm_interface::PointerType> RIType for Pointer<T> {
|
||||
type FFIType = u32;
|
||||
}
|
||||
|
||||
/// The type is passed as `u32`.
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> RIType for Pointer<T> {
|
||||
type FFIType = u32;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> IntoFFIValue for Pointer<T> {
|
||||
type Owned = ();
|
||||
|
||||
fn into_ffi_value(&self) -> WrappedFFIValue<u32> {
|
||||
(*self as u32).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> FromFFIValue for Pointer<T> {
|
||||
fn from_ffi_value(arg: u32) -> Self {
|
||||
arg as _
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: wasm_interface::PointerType> FromFFIValue for Pointer<T> {
|
||||
type SelfInstance = Self;
|
||||
|
||||
fn from_ffi_value(_: &mut dyn FunctionContext, arg: u32) -> Result<Self> {
|
||||
Ok(Pointer::new(arg))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: wasm_interface::PointerType> IntoFFIValue for Pointer<T> {
|
||||
fn into_ffi_value(self, _: &mut dyn FunctionContext) -> Result<u32> {
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<T>`, 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>`, `[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<T> = *mut T;
|
||||
|
||||
/// A pointer that can be used in a runtime interface function signature.
|
||||
#[cfg(feature = "std")]
|
||||
pub type Pointer<T> = wasm_interface::Pointer<T>;
|
||||
|
||||
#[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<primitives::Blake2Hasher, u64>;
|
||||
|
||||
fn call_wasm_method<HF: HostFunctionsT>(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::<HostFunctions>("test_return_data");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_return_option_data() {
|
||||
call_wasm_method::<HostFunctions>("test_return_option_data");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_storage() {
|
||||
let mut ext = call_wasm_method::<HostFunctions>("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::<HostFunctions>("test_return_value_into_mutable_reference");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_and_return_array() {
|
||||
call_wasm_method::<HostFunctions>("test_get_and_return_array");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array_as_mutable_reference() {
|
||||
call_wasm_method::<HostFunctions>("test_array_as_mutable_reference");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_return_input_public_key() {
|
||||
call_wasm_method::<HostFunctions>("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::<HostFunctions>("test_invalid_utf8_data_should_return_an_error");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_overwrite_native_function_implementation() {
|
||||
call_wasm_method::<HostFunctions>("test_overwrite_native_function_implementation");
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<Self>;
|
||||
}
|
||||
|
||||
/// 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<T>: 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<Self::FFIType>;
|
||||
|
||||
/// 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<T>;
|
||||
}
|
||||
|
||||
/// 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<T>: 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<Self::FFIType, Self::Owned>;
|
||||
|
||||
/// 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<T: PassBy> RIType for T {
|
||||
type FFIType = <T::PassBy as RIType>::FFIType;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: PassBy> IntoFFIValue for T {
|
||||
fn into_ffi_value(
|
||||
self,
|
||||
context: &mut dyn FunctionContext,
|
||||
) -> Result<<T::PassBy as RIType>::FFIType> {
|
||||
T::PassBy::into_ffi_value(self, context)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: PassBy> FromFFIValue for T {
|
||||
type SelfInstance = Self;
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: <T::PassBy as RIType>::FFIType,
|
||||
) -> Result<Self> {
|
||||
T::PassBy::from_ffi_value(context, arg)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: PassBy> IntoFFIValue for T {
|
||||
type Owned = <T::PassBy as PassByImpl<T>>::Owned;
|
||||
|
||||
fn into_ffi_value(&self) -> WrappedFFIValue<<T::PassBy as RIType>::FFIType, Self::Owned> {
|
||||
T::PassBy::into_ffi_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: PassBy> FromFFIValue for T {
|
||||
fn from_ffi_value(arg: <T::PassBy as RIType>::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<Self>;
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Codec<T: codec::Codec>(PhantomData<T>);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: codec::Codec> PassByImpl<T> for Codec<T> {
|
||||
fn into_ffi_value(
|
||||
instance: T,
|
||||
context: &mut dyn FunctionContext,
|
||||
) -> Result<Self::FFIType> {
|
||||
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<T> {
|
||||
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<T: codec::Codec> PassByImpl<T> for Codec<T> {
|
||||
type Owned = Vec<u8>;
|
||||
|
||||
fn into_ffi_value(instance: &T) -> WrappedFFIValue<Self::FFIType, Self::Owned> {
|
||||
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<T: codec::Codec> RIType for Codec<T> {
|
||||
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<Self, [u8; 32]>;
|
||||
/// }
|
||||
///
|
||||
/// 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<T: PassByInner<Inner = I>, I: RIType>(PhantomData<(T, I)>);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: PassByInner<Inner = I>, I: RIType> PassByImpl<T> for Inner<T, I>
|
||||
where I: IntoFFIValue + FromFFIValue<SelfInstance=I>
|
||||
{
|
||||
fn into_ffi_value(
|
||||
instance: T,
|
||||
context: &mut dyn FunctionContext,
|
||||
) -> Result<Self::FFIType> {
|
||||
instance.into_inner().into_ffi_value(context)
|
||||
}
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<T> {
|
||||
I::from_ffi_value(context, arg).map(T::from_inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: PassByInner<Inner = I>, I: RIType> PassByImpl<T> for Inner<T, I>
|
||||
where I: IntoFFIValue + FromFFIValue
|
||||
{
|
||||
type Owned = I::Owned;
|
||||
|
||||
fn into_ffi_value(instance: &T) -> WrappedFFIValue<Self::FFIType, Self::Owned> {
|
||||
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<T: PassByInner<Inner = I>, I: RIType> RIType for Inner<T, I> {
|
||||
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<u8>` and `From<Self> 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<Test> for u8 {
|
||||
/// fn from(val: Test) -> u8 {
|
||||
/// match val {
|
||||
/// Test::Test1 => 0,
|
||||
/// Test::Test2 => 1,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl std::convert::TryFrom<u8> for Test {
|
||||
/// type Error = ();
|
||||
///
|
||||
/// fn try_from(val: u8) -> Result<Test, ()> {
|
||||
/// match val {
|
||||
/// 0 => Ok(Test::Test1),
|
||||
/// 1 => Ok(Test::Test2),
|
||||
/// _ => Err(()),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl PassBy for Test {
|
||||
/// type PassBy = Enum<Self>;
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Enum<T: Copy + Into<u8> + TryFrom<u8>>(PhantomData<T>);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Copy + Into<u8> + TryFrom<u8>> PassByImpl<T> for Enum<T> {
|
||||
fn into_ffi_value(
|
||||
instance: T,
|
||||
_: &mut dyn FunctionContext,
|
||||
) -> Result<Self::FFIType> {
|
||||
Ok(instance.into())
|
||||
}
|
||||
|
||||
fn from_ffi_value(
|
||||
_: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<T> {
|
||||
T::try_from(arg).map_err(|_| format!("Invalid enum discriminant: {}", arg))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: Copy + Into<u8> + TryFrom<u8, Error = ()>> PassByImpl<T> for Enum<T> {
|
||||
type Owned = ();
|
||||
|
||||
fn into_ffi_value(instance: &T) -> WrappedFFIValue<Self::FFIType, Self::Owned> {
|
||||
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<T: Copy + Into<u8> + TryFrom<u8>> RIType for Enum<T> {
|
||||
type FFIType = u8;
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<Self::FFIType, Self::Owned>;
|
||||
}
|
||||
|
||||
/// 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<u8>`, while using the
|
||||
/// pointer to a SCALE encoded `Vec<u8>` that is stored in this wrapper for any other `Vec<T>`.
|
||||
pub enum WrappedFFIValue<T, O = ()> {
|
||||
Wrapped(T),
|
||||
WrappedAndOwned(T, O),
|
||||
}
|
||||
|
||||
impl<T: Copy, O> WrappedFFIValue<T, O> {
|
||||
/// Returns the wrapped ffi value.
|
||||
pub fn get(&self) -> T {
|
||||
match self {
|
||||
Self::Wrapped(data) | Self::WrappedAndOwned(data, _) => *data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, O> From<T> for WrappedFFIValue<T, O> {
|
||||
fn from(val: T) -> Self {
|
||||
WrappedFFIValue::Wrapped(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, O> From<(T, O)> for WrappedFFIValue<T, O> {
|
||||
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<T>(Cell<(T, ExchangeableFunctionState)>);
|
||||
|
||||
impl<T> ExchangeableFunction<T> {
|
||||
/// Create a new instance of `ExchangeableFunction`.
|
||||
pub const fn new(impl_: T) -> Self {
|
||||
Self(Cell::new((impl_, ExchangeableFunctionState::Original)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> ExchangeableFunction<T> {
|
||||
/// 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<T> {
|
||||
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<T> Sync for ExchangeableFunction<T> {}
|
||||
|
||||
/// Restores a function implementation on drop.
|
||||
///
|
||||
/// Stores a static reference to the function object and the original implementation.
|
||||
pub struct RestoreImplementation<T: 'static + Copy>(&'static ExchangeableFunction<T>, Option<T>);
|
||||
|
||||
impl<T: Copy> Drop for RestoreImplementation<T> {
|
||||
fn drop(&mut self) {
|
||||
self.0.restore_orig_implementation(self.1.take().expect("Value is only taken on drop; qed"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "substrate-runtime-interface-test-wasm"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
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" ]
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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",
|
||||
);
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<u8>) -> Vec<u8> {
|
||||
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<u8>) -> Option<Vec<u8>> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::env;
|
||||
|
||||
#[rustversion::attr(not(stable), ignore)]
|
||||
|
||||
@@ -2,20 +2,17 @@
|
||||
name = "sr-io"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
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 = []
|
||||
|
||||
@@ -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\"");
|
||||
}
|
||||
}
|
||||
+785
-344
File diff suppressed because it is too large
Load Diff
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<T: Hasher> 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<Vec<u8>> {
|
||||
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<usize> {
|
||||
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<Vec<u8>> {
|
||||
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<usize> {
|
||||
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<u8> {
|
||||
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<u8>, Vec<u8>)>) -> H256 {
|
||||
Layout::<Blake2Hasher>::trie_root(input)
|
||||
}
|
||||
|
||||
fn blake2_256_ordered_trie_root(input: Vec<Vec<u8>>) -> H256 {
|
||||
Layout::<Blake2Hasher>::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<ed25519::Public> {
|
||||
with_externalities(|ext| {
|
||||
ext.extension::<KeystoreExt>()
|
||||
.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::<KeystoreExt>()
|
||||
.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<ed25519::Signature> {
|
||||
let pub_key = ed25519::Public::try_from(pubkey.as_ref()).ok()?;
|
||||
|
||||
with_externalities(|ext| {
|
||||
ext.extension::<KeystoreExt>()
|
||||
.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<sr25519::Public> {
|
||||
with_externalities(|ext| {
|
||||
ext.extension::<KeystoreExt>()
|
||||
.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::<KeystoreExt>()
|
||||
.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<sr25519::Signature> {
|
||||
let pub_key = sr25519::Public::try_from(pubkey.as_ref()).ok()?;
|
||||
|
||||
with_externalities(|ext| {
|
||||
ext.extension::<KeystoreExt>()
|
||||
.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<R>(f: impl FnOnce(&mut dyn offchain::Externalities) -> R, msg: &'static str) -> R {
|
||||
with_externalities(|ext| ext
|
||||
.extension::<OffchainExt>()
|
||||
.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<u8>) -> Result<(), ()> {
|
||||
with_offchain(|ext| {
|
||||
ext.submit_transaction(data)
|
||||
}, "submit_transaction can be called only in the offchain worker context")
|
||||
}
|
||||
|
||||
fn network_state() -> Result<OpaqueNetworkState, ()> {
|
||||
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<Vec<u8>> {
|
||||
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<offchain::HttpRequestId, ()> {
|
||||
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<offchain::Timestamp>,
|
||||
) -> 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<offchain::Timestamp>,
|
||||
) -> Vec<offchain::HttpRequestStatus> {
|
||||
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<u8>, Vec<u8>)> {
|
||||
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<offchain::Timestamp>,
|
||||
) -> Result<usize, offchain::HttpError> {
|
||||
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<u8>, Vec<u8>>;
|
||||
|
||||
/// A set of key value pairs for children storage;
|
||||
pub type ChildrenStorageOverlay = HashMap<Vec<u8>, 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, F: FnOnce() -> 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());
|
||||
});
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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};
|
||||
|
||||
@@ -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()) == <dyn AsRef<[u8; 32]>>::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())
|
||||
== <dyn AsRef<[u8; 32]>>::as_ref(who),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,11 +221,15 @@ impl<'a, I: AsRef<[u8]>, T: IntoIterator<Item=I>> 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<Item=I>> 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<Option<Timestamp>>
|
||||
) -> Vec<Result<HttpResult, PendingRequest>> {
|
||||
let ids = requests.iter().map(|r| r.id).collect::<Vec<_>>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Vec<u8>>) -> Self {
|
||||
use rand::RngCore;
|
||||
UintAuthorityId(rand::thread_rng().next_u64())
|
||||
}
|
||||
|
||||
@@ -85,21 +85,24 @@ pub trait Verify {
|
||||
impl Verify for primitives::ed25519::Signature {
|
||||
type Signer = primitives::ed25519::Public;
|
||||
fn verify<L: Lazy<[u8]>>(&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<L: Lazy<[u8]>>(&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<L: Lazy<[u8]>>(&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) => <dyn AsRef<[u8]>>::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<u8>, Vec<u8>)>) -> Self::Output {
|
||||
runtime_io::blake2_256_trie_root(input)
|
||||
runtime_io::storage::blake2_256_trie_root(input)
|
||||
}
|
||||
|
||||
fn ordered_trie_root(input: Vec<Vec<u8>>) -> 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<Self::Output> {
|
||||
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<u8> {
|
||||
pub fn generate(seed: Option<$crate::rstd::vec::Vec<u8>>) -> $crate::rstd::vec::Vec<u8> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,16 +2,13 @@
|
||||
name = "sr-sandbox"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
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 = []
|
||||
|
||||
@@ -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\"");
|
||||
}
|
||||
}
|
||||
@@ -197,7 +197,7 @@ impl<T> Instance<T> {
|
||||
/// - Trap occured at the execution time.
|
||||
pub fn invoke(
|
||||
&mut self,
|
||||
name: &[u8],
|
||||
name: &str,
|
||||
args: &[TypedValue],
|
||||
state: &mut T,
|
||||
) -> Result<ReturnValue, Error> {
|
||||
|
||||
@@ -257,7 +257,11 @@ pub struct Instance<T> {
|
||||
}
|
||||
|
||||
impl<T> Instance<T> {
|
||||
pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder<T>, state: &mut T) -> Result<Instance<T>, Error> {
|
||||
pub fn new(
|
||||
code: &[u8],
|
||||
env_def_builder: &EnvironmentDefinitionBuilder<T>,
|
||||
state: &mut T,
|
||||
) -> Result<Instance<T>, 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<T> Instance<T> {
|
||||
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<T> Instance<T> {
|
||||
|
||||
pub fn invoke(
|
||||
&mut self,
|
||||
name: &[u8],
|
||||
name: &str,
|
||||
args: &[TypedValue],
|
||||
state: &mut T,
|
||||
) -> Result<ReturnValue, Error> {
|
||||
let args = args.iter().cloned().map(Into::into).collect::<Vec<_>>();
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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::<HostFuncIndex>() == mem::size_of::<HostFuncType<T>>());
|
||||
mem::transmute::<HostFuncIndex, HostFuncType<T>>(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<u32>) -> Result<Memory, Error> {
|
||||
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<T>(
|
||||
}
|
||||
|
||||
impl<T> Instance<T> {
|
||||
pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder<T>, state: &mut T) -> Result<Instance<T>, Error> {
|
||||
pub fn new(
|
||||
code: &[u8],
|
||||
env_def_builder: &EnvironmentDefinitionBuilder<T>,
|
||||
state: &mut T,
|
||||
) -> Result<Instance<T>, Error> {
|
||||
let serialized_env_def: Vec<u8> = 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::<T>;
|
||||
// It's very important to instantiate thunk with the right type.
|
||||
let dispatch_thunk = dispatch_thunk::<T>;
|
||||
let result = sandbox::instantiate(
|
||||
dispatch_thunk as u32,
|
||||
code,
|
||||
&serialized_env_def,
|
||||
state as *const T as _,
|
||||
);
|
||||
|
||||
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,
|
||||
)
|
||||
};
|
||||
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<T> Instance<T> {
|
||||
|
||||
pub fn invoke(
|
||||
&mut self,
|
||||
name: &[u8],
|
||||
name: &str,
|
||||
args: &[TypedValue],
|
||||
state: &mut T,
|
||||
) -> Result<ReturnValue, Error> {
|
||||
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<T> Instance<T> {
|
||||
|
||||
impl<T> Drop for Instance<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
ffi::ext_sandbox_instance_teardown(self.instance_idx);
|
||||
}
|
||||
sandbox::instance_teardown(self.instance_idx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,8 @@
|
||||
name = "sr-std"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
edition = "2018"
|
||||
|
||||
[build-dependencies]
|
||||
rustc_version = "0.2.3"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
nightly = []
|
||||
strict = []
|
||||
no_global_allocator = []
|
||||
|
||||
@@ -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\"");
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub use std::alloc;
|
||||
pub use std::any;
|
||||
pub use std::borrow;
|
||||
pub use std::boxed;
|
||||
pub use std::cell;
|
||||
|
||||
@@ -14,40 +14,12 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[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;
|
||||
|
||||
@@ -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<u8>, Vec<u8>>,
|
||||
children: HashMap<Vec<u8>, HashMap<Vec<u8>, Vec<u8>>>,
|
||||
top: StorageOverlay,
|
||||
children: ChildrenStorageOverlay,
|
||||
}
|
||||
|
||||
impl BasicExternalities {
|
||||
/// Create a new instance of `BasicExternalities`
|
||||
pub fn new(
|
||||
top: HashMap<Vec<u8>, Vec<u8>>,
|
||||
children: HashMap<Vec<u8>, HashMap<Vec<u8>, Vec<u8>>>,
|
||||
) -> 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<R>(
|
||||
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<R>(&mut self, f: impl FnOnce() -> R) -> R {
|
||||
externalities::set_and_run_with_externalities(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BasicExternalities {
|
||||
|
||||
@@ -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<F> {
|
||||
/// 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,
|
||||
|
||||
@@ -46,7 +46,6 @@ pub struct TestExternalities<H: Hasher<Out=H256>=Blake2Hasher, N: ChangesTrieBlo
|
||||
}
|
||||
|
||||
impl<H: Hasher<Out=H256>, N: ChangesTrieBlockNumber> TestExternalities<H, N> {
|
||||
|
||||
/// Get externalities implementation.
|
||||
pub fn ext(&mut self) -> Ext<H, N, InMemory<H>, ChangesTrieInMemoryStorage<H, N>> {
|
||||
Ext::new(
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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<fn(u64) -> u64> = runtime_io::ExchangeableFunction::new(benchmark_add_one);
|
||||
static BENCHMARK_ADD_ONE: runtime_interface::wasm::ExchangeableFunction<fn(u64) -> 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<Block> 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<Block> 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]);
|
||||
}
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -6,3 +6,4 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
wasmi = "0.5.1"
|
||||
impl-trait-for-tuples = "0.1.2"
|
||||
|
||||
@@ -131,6 +131,12 @@ impl<T: PointerType> From<Pointer<T>> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PointerType> From<Pointer<T>> for u64 {
|
||||
fn from(ptr: Pointer<T>) -> Self {
|
||||
u64::from(ptr.ptr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PointerType> From<Pointer<T>> for usize {
|
||||
fn from(ptr: Pointer<T>) -> 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<Option<Value>>;
|
||||
}
|
||||
|
||||
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<T: PointerType> {
|
||||
/// Write the given value `t` to the given memory location `ptr`.
|
||||
fn write_primitive(&mut self, ptr: Pointer<T>, t: T) -> Result<()>;
|
||||
}
|
||||
|
||||
impl WritePrimitive<u32> for &mut dyn FunctionContext {
|
||||
fn write_primitive(&mut self, ptr: Pointer<u32>, t: u32) -> Result<()> {
|
||||
let r = t.to_le_bytes();
|
||||
self.write_memory(ptr.cast(), &r)
|
||||
}
|
||||
}
|
||||
|
||||
impl WritePrimitive<u64> for &mut dyn FunctionContext {
|
||||
fn write_primitive(&mut self, ptr: Pointer<u64>, 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<T: PointerType> {
|
||||
/// Read a primitive from the given memory location `ptr`.
|
||||
fn read_primitive(&self, ptr: Pointer<T>) -> Result<T>;
|
||||
}
|
||||
|
||||
impl ReadPrimitive<u32> for &mut dyn FunctionContext {
|
||||
fn read_primitive(&self, ptr: Pointer<u32>) -> Result<u32> {
|
||||
let mut r = [0u8; 4];
|
||||
self.read_memory_into(ptr.cast(), &mut r)?;
|
||||
Ok(u32::from_le_bytes(r))
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadPrimitive<u64> for &mut dyn FunctionContext {
|
||||
fn read_primitive(&self, ptr: Pointer<u64>) -> Result<u64> {
|
||||
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::*;
|
||||
|
||||
@@ -349,7 +349,6 @@ impl_runtime_apis! {
|
||||
|
||||
impl substrate_session::SessionKeys<Block> for Runtime {
|
||||
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
|
||||
let seed = seed.as_ref().map(|s| rstd::str::from_utf8(&s).expect("Seed is an utf8 string"));
|
||||
opaque::SessionKeys::generate(seed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -247,7 +247,7 @@ fn sign<RA: RuntimeAdapter>(
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -698,8 +698,6 @@ impl_runtime_apis! {
|
||||
|
||||
impl substrate_session::SessionKeys<Block> for Runtime {
|
||||
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
|
||||
let seed = seed.as_ref().map(|s| rstd::str::from_utf8(&s)
|
||||
.expect("Seed is an utf8 string"));
|
||||
SessionKeys::generate(seed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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<T: Trait> ProvideInherent for Module<T> {
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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<T: Trait> Module<T> {
|
||||
let tombstone = <TombstoneContractInfo<T>>::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,
|
||||
);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user