Integrate Wasmtime for runtime execution (#3869)

* executor: Use non wasmi-specific execution in tests.

* executor: Move all runtime execution tests into tests file.

* executor: Use test_case macro to easily execute tests with different
Wasm execution methods.

* executor: Convert errors to strings with Display, not Debug.

* node-executor: Rewrite benchmarks with criterion.

They were not passing compilation before and criterion seems to be more
widely used in Substrate.

* executor: Begin implementation of Wasm runtime.

The implementation demonstrates the outline of the execution, but does
not link against the external host functions.

* executor: Define and implement basic FunctionExecutor.

The SandboxCapabilities::invoke is still left unimplemented.

* executor: Implement host function trampoline generation.

* executor: Instantiate and link runtime module to env module.

* executor: Provide input data during wasmtime execution.

* executor: Implement SandboxCapabilites::invoke for wasmtime executor.

* executor: Integrate and test wasmtime execution method.

* executor: Improve FunctionExecution error messages.

* Scope the unsafe blocks to be smaller.

* Rename TrampolineState to EnvState.

* Let EnvState own its own compiler instead of unsafe lifetime cast.

* Refactor out some common wasmi/wasmtime logic.

* Typos and cosmetic changes.

* More trampoline comments.

* Cargo.lock update.

* cli: CLI option for running Substrate with compiled Wasm execution.

* executor: Switch dependency from fork to official wasmtime repo.

* Quiet down cranelift logs.

* Explicitly catch panics during host calls.

We do this to ensure that panics do not cross language boundaries.

* Additional checks and clarifications in make_trampoline.

* Fixes after merge from master and panic safety for wasmtime
instantiation.
This commit is contained in:
Jim Posen
2019-11-01 13:32:14 +01:00
committed by GitHub
parent 34bd4c335b
commit 8676c25ef4
25 changed files with 2862 additions and 652 deletions
+466
View File
@@ -188,6 +188,16 @@ dependencies = [
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bincode"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bindgen"
version = "0.47.3"
@@ -244,6 +254,16 @@ dependencies = [
"constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "blake2b_simd"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "block-buffer"
version = "0.2.0"
@@ -523,6 +543,89 @@ name = "core-foundation-sys"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cranelift-bforest"
version = "0.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cranelift-codegen"
version = "0.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cranelift-bforest 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen-meta 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen-shared 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cranelift-codegen-meta"
version = "0.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cranelift-codegen-shared 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cranelift-codegen-shared"
version = "0.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cranelift-entity"
version = "0.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cranelift-frontend"
version = "0.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cranelift-native"
version = "0.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cranelift-wasm"
version = "0.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-frontend 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crc32fast"
version = "1.2.0"
@@ -777,6 +880,26 @@ dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "directories"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dirs-sys"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dns-parser"
version = "0.8.0"
@@ -865,6 +988,25 @@ dependencies = [
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "errno"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "error-chain"
version = "0.12.1"
@@ -883,6 +1025,20 @@ dependencies = [
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "faerie"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"goblin 0.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"string-interner 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure"
version = "0.1.6"
@@ -908,6 +1064,11 @@ name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fdlimit"
version = "0.1.1"
@@ -916,6 +1077,15 @@ dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "file-per-thread-logger"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "finality-grandpa"
version = "0.9.0"
@@ -1174,6 +1344,18 @@ dependencies = [
"wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gimli"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "glob"
version = "0.2.11"
@@ -1196,6 +1378,16 @@ dependencies = [
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "goblin"
version = "0.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "h2"
version = "0.1.26"
@@ -2205,6 +2397,14 @@ dependencies = [
"linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mach"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "malloc_size_of_derive"
version = "0.1.0"
@@ -2462,6 +2662,7 @@ dependencies = [
name = "node-executor"
version = "2.0.0"
dependencies = [
"criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"node-primitives 2.0.0",
"node-runtime 2.0.0",
"node-testing 2.0.0",
@@ -3052,6 +3253,11 @@ name = "pkg-config"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ppv-lite86"
version = "0.2.5"
@@ -3426,6 +3632,16 @@ dependencies = [
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "raw-cpuid"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rayon"
version = "1.2.0"
@@ -3461,6 +3677,17 @@ name = "redox_syscall"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_users"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ref_thread_local"
version = "0.0.0"
@@ -3490,6 +3717,17 @@ name = "regex-syntax"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "region"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "remove_dir_all"
version = "0.5.2"
@@ -3540,6 +3778,16 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rust-argon2"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.16"
@@ -3652,6 +3900,25 @@ name = "scopeguard"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "scroll"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "scroll_derive"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sct"
version = "0.5.0"
@@ -4745,6 +5012,14 @@ dependencies = [
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "string-interner"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "strsim"
version = "0.8.0"
@@ -5224,6 +5499,11 @@ name = "substrate-executor"
version = "2.0.0"
dependencies = [
"assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-frontend 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-native 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -5244,9 +5524,13 @@ dependencies = [
"substrate-state-machine 2.0.0",
"substrate-trie 2.0.0",
"substrate-wasm-interface 2.0.0",
"test-case 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)",
"wasmtime-jit 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)",
"wasmtime-runtime 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)",
]
[[package]]
@@ -5958,6 +6242,16 @@ name = "take_mut"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "target-lexicon"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "target_info"
version = "0.1.0"
@@ -5993,6 +6287,18 @@ dependencies = [
"wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "test-case"
version = "0.3.3"
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)",
"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)",
"syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "textwrap"
version = "0.11.0"
@@ -6509,6 +6815,11 @@ name = "version_check"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "version_check"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
@@ -6680,6 +6991,95 @@ dependencies = [
"parity-wasm 0.40.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasmparser"
version = "0.39.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wasmtime-debug"
version = "0.2.0"
source = "git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6#71dd73d672deb325664e3c9cd4ee7acebed5fb95"
dependencies = [
"cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"faerie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"gimli 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)",
]
[[package]]
name = "wasmtime-environ"
version = "0.2.0"
source = "git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6#71dd73d672deb325664e3c9cd4ee7acebed5fb95"
dependencies = [
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"file-per-thread-logger 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"zstd 0.4.28+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasmtime-jit"
version = "0.2.0"
source = "git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6#71dd73d672deb325664e3c9cd4ee7acebed5fb95"
dependencies = [
"cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-frontend 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmtime-debug 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)",
"wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)",
"wasmtime-runtime 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)",
]
[[package]]
name = "wasmtime-runtime"
version = "0.2.0"
source = "git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6#71dd73d672deb325664e3c9cd4ee7acebed5fb95"
dependencies = [
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "web-sys"
version = "0.3.28"
@@ -6882,6 +7282,33 @@ dependencies = [
"synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zstd"
version = "0.4.28+zstd.1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"zstd-safe 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zstd-safe"
version = "1.4.13+zstd.1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"zstd-sys 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zstd-sys"
version = "1.4.13+zstd.1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"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"
@@ -6906,12 +7333,14 @@ dependencies = [
"checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83"
"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
"checksum bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8ab639324e3ee8774d296864fbc0dbbb256cf1a41c490b94cba90c082915f92"
"checksum bindgen 0.47.3 (registry+https://github.com/rust-lang/crates.io-index)" = "df683a55b54b41d5ea8ebfaebb5aa7e6b84e3f3006a78f010dadc9ca88469260"
"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
"checksum bitmask 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead"
"checksum bitvec 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9633b74910e1870f50f5af189b08487195cdb83c0e27a71d6f64d5e09dd0538b"
"checksum blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330"
"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400"
"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182"
"checksum block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1339a1042f5d9f295737ad4d9a6ab6bf81c84a933dba110b9200cd6d1448b814"
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
@@ -6946,6 +7375,14 @@ dependencies = [
"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120"
"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
"checksum cranelift-bforest 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "18c97588946d3e5fe11f8e34ebf8cc65fd3fda50f3ffa2e80c98b2748058f00f"
"checksum cranelift-codegen 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3255935da50302bcb0f7109f2fef27f44b46f1c797dfa7db971379261023adcd"
"checksum cranelift-codegen-meta 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd57265ef5e6ff253c378b6261ed8c2e6cb1b15e91624540dbd09b1e5a40e9ca"
"checksum cranelift-codegen-shared 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c093398d21f9493ab29445191362592ef621f497e56a8efb15bdf80471978b7a"
"checksum cranelift-entity 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e915fa58d2a75e3c4b768b7e4760282889915c3fcd9ccb2ad2b3ebec99654a78"
"checksum cranelift-frontend 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46963952cda267bd0177b3f036e50038cd56e7b4c5b09a455b02df727e0f2a16"
"checksum cranelift-native 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7ba8a2d69ddd4729199a321bc2f4020e1969a088b468ed6a29dc7a69350be76e"
"checksum cranelift-wasm 0.46.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5a802357a6a016bf4c1dcdc6d73a650640eb3b613cc098a1a044a6c3731ca264"
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
"checksum criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0363053954f3e679645fc443321ca128b7b950a6fe288cf5f9335cc22ee58394"
"checksum criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "938703e165481c8d612ea3479ac8342e5615185db37765162e762ec3523e2fc6"
@@ -6971,6 +7408,8 @@ dependencies = [
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
"checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a"
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
"checksum directories 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c"
"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
"checksum dns-parser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea"
"checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97"
"checksum ed25519-dalek 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d07e8b8a8386c3b89a7a4b329fdfa4cb545de2545e9e2ebbc3dd3929253e426"
@@ -6981,12 +7420,17 @@ dependencies = [
"checksum env_logger 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39ecdb7dd54465526f0a56d666e3b2dd5f3a218665a030b6e4ad9e70fa95d8fa"
"checksum environmental 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "34f8467a0284de039e6bd0e25c14519538462ba5beb548bb1f03e645097837a8"
"checksum erased-serde 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3beee4bc16478a1b26f2e80ad819a52d24745e292f521a63c16eea5f74b7eb60"
"checksum errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e"
"checksum errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067"
"checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9"
"checksum exit-future 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d8013f441e38e31c670e7f34ec8f1d5d3a2bd9d303c1ff83976ca886005e8f48"
"checksum faerie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "875d78b92b2a4d9e1e2c7eeccfa30a327d2ee6434db3beb8fd6fd92f41898bc4"
"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9"
"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
"checksum file-per-thread-logger 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8505b75b31ef7285168dd237c4a7db3c1f3e0927e7d314e670bc98e854272fe9"
"checksum finality-grandpa 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9681c1f75941ea47584573dd2bc10558b2067d460612945887e00744e43393be"
"checksum fixed-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "516877b7b9a1cc2d0293cbce23cd6203f0edbfd4090e6ca4489fecb5aa73050e"
"checksum fixed-hash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6357b15872f8126e4ea7cf79d579473f132ccd2de239494ad1bf4aa892faea68"
@@ -7016,9 +7460,11 @@ dependencies = [
"checksum get_if_addrs 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "abddb55a898d32925f3148bd281174a68eeb68bbfd9a5938a57b18f506ee4ef7"
"checksum get_if_addrs-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0d04f9fb746cf36b191c00f3ede8bde9c8e64f9f4b05ae2694a9ccf5e3f5ab48"
"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571"
"checksum gimli 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "162d18ae5f2e3b90a993d202f1ba17a5633c2484426f8bcae201f86194bacd00"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
"checksum globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2"
"checksum goblin 0.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "e3fa261d919c1ae9d1e4533c4a2f99e10938603c4208d56c05bec7a872b661b0"
"checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462"
"checksum hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a"
"checksum hash256-std-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2"
@@ -7107,6 +7553,7 @@ dependencies = [
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
"checksum mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1"
"checksum malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35adee9ed962cf7d07d62cb58bc45029f3227f5b5b86246caa8632f06c187bc3"
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
@@ -7170,6 +7617,7 @@ dependencies = [
"checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f"
"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
"checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea"
"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.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "83ef7b3b965c0eadcb6838f34f827e1dfb2939bdd5ebd43f9647e009b12b0371"
@@ -7210,19 +7658,23 @@ dependencies = [
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
"checksum rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03b418169fb9c46533f326efd6eed2576699c44ca92d3052a066214a8d828929"
"checksum rand_xoshiro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e18c91676f670f6f0312764c759405f13afb98d5d73819840cf72a518487bff"
"checksum raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "30a9d219c32c9132f7be513c18be77c9881c7107d2ab5569d205a6a0f0e6dc7d"
"checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123"
"checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
"checksum redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d"
"checksum ref_thread_local 0.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d813022b2e00774a48eaf43caaa3c20b45f040ba8cbf398e2e8911a06668dbe6"
"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd"
"checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9"
"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
"checksum region 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "448e868c6e4cfddfa49b6a72c95906c04e8547465e9536575b95c70a4044f856"
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
"checksum rhododendron 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36542aafc2429a4c010fafa079a20dee953b663cb2427f51d86cf1d436846b4d"
"checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c"
"checksum rocksdb 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1651697fefd273bfb4fd69466cc2a9d20de557a0213b97233b22b5e95924b5e"
"checksum rpassword 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f072d931f11a96546efd97642e1e75e807345aced86b947f9239102f262d0fcd"
"checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf"
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
"checksum rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
@@ -7237,6 +7689,8 @@ dependencies = [
"checksum schnorrkel 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3"
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
"checksum scroll 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383"
"checksum scroll_derive 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb"
"checksum sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f5adf8fbd58e1b1b52699dc8bed2630faecb6d8c7bee77d009d6bbe4af569b9"
"checksum security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eee63d0f4a9ec776eeb30e220f0bc1e092c3ad744b2a379e3993070364d3adc2"
"checksum security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56"
@@ -7269,6 +7723,7 @@ dependencies = [
"checksum static_slice 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "92a7e0c5e3dfb52e8fbe0e63a1b947bbb17b4036408b151353c4491374931362"
"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"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum structopt 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4f66a4c0ddf7aee4677995697366de0749b0139057342eccbb609b12d0affc"
"checksum structopt-derive 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8fe0c13e476b4e21ff7f5c4ace3818b6d7bdc16897c31c73862471bc1663acae"
@@ -7284,10 +7739,12 @@ dependencies = [
"checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203"
"checksum sysinfo 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bd3b813d94552a8033c650691645f8dd5a63d614dddd62428a95d3931ef7b6"
"checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
"checksum target-lexicon 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7975cb2c6f37d77b190bc5004a2bb015971464756fde9514651a525ada2a741a"
"checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe"
"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e"
"checksum test-case 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a605baa797821796a751f4a959e1206079b24a4b7e1ed302b7d785d81a9276c9"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865"
@@ -7341,6 +7798,7 @@ dependencies = [
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum vergen 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6aba5e34f93dc7051dfad05b98a18e9156f27e7b431fe1d2398cb6061c0a1dba"
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5c5c1286c6e578416982609f47594265f9d489f9b836157d403ad605a46693"
"checksum wabt-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "af5d153dc96aad7dc13ab90835b892c69867948112d95299e522d370c4e13a08"
@@ -7358,6 +7816,11 @@ dependencies = [
"checksum wasm-timer 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aa3e01d234bb71760e685cfafa5e2c96f8ad877c161a721646356651069e26ac"
"checksum wasmi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f31d26deb2d9a37e6cfed420edce3ed604eab49735ba89035e13c98f9a528313"
"checksum wasmi-validation 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6bc0356e3df56e639fc7f7d8a99741915531e27ed735d911ed83d7e1339c8188"
"checksum wasmparser 0.39.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5083b449454f7de0b15f131eee17de54b5a71dcb9adcf11df2b2f78fad0cd82"
"checksum wasmtime-debug 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)" = "<none>"
"checksum wasmtime-environ 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)" = "<none>"
"checksum wasmtime-jit 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)" = "<none>"
"checksum wasmtime-runtime 0.2.0 (git+https://github.com/CraneStation/wasmtime.git?rev=71dd73d6)" = "<none>"
"checksum web-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "c84440699cd02ca23bed6f045ffb1497bc18a3c2628bd13e2093186faaaacf6b"
"checksum webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f7e1cd7900a3a6b65a3e8780c51a3e6b59c0e2c55c6dc69578c288d69f7d082"
"checksum webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c10fa4212003ba19a564f25cd8ab572c6791f99a03cc219c13ed35ccab00de0e"
@@ -7380,3 +7843,6 @@ dependencies = [
"checksum zeroize 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4090487fa66630f7b166fba2bbb525e247a5449f41c468cc1d98f8ae6ac03120"
"checksum zeroize 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86"
"checksum zeroize_derive 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "080616bd0e31f36095288bb0acdf1f78ef02c2fa15527d7e993f2a6c7591643e"
"checksum zstd 0.4.28+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f4e716acaad66f2daf2526f37a1321674a8814c0b37a366ebe6c97a699f85ddc"
"checksum zstd-safe 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bfe4d3b26a0790201848865663e8ffabf091e126e548bc9710ccfa95621ece48"
"checksum zstd-sys 1.4.13+zstd.1.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fadc8ebe858f056ab82dffb9d93850b841603bdf663db7cf5e3dbd7f34cc55b2"
+1
View File
@@ -108,3 +108,4 @@ members = [
[profile.release]
# Substrate runtime requires unwinding.
panic = "unwind"
+5
View File
@@ -38,3 +38,8 @@ rpassword = "4.0.1"
[dev-dependencies]
tempdir = "0.3.7"
[features]
wasmtime = [
"service/wasmtime",
]
+1
View File
@@ -897,6 +897,7 @@ fn init_logger(pattern: &str) {
// Disable info logging by default for some modules:
builder.filter(Some("ws"), log::LevelFilter::Off);
builder.filter(Some("hyper"), log::LevelFilter::Warn);
builder.filter(Some("cranelift_wasm"), log::LevelFilter::Warn);
// Enable info for others.
builder.filter(None, log::LevelFilter::Info);
+20 -1
View File
@@ -50,6 +50,19 @@ arg_enum! {
pub enum WasmExecutionMethod {
// Uses an interpreter.
Interpreted,
// Uses a compiled runtime.
Compiled,
}
}
impl WasmExecutionMethod {
/// Returns list of variants that are not disabled by feature flags.
fn enabled_variants() -> Vec<&'static str> {
Self::variants()
.iter()
.cloned()
.filter(|&name| cfg!(feature = "wasmtime") || name != "Compiled")
.collect()
}
}
@@ -57,6 +70,12 @@ impl Into<service::config::WasmExecutionMethod> for WasmExecutionMethod {
fn into(self) -> service::config::WasmExecutionMethod {
match self {
WasmExecutionMethod::Interpreted => service::config::WasmExecutionMethod::Interpreted,
#[cfg(feature = "wasmtime")]
WasmExecutionMethod::Compiled => service::config::WasmExecutionMethod::Compiled,
#[cfg(not(feature = "wasmtime"))]
WasmExecutionMethod::Compiled => panic!(
"Substrate must be compiled with \"wasmtime\" feature for compiled Wasm execution"
),
}
}
}
@@ -429,7 +448,7 @@ pub struct RunCmd {
#[structopt(
long = "wasm-execution",
value_name = "METHOD",
possible_values = &WasmExecutionMethod::variants(),
possible_values = &WasmExecutionMethod::enabled_variants(),
case_insensitive = true,
default_value = "Interpreted"
)]
+20
View File
@@ -23,6 +23,15 @@ log = "0.4.8"
libsecp256k1 = "0.3.0"
tiny-keccak = "1.5.0"
cranelift-codegen = { version = "0.46.1", optional = true }
cranelift-entity = { version = "0.46.1", optional = true }
cranelift-frontend = { version = "0.46.1", optional = true }
cranelift-native = { version = "0.46.1", optional = true }
cranelift-wasm = { version = "0.46.1", optional = true }
wasmtime-environ = { version = "0.2", optional = true, git = "https://github.com/CraneStation/wasmtime.git", rev = "71dd73d6" }
wasmtime-jit = { version = "0.2", optional = true, git = "https://github.com/CraneStation/wasmtime.git", rev = "71dd73d6" }
wasmtime-runtime = { version = "0.2", optional = true, git = "https://github.com/CraneStation/wasmtime.git", rev = "71dd73d6" }
[dev-dependencies]
assert_matches = "1.3.0"
wabt = "0.9.2"
@@ -31,7 +40,18 @@ runtime-test = { package = "substrate-runtime-test", path = "runtime-test" }
substrate-client = { path = "../client" }
substrate-offchain = { path = "../offchain/" }
state_machine = { package = "substrate-state-machine", path = "../state-machine" }
test-case = "0.3.3"
[features]
default = []
wasm-extern-trace = []
wasmtime = [
"cranelift-codegen",
"cranelift-entity",
"cranelift-frontend",
"cranelift-native",
"cranelift-wasm",
"wasmtime-environ",
"wasmtime-jit",
"wasmtime-runtime",
]
+19 -4
View File
@@ -18,6 +18,8 @@
use serializer;
use wasmi;
#[cfg(feature = "wasmtime")]
use wasmtime_jit::{ActionError, SetupError};
/// Result type alias.
pub type Result<T> = std::result::Result<T, Error>;
@@ -31,6 +33,9 @@ pub enum Error {
Trap(wasmi::Trap),
/// Wasmi loading/instantiating error
Wasmi(wasmi::Error),
/// Wasmtime action error
#[cfg(feature = "wasmtime")]
Wasmtime(ActionError),
/// Error in the API. Parameter is an error message.
ApiError(String),
/// Method is not found
@@ -75,9 +80,9 @@ pub enum Error {
/// Someone tried to allocate more memory than the allowed maximum per allocation.
#[display(fmt="Requested allocation size is too large")]
RequestedAllocationTooLarge,
/// Executing the given function failed with the given error.
#[display(fmt="Function execution failed with: {}", _0)]
FunctionExecution(String),
/// Execution of a host function failed.
#[display(fmt="Host function {} execution failed with: {}", _0, _1)]
FunctionExecution(String, String),
}
impl std::error::Error for Error {
@@ -116,6 +121,16 @@ pub enum WasmError {
InvalidModule,
/// Wasm code could not be deserialized.
CantDeserializeWasm,
/// The module does not export a linear memory named `memory`.
InvalidMemory,
/// The number of heap pages requested is disallowed by the module.
InvalidHeapPages,
/// Instantiation error.
Instantiation(Error),
Instantiation(String),
/// The compiler does not support the host machine as a target.
#[cfg(feature = "wasmtime")]
MissingCompilerSupport(&'static str),
/// Wasmtime setup error.
#[cfg(feature = "wasmtime")]
WasmtimeSetup(SetupError),
}
@@ -0,0 +1,441 @@
// 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/>.
mod sandbox;
use codec::{Encode, Decode};
use hex_literal::hex;
use primitives::{
Blake2Hasher, blake2_128, blake2_256, ed25519, sr25519, map, Pair, offchain::OffchainExt,
traits::Externalities,
};
use runtime_test::WASM_BINARY;
use state_machine::TestExternalities as CoreTestExternalities;
use substrate_offchain::testing;
use test_case::test_case;
use trie::{TrieConfiguration, trie_types::Layout};
use crate::{WasmExecutionMethod, call_in_wasm};
pub type TestExternalities = CoreTestExternalities<Blake2Hasher, u64>;
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn returning_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let output = call_in_wasm(
"test_empty_return",
&[],
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap();
assert_eq!(output, vec![0u8; 0]);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn panicking_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let output = call_in_wasm(
"test_panic",
&[],
wasm_method,
&mut ext,
&test_code[..],
8,
);
assert!(output.is_err());
let output = call_in_wasm(
"test_conditional_panic",
&[0],
wasm_method,
&mut ext,
&test_code[..],
8,
);
assert_eq!(Decode::decode(&mut &output.unwrap()[..]), Ok(Vec::<u8>::new()));
let output = call_in_wasm(
"test_conditional_panic",
&vec![2].encode(),
wasm_method,
&mut ext,
&test_code[..],
8,
);
assert!(output.is_err());
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn storage_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
{
let mut ext = ext.ext();
ext.set_storage(b"foo".to_vec(), b"bar".to_vec());
let test_code = WASM_BINARY;
let output = call_in_wasm(
"test_data_in",
&b"Hello world".to_vec().encode(),
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap();
assert_eq!(output, b"all ok!".to_vec().encode());
}
let expected = TestExternalities::new((map![
b"input".to_vec() => b"Hello world".to_vec(),
b"foo".to_vec() => b"bar".to_vec(),
b"baz".to_vec() => b"bar".to_vec()
], map![]));
assert_eq!(ext, expected);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn clear_prefix_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
{
let mut ext = ext.ext();
ext.set_storage(b"aaa".to_vec(), b"1".to_vec());
ext.set_storage(b"aab".to_vec(), b"2".to_vec());
ext.set_storage(b"aba".to_vec(), b"3".to_vec());
ext.set_storage(b"abb".to_vec(), b"4".to_vec());
ext.set_storage(b"bbb".to_vec(), b"5".to_vec());
let test_code = WASM_BINARY;
// This will clear all entries which prefix is "ab".
let output = call_in_wasm(
"test_clear_prefix",
&b"ab".to_vec().encode(),
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap();
assert_eq!(output, b"all ok!".to_vec().encode());
}
let expected = TestExternalities::new((map![
b"aaa".to_vec() => b"1".to_vec(),
b"aab".to_vec() => b"2".to_vec(),
b"bbb".to_vec() => b"5".to_vec()
], map![]));
assert_eq!(expected, ext);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn blake2_256_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
assert_eq!(
call_in_wasm(
"test_blake2_256",
&[0],
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
blake2_256(&b""[..]).to_vec().encode(),
);
assert_eq!(
call_in_wasm(
"test_blake2_256",
&b"Hello world!".to_vec().encode(),
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
blake2_256(&b"Hello world!"[..]).to_vec().encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn blake2_128_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
assert_eq!(
call_in_wasm(
"test_blake2_128",
&[0],
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
blake2_128(&b""[..]).to_vec().encode(),
);
assert_eq!(
call_in_wasm(
"test_blake2_128",
&b"Hello world!".to_vec().encode(),
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
blake2_128(&b"Hello world!"[..]).to_vec().encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn twox_256_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
assert_eq!(
call_in_wasm(
"test_twox_256",
&[0],
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
hex!(
"99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a"
).to_vec().encode(),
);
assert_eq!(
call_in_wasm(
"test_twox_256",
&b"Hello world!".to_vec().encode(),
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
hex!(
"b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74"
).to_vec().encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn twox_128_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
assert_eq!(
call_in_wasm(
"test_twox_128",
&[0],
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
hex!("99e9d85137db46ef4bbea33613baafd5").to_vec().encode(),
);
assert_eq!(
call_in_wasm(
"test_twox_128",
&b"Hello world!".to_vec().encode(),
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
hex!("b27dfd7f223f177f2a13647b533599af").to_vec().encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn ed25519_verify_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let key = ed25519::Pair::from_seed(&blake2_256(b"test"));
let sig = key.sign(b"all ok!");
let mut calldata = vec![];
calldata.extend_from_slice(key.public().as_ref());
calldata.extend_from_slice(sig.as_ref());
assert_eq!(
call_in_wasm(
"test_ed25519_verify",
&calldata.encode(),
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
true.encode(),
);
let other_sig = key.sign(b"all is not ok!");
let mut calldata = vec![];
calldata.extend_from_slice(key.public().as_ref());
calldata.extend_from_slice(other_sig.as_ref());
assert_eq!(
call_in_wasm(
"test_ed25519_verify",
&calldata.encode(),
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
false.encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn sr25519_verify_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let key = sr25519::Pair::from_seed(&blake2_256(b"test"));
let sig = key.sign(b"all ok!");
let mut calldata = vec![];
calldata.extend_from_slice(key.public().as_ref());
calldata.extend_from_slice(sig.as_ref());
assert_eq!(
call_in_wasm(
"test_sr25519_verify",
&calldata.encode(),
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
true.encode(),
);
let other_sig = key.sign(b"all is not ok!");
let mut calldata = vec![];
calldata.extend_from_slice(key.public().as_ref());
calldata.extend_from_slice(other_sig.as_ref());
assert_eq!(
call_in_wasm(
"test_sr25519_verify",
&calldata.encode(),
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
false.encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn ordered_trie_root_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let trie_input = vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()];
let test_code = WASM_BINARY;
assert_eq!(
call_in_wasm(
"test_ordered_trie_root",
&[0],
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
Layout::<Blake2Hasher>::ordered_trie_root(trie_input.iter()).as_bytes().encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn offchain_local_storage_should_work(wasm_method: WasmExecutionMethod) {
use substrate_client::backend::OffchainStorage;
let mut ext = TestExternalities::default();
let (offchain, state) = testing::TestOffchainExt::new();
ext.register_extension(OffchainExt::new(offchain));
let test_code = WASM_BINARY;
let mut ext = ext.ext();
assert_eq!(
call_in_wasm(
"test_offchain_local_storage",
&[0],
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
true.encode(),
);
assert_eq!(state.read().persistent_storage.get(b"", b"test"), Some(vec![]));
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn offchain_http_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let (offchain, state) = testing::TestOffchainExt::new();
ext.register_extension(OffchainExt::new(offchain));
state.write().expect_request(
0,
testing::PendingRequest {
method: "POST".into(),
uri: "http://localhost:12345".into(),
body: vec![1, 2, 3, 4],
headers: vec![("X-Auth".to_owned(), "test".to_owned())],
sent: true,
response: Some(vec![1, 2, 3]),
response_headers: vec![("X-Auth".to_owned(), "hello".to_owned())],
..Default::default()
},
);
let test_code = WASM_BINARY;
let mut ext = ext.ext();
assert_eq!(
call_in_wasm(
"test_offchain_http",
&[0],
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
true.encode(),
);
}
@@ -0,0 +1,363 @@
// Copyright 2018-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 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) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(import "env" "assert" (func $assert (param i32)))
(import "env" "inc_counter" (func $inc_counter (param i32) (result i32)))
(func (export "call")
(drop
(call $inc_counter (i32.const 5))
)
(call $inc_counter (i32.const 3))
;; current counter value is on the stack
;; check whether current == 8
i32.const 8
i32.eq
call $assert
)
)
"#).unwrap().encode();
assert_eq!(
call_in_wasm(
"test_sandbox",
&code,
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
true.encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn sandbox_trap(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(import "env" "assert" (func $assert (param i32)))
(func (export "call")
i32.const 0
call $assert
)
)
"#).unwrap();
assert_eq!(
call_in_wasm(
"test_sandbox",
&code,
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
vec![0],
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn sandbox_should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(import "env" "assert" (func $assert (param i32)))
(func (export "call")
i32.const 0
call $assert
)
)
"#).unwrap().encode();
let res = 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"));
}
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn start_called(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(import "env" "assert" (func $assert (param i32)))
(import "env" "inc_counter" (func $inc_counter (param i32) (result i32)))
;; Start function
(start $start)
(func $start
;; Increment counter by 1
(drop
(call $inc_counter (i32.const 1))
)
)
(func (export "call")
;; Increment counter by 1. The current value is placed on the stack.
(call $inc_counter (i32.const 1))
;; Counter is incremented twice by 1, once there and once in `start` func.
;; So check the returned value is equal to 2.
i32.const 2
i32.eq
call $assert
)
)
"#).unwrap().encode();
assert_eq!(
call_in_wasm(
"test_sandbox",
&code,
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
true.encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn invoke_args(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(import "env" "assert" (func $assert (param i32)))
(func (export "call") (param $x i32) (param $y i64)
;; assert that $x = 0x12345678
(call $assert
(i32.eq
(get_local $x)
(i32.const 0x12345678)
)
)
(call $assert
(i64.eq
(get_local $y)
(i64.const 0x1234567887654321)
)
)
)
)
"#).unwrap().encode();
assert_eq!(
call_in_wasm(
"test_sandbox_args",
&code,
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
true.encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn return_val(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(func (export "call") (param $x i32) (result i32)
(i32.add
(get_local $x)
(i32.const 1)
)
)
)
"#).unwrap().encode();
assert_eq!(
call_in_wasm(
"test_sandbox_return_val",
&code,
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
true.encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn unlinkable_module(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(import "env" "non-existent" (func))
(func (export "call")
)
)
"#).unwrap().encode();
assert_eq!(
call_in_wasm(
"test_sandbox_instantiate",
&code,
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
1u8.encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn corrupted_module(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
// Corrupted wasm file
let code = vec![0u8, 0, 0, 0, 1, 0, 0, 0].encode();
assert_eq!(
call_in_wasm(
"test_sandbox_instantiate",
&code,
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
1u8.encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn start_fn_ok(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(func (export "call")
)
(func $start
)
(start $start)
)
"#).unwrap().encode();
assert_eq!(
call_in_wasm(
"test_sandbox_instantiate",
&code,
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
0u8.encode(),
);
}
#[test_case(WasmExecutionMethod::Interpreted)]
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
fn start_fn_traps(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(func (export "call")
)
(func $start
unreachable
)
(start $start)
)
"#).unwrap().encode();
assert_eq!(
call_in_wasm(
"test_sandbox_instantiate",
&code,
wasm_method,
&mut ext,
&test_code[..],
8,
).unwrap(),
2u8.encode(),
);
}
+4
View File
@@ -38,6 +38,10 @@ mod sandbox;
mod allocator;
mod host_interface;
mod wasm_runtime;
#[cfg(feature = "wasmtime")]
mod wasmtime;
#[cfg(test)]
mod integration_tests;
pub mod error;
pub use wasmi;
-291
View File
@@ -582,294 +582,3 @@ impl<FR> Store<FR> {
instance_idx as u32
}
}
#[cfg(test)]
mod tests {
use super::*;
use primitives::{Blake2Hasher, traits::Externalities};
use crate::wasm_runtime::WasmRuntime;
use crate::wasmi_execution;
use state_machine::TestExternalities as CoreTestExternalities;
use wabt;
use runtime_test::WASM_BINARY;
type TestExternalities = CoreTestExternalities<Blake2Hasher, u64>;
fn call_wasm<E: Externalities>(
ext: &mut E,
heap_pages: u64,
code: &[u8],
method: &str,
data: &[u8],
) -> Result<Vec<u8>> {
let mut instance = wasmi_execution::create_instance(ext, code, heap_pages)
.map_err(|err| err.to_string())?;
instance.call(ext, method, data)
}
#[test]
fn sandbox_should_work() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(import "env" "assert" (func $assert (param i32)))
(import "env" "inc_counter" (func $inc_counter (param i32) (result i32)))
(func (export "call")
(drop
(call $inc_counter (i32.const 5))
)
(call $inc_counter (i32.const 3))
;; current counter value is on the stack
;; check whether current == 8
i32.const 8
i32.eq
call $assert
)
)
"#).unwrap().encode();
assert_eq!(
call_wasm(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(),
true.encode(),
);
}
#[test]
fn sandbox_trap() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(import "env" "assert" (func $assert (param i32)))
(func (export "call")
i32.const 0
call $assert
)
)
"#).unwrap();
assert_eq!(
call_wasm(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(),
vec![0],
);
}
#[test]
fn sandbox_should_trap_when_heap_exhausted() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(import "env" "assert" (func $assert (param i32)))
(func (export "call")
i32.const 0
call $assert
)
)
"#).unwrap().encode();
let res = call_wasm(&mut ext, 8, &test_code[..], "test_exhaust_heap", &code);
assert_eq!(res.is_err(), true);
if let Err(err) = res {
assert_eq!(
format!("{}", err),
format!(
"{}",
wasmi::Error::Trap(Error::FunctionExecution("AllocatorOutOfSpace".into()).into()),
),
);
}
}
#[test]
fn start_called() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(import "env" "assert" (func $assert (param i32)))
(import "env" "inc_counter" (func $inc_counter (param i32) (result i32)))
;; Start function
(start $start)
(func $start
;; Increment counter by 1
(drop
(call $inc_counter (i32.const 1))
)
)
(func (export "call")
;; Increment counter by 1. The current value is placed on the stack.
(call $inc_counter (i32.const 1))
;; Counter is incremented twice by 1, once there and once in `start` func.
;; So check the returned value is equal to 2.
i32.const 2
i32.eq
call $assert
)
)
"#).unwrap().encode();
assert_eq!(
call_wasm(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(),
true.encode(),
);
}
#[test]
fn invoke_args() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(import "env" "assert" (func $assert (param i32)))
(func (export "call") (param $x i32) (param $y i64)
;; assert that $x = 0x12345678
(call $assert
(i32.eq
(get_local $x)
(i32.const 0x12345678)
)
)
(call $assert
(i64.eq
(get_local $y)
(i64.const 0x1234567887654321)
)
)
)
)
"#).unwrap().encode();
assert_eq!(
call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_args", &code).unwrap(),
true.encode(),
);
}
#[test]
fn return_val() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(func (export "call") (param $x i32) (result i32)
(i32.add
(get_local $x)
(i32.const 1)
)
)
)
"#).unwrap().encode();
assert_eq!(
call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_return_val", &code).unwrap(),
true.encode(),
);
}
#[test]
fn unlinkable_module() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(import "env" "non-existent" (func))
(func (export "call")
)
)
"#).unwrap().encode();
assert_eq!(
call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(),
1u8.encode(),
);
}
#[test]
fn corrupted_module() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
// Corrupted wasm file
let code = vec![0u8, 0, 0, 0, 1, 0, 0, 0].encode();
assert_eq!(
call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(),
1u8.encode(),
);
}
#[test]
fn start_fn_ok() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(func (export "call")
)
(func $start
)
(start $start)
)
"#).unwrap().encode();
assert_eq!(
call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(),
0u8.encode(),
);
}
#[test]
fn start_fn_traps() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let code = wabt::wat2wasm(r#"
(module
(func (export "call")
)
(func $start
unreachable
)
(start $start)
)
"#).unwrap().encode();
assert_eq!(
call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(),
2u8.encode(),
);
}
}
@@ -21,6 +21,8 @@
use crate::error::{Error, WasmError};
use crate::wasmi_execution;
#[cfg(feature = "wasmtime")]
use crate::wasmtime;
use log::{trace, warn};
use codec::Decode;
use primitives::{storage::well_known_keys, traits::Externalities, H256};
@@ -51,6 +53,9 @@ pub trait WasmRuntime {
pub enum WasmExecutionMethod {
/// Uses the Wasmi interpreter.
Interpreted,
/// Uses the Wasmtime compiled runtime.
#[cfg(feature = "wasmtime")]
Compiled,
}
/// Cache for the runtimes.
@@ -181,6 +186,10 @@ pub fn create_wasm_runtime_with_code<E: Externalities>(
WasmExecutionMethod::Interpreted =>
wasmi_execution::create_instance(ext, code, heap_pages)
.map(|runtime| -> Box<dyn WasmRuntime> { Box::new(runtime) }),
#[cfg(feature = "wasmtime")]
WasmExecutionMethod::Compiled =>
wasmtime::create_instance(ext, code, heap_pages)
.map(|runtime| -> Box<dyn WasmRuntime> { Box::new(runtime) }),
}
}
+13
View File
@@ -16,6 +16,8 @@
//! Utilities for defining the wasm host environment.
use wasm_interface::{Pointer, WordSize};
/// Converts arguments into respective WASM types.
#[macro_export]
macro_rules! convert_args {
@@ -171,3 +173,14 @@ macro_rules! impl_wasm_host_interface {
}
);
}
/// Runtime API functions return an i64 which encodes a pointer in the least-significant 32 bits
/// and a length in the most-significant 32 bits. This interprets the returned value as a pointer,
/// length tuple.
pub fn interpret_runtime_api_result(retval: i64) -> (Pointer<u8>, WordSize) {
let ptr = <Pointer<u8>>::new(retval as u32);
// The first cast to u64 is necessary so that the right shift does not sign-extend.
let len = ((retval as u64) >> 32) as WordSize;
(ptr, len)
}
+22 -333
View File
@@ -27,6 +27,7 @@ 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;
use crate::wasm_runtime::WasmRuntime;
use log::trace;
use parity_wasm::elements::{deserialize_buffer, DataSegment, Instruction, Module as RawModule};
@@ -110,24 +111,24 @@ impl sandbox::SandboxCapabilities for FunctionExecutor {
impl FunctionContext for FunctionExecutor {
fn read_memory_into(&self, address: Pointer<u8>, dest: &mut [u8]) -> WResult<()> {
self.memory.get_into(address.into(), dest).map_err(|e| format!("{:?}", e))
self.memory.get_into(address.into(), dest).map_err(|e| e.to_string())
}
fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> WResult<()> {
self.memory.set(address.into(), data).map_err(|e| format!("{:?}", e))
self.memory.set(address.into(), data).map_err(|e| e.to_string())
}
fn allocate_memory(&mut self, size: WordSize) -> WResult<Pointer<u8>> {
let heap = &mut self.heap;
self.memory.with_direct_access_mut(|mem| {
heap.allocate(mem, size).map_err(|e| format!("{:?}", e))
heap.allocate(mem, size).map_err(|e| e.to_string())
})
}
fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> WResult<()> {
let heap = &mut self.heap;
self.memory.with_direct_access_mut(|mem| {
heap.deallocate(mem, ptr).map_err(|e| format!("{:?}", e))
heap.deallocate(mem, ptr).map_err(|e| e.to_string())
})
}
@@ -138,13 +139,13 @@ impl FunctionContext for FunctionExecutor {
impl Sandbox for FunctionExecutor {
fn memory_get(
&self,
&mut self,
memory_id: MemoryId,
offset: WordSize,
buf_ptr: Pointer<u8>,
buf_len: WordSize,
) -> WResult<u32> {
let sandboxed_memory = self.sandbox_store.memory(memory_id).map_err(|e| format!("{:?}", e))?;
let sandboxed_memory = self.sandbox_store.memory(memory_id).map_err(|e| e.to_string())?;
match MemoryInstance::transfer(
&sandboxed_memory,
@@ -165,7 +166,7 @@ impl Sandbox for FunctionExecutor {
val_ptr: Pointer<u8>,
val_len: WordSize,
) -> WResult<u32> {
let sandboxed_memory = self.sandbox_store.memory(memory_id).map_err(|e| format!("{:?}", e))?;
let sandboxed_memory = self.sandbox_store.memory(memory_id).map_err(|e| e.to_string())?;
match MemoryInstance::transfer(
&self.memory,
@@ -180,7 +181,7 @@ impl Sandbox for FunctionExecutor {
}
fn memory_teardown(&mut self, memory_id: MemoryId) -> WResult<()> {
self.sandbox_store.memory_teardown(memory_id).map_err(|e| format!("{:?}", e))
self.sandbox_store.memory_teardown(memory_id).map_err(|e| e.to_string())
}
fn memory_new(
@@ -188,7 +189,7 @@ impl Sandbox for FunctionExecutor {
initial: u32,
maximum: u32,
) -> WResult<MemoryId> {
self.sandbox_store.new_memory(initial, maximum).map_err(|e| format!("{:?}", e))
self.sandbox_store.new_memory(initial, maximum).map_err(|e| e.to_string())
}
fn invoke(
@@ -209,7 +210,7 @@ impl Sandbox for FunctionExecutor {
.map(Into::into)
.collect::<Vec<_>>();
let instance = self.sandbox_store.instance(instance_id).map_err(|e| format!("{:?}", e))?;
let instance = self.sandbox_store.instance(instance_id).map_err(|e| e.to_string())?;
let result = instance.invoke(export_name, &args, self, state);
match result {
@@ -229,7 +230,7 @@ impl Sandbox for FunctionExecutor {
}
fn instance_teardown(&mut self, instance_id: u32) -> WResult<()> {
self.sandbox_store.instance_teardown(instance_id).map_err(|e| format!("{:?}", e))
self.sandbox_store.instance_teardown(instance_id).map_err(|e| e.to_string())
}
fn instance_new(
@@ -309,7 +310,7 @@ impl wasmi::Externals for FunctionExecutor {
)?;
function.execute(self, &mut args)
.map_err(Error::FunctionExecution)
.map_err(|msg| Error::FunctionExecution(function.name().to_string(), msg))
.map_err(wasmi::Trap::from)
.map(|v| v.map(Into::into))
}
@@ -356,10 +357,9 @@ fn call_in_wasm_module(
Ok(vec![I32(offset as i32), I32(data.len() as i32)])
},
|res, memory| {
if let Some(I64(r)) = res {
let offset = r as u32;
let length = (r as u64 >> 32) as usize;
memory.get(offset, length).map_err(|_| Error::Runtime).map(Some)
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)
}
@@ -614,7 +614,7 @@ pub fn create_instance<E: Externalities>(ext: &mut E, code: &[u8], heap_pages: u
// Instantiate this module.
let instance = instantiate_module(heap_pages as usize, &module)
.map_err(WasmError::Instantiation)?;
.map_err(|e| WasmError::Instantiation(e.to_string()))?;
// Take state snapshot before executing anything.
let state_snapshot = StateSnapshot::take(&instance, data_segments, heap_pages)
@@ -627,11 +627,12 @@ pub fn create_instance<E: Externalities>(ext: &mut E, code: &[u8], heap_pages: u
let mut ext = AssertUnwindSafe(ext);
let call_instance = AssertUnwindSafe(&instance);
let version = crate::native_executor::safe_call(
let version_result = crate::native_executor::safe_call(
move || call_in_wasm_module(&mut **ext, *call_instance, "Core_version", &[])
.ok()
.and_then(|v| RuntimeVersion::decode(&mut v.as_slice()).ok())
).map_err(WasmError::Instantiation)?;
).map_err(|_| WasmError::Instantiation("panic in call to get runtime version".to_string()))?;
let version = version_result
.ok()
.and_then(|v| RuntimeVersion::decode(&mut v.as_slice()).ok());
Ok(WasmiRuntime {
instance,
@@ -654,315 +655,3 @@ fn extract_data_segments(wasm_code: &[u8]) -> Result<Vec<DataSegment>, WasmError
.to_vec();
Ok(segments)
}
#[cfg(test)]
mod tests {
use super::*;
use state_machine::TestExternalities as CoreTestExternalities;
use hex_literal::hex;
use primitives::{
Blake2Hasher, blake2_128, blake2_256, ed25519, sr25519, map, Pair, offchain::OffchainExt,
};
use runtime_test::WASM_BINARY;
use substrate_offchain::testing;
use trie::{TrieConfiguration, trie_types::Layout};
use codec::{Encode, Decode};
type TestExternalities = CoreTestExternalities<Blake2Hasher, u64>;
fn call<E: Externalities>(
ext: &mut E,
heap_pages: u64,
code: &[u8],
method: &str,
data: &[u8],
) -> Result<Vec<u8>, Error> {
let mut instance = create_instance(ext, code, heap_pages)
.map_err(|err| err.to_string())?;
instance.call(ext, method, data)
}
#[test]
fn returning_should_work() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let output = call(&mut ext, 8, &test_code[..], "test_empty_return", &[]).unwrap();
assert_eq!(output, vec![0u8; 0]);
}
#[test]
fn panicking_should_work() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let output = call(&mut ext, 8, &test_code[..], "test_panic", &[]);
assert!(output.is_err());
let output = call(&mut ext, 8, &test_code[..], "test_conditional_panic", &[0]);
assert_eq!(Decode::decode(&mut &output.unwrap()[..]), Ok(Vec::<u8>::new()));
let output = call(&mut ext, 8, &test_code[..], "test_conditional_panic", &vec![2].encode());
assert!(output.is_err());
}
#[test]
fn storage_should_work() {
let mut ext = TestExternalities::default();
{
let mut ext = ext.ext();
ext.set_storage(b"foo".to_vec(), b"bar".to_vec());
let test_code = WASM_BINARY;
let output = call(
&mut ext,
8,
&test_code[..],
"test_data_in",
&b"Hello world".to_vec().encode(),
).unwrap();
assert_eq!(output, b"all ok!".to_vec().encode());
}
let expected = TestExternalities::new((map![
b"input".to_vec() => b"Hello world".to_vec(),
b"foo".to_vec() => b"bar".to_vec(),
b"baz".to_vec() => b"bar".to_vec()
], map![]));
assert_eq!(ext, expected);
}
#[test]
fn clear_prefix_should_work() {
let mut ext = TestExternalities::default();
{
let mut ext = ext.ext();
ext.set_storage(b"aaa".to_vec(), b"1".to_vec());
ext.set_storage(b"aab".to_vec(), b"2".to_vec());
ext.set_storage(b"aba".to_vec(), b"3".to_vec());
ext.set_storage(b"abb".to_vec(), b"4".to_vec());
ext.set_storage(b"bbb".to_vec(), b"5".to_vec());
let test_code = WASM_BINARY;
// This will clear all entries which prefix is "ab".
let output = call(
&mut ext,
8,
&test_code[..],
"test_clear_prefix",
&b"ab".to_vec().encode(),
).unwrap();
assert_eq!(output, b"all ok!".to_vec().encode());
}
let expected = TestExternalities::new((map![
b"aaa".to_vec() => b"1".to_vec(),
b"aab".to_vec() => b"2".to_vec(),
b"bbb".to_vec() => b"5".to_vec()
], map![]));
assert_eq!(expected, ext);
}
#[test]
fn blake2_256_should_work() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
assert_eq!(
call(&mut ext, 8, &test_code[..], "test_blake2_256", &[0]).unwrap(),
blake2_256(&b""[..]).to_vec().encode(),
);
assert_eq!(
call(
&mut ext,
8,
&test_code[..],
"test_blake2_256",
&b"Hello world!".to_vec().encode(),
).unwrap(),
blake2_256(&b"Hello world!"[..]).to_vec().encode(),
);
}
#[test]
fn blake2_128_should_work() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
assert_eq!(
call(&mut ext, 8, &test_code[..], "test_blake2_128", &[0]).unwrap(),
blake2_128(&b""[..]).to_vec().encode(),
);
assert_eq!(
call(
&mut ext,
8,
&test_code[..],
"test_blake2_128",
&b"Hello world!".to_vec().encode(),
).unwrap(),
blake2_128(&b"Hello world!"[..]).to_vec().encode(),
);
}
#[test]
fn twox_256_should_work() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
assert_eq!(
call(&mut ext, 8, &test_code[..], "test_twox_256", &[0]).unwrap(),
hex!(
"99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a"
).to_vec().encode(),
);
assert_eq!(
call(
&mut ext,
8,
&test_code[..],
"test_twox_256",
&b"Hello world!".to_vec().encode(),
).unwrap(),
hex!(
"b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74"
).to_vec().encode(),
);
}
#[test]
fn twox_128_should_work() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
assert_eq!(
call(&mut ext, 8, &test_code[..], "test_twox_128", &[0]).unwrap(),
hex!("99e9d85137db46ef4bbea33613baafd5").to_vec().encode(),
);
assert_eq!(
call(
&mut ext,
8,
&test_code[..],
"test_twox_128",
&b"Hello world!".to_vec().encode(),
).unwrap(),
hex!("b27dfd7f223f177f2a13647b533599af").to_vec().encode(),
);
}
#[test]
fn ed25519_verify_should_work() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let key = ed25519::Pair::from_seed(&blake2_256(b"test"));
let sig = key.sign(b"all ok!");
let mut calldata = vec![];
calldata.extend_from_slice(key.public().as_ref());
calldata.extend_from_slice(sig.as_ref());
assert_eq!(
call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata.encode()).unwrap(),
true.encode(),
);
let other_sig = key.sign(b"all is not ok!");
let mut calldata = vec![];
calldata.extend_from_slice(key.public().as_ref());
calldata.extend_from_slice(other_sig.as_ref());
assert_eq!(
call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata.encode()).unwrap(),
false.encode(),
);
}
#[test]
fn sr25519_verify_should_work() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let test_code = WASM_BINARY;
let key = sr25519::Pair::from_seed(&blake2_256(b"test"));
let sig = key.sign(b"all ok!");
let mut calldata = vec![];
calldata.extend_from_slice(key.public().as_ref());
calldata.extend_from_slice(sig.as_ref());
assert_eq!(
call(&mut ext, 8, &test_code[..], "test_sr25519_verify", &calldata.encode()).unwrap(),
true.encode(),
);
let other_sig = key.sign(b"all is not ok!");
let mut calldata = vec![];
calldata.extend_from_slice(key.public().as_ref());
calldata.extend_from_slice(other_sig.as_ref());
assert_eq!(
call(&mut ext, 8, &test_code[..], "test_sr25519_verify", &calldata.encode()).unwrap(),
false.encode(),
);
}
#[test]
fn ordered_trie_root_should_work() {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
let trie_input = vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()];
let test_code = WASM_BINARY;
assert_eq!(
call(&mut ext, 8, &test_code[..], "test_ordered_trie_root", &[0]).unwrap(),
Layout::<Blake2Hasher>::ordered_trie_root(trie_input.iter()).as_bytes().encode(),
);
}
#[test]
fn offchain_local_storage_should_work() {
use substrate_client::backend::OffchainStorage;
let mut ext = TestExternalities::default();
let (offchain, state) = testing::TestOffchainExt::new();
ext.register_extension(OffchainExt::new(offchain));
let test_code = WASM_BINARY;
let mut ext = ext.ext();
assert_eq!(
call(&mut ext, 8, &test_code[..], "test_offchain_local_storage", &[0]).unwrap(),
true.encode(),
);
assert_eq!(state.read().persistent_storage.get(b"", b"test"), Some(vec![]));
}
#[test]
fn offchain_http_should_work() {
let mut ext = TestExternalities::default();
let (offchain, state) = testing::TestOffchainExt::new();
ext.register_extension(OffchainExt::new(offchain));
state.write().expect_request(
0,
testing::PendingRequest {
method: "POST".into(),
uri: "http://localhost:12345".into(),
body: vec![1, 2, 3, 4],
headers: vec![("X-Auth".to_owned(), "test".to_owned())],
sent: true,
response: Some(vec![1, 2, 3]),
response_headers: vec![("X-Auth".to_owned(), "hello".to_owned())],
..Default::default()
},
);
let test_code = WASM_BINARY;
let mut ext = ext.ext();
assert_eq!(
call(&mut ext, 8, &test_code[..], "test_offchain_http", &[0]).unwrap(),
true.encode(),
);
}
}
@@ -0,0 +1,387 @@
// 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::allocator::FreeingBumpHeapAllocator;
use crate::error::{Error, Result};
use crate::sandbox::{self, SandboxCapabilities, SupervisorFuncIndex};
use crate::wasmtime::util::{
checked_range, cranelift_ir_signature, read_memory_into, write_memory_from,
};
use codec::{Decode, Encode};
use cranelift_codegen::ir;
use cranelift_codegen::isa::TargetFrontendConfig;
use log::trace;
use primitives::sandbox as sandbox_primitives;
use std::{cmp, mem, ptr};
use wasmtime_environ::translate_signature;
use wasmtime_jit::{ActionError, Compiler};
use wasmtime_runtime::{Export, VMCallerCheckedAnyfunc, VMContext, wasmtime_call_trampoline};
use wasm_interface::{
FunctionContext, MemoryId, Pointer, Result as WResult, Sandbox, Signature, Value, ValueType,
WordSize,
};
/// Wrapper type for pointer to a Wasm table entry.
///
/// The wrapper type is used to ensure that the function reference is valid as it must be unsafely
/// dereferenced from within the safe method `<FunctionExecutor as SandboxCapabilities>::invoke`.
#[derive(Clone, Copy)]
pub struct SupervisorFuncRef(*const VMCallerCheckedAnyfunc);
/// The state required to construct a FunctionExecutor context. The context only lasts for one host
/// call, whereas the state is maintained for the duration of a Wasm runtime call, which may make
/// many different host calls that must share state.
///
/// This is stored as part of the host state of the "env" Wasmtime instance.
pub struct FunctionExecutorState {
sandbox_store: sandbox::Store<SupervisorFuncRef>,
heap: FreeingBumpHeapAllocator,
}
impl FunctionExecutorState {
/// Constructs a new `FunctionExecutorState`.
pub fn new(heap_base: u32) -> Self {
FunctionExecutorState {
sandbox_store: sandbox::Store::new(),
heap: FreeingBumpHeapAllocator::new(heap_base),
}
}
/// Returns a mutable reference to the heap allocator.
pub fn heap(&mut self) -> &mut FreeingBumpHeapAllocator {
&mut self.heap
}
}
/// A `FunctionExecutor` implements `FunctionContext` for making host calls from a Wasmtime
/// runtime. The `FunctionExecutor` exists only for the lifetime of the call and borrows state from
/// a longer-living `FunctionExecutorState`.
pub struct FunctionExecutor<'a> {
compiler: &'a mut Compiler,
sandbox_store: &'a mut sandbox::Store<SupervisorFuncRef>,
heap: &'a mut FreeingBumpHeapAllocator,
memory: &'a mut [u8],
table: Option<&'a [VMCallerCheckedAnyfunc]>,
}
impl<'a> FunctionExecutor<'a> {
/// Construct a new `FunctionExecutor`.
///
/// The vmctx MUST come from a call to a function in the "env" module.
/// The state MUST be looked up from the host state of the "env" module.
pub unsafe fn new(
vmctx: *mut VMContext,
compiler: &'a mut Compiler,
state: &'a mut FunctionExecutorState,
) -> Result<Self>
{
let memory = match (*vmctx).lookup_global_export("memory") {
Some(Export::Memory { definition, vmctx: _, memory: _ }) =>
std::slice::from_raw_parts_mut(
(*definition).base,
(*definition).current_length,
),
_ => return Err(Error::InvalidMemoryReference),
};
let table = match (*vmctx).lookup_global_export("__indirect_function_table") {
Some(Export::Table { definition, vmctx: _, table: _ }) =>
Some(std::slice::from_raw_parts(
(*definition).base as *const VMCallerCheckedAnyfunc,
(*definition).current_elements as usize,
)),
_ => None,
};
Ok(FunctionExecutor {
compiler,
sandbox_store: &mut state.sandbox_store,
heap: &mut state.heap,
memory,
table,
})
}
}
impl<'a> SandboxCapabilities for FunctionExecutor<'a> {
type SupervisorFuncRef = SupervisorFuncRef;
fn store(&self) -> &sandbox::Store<Self::SupervisorFuncRef> {
&self.sandbox_store
}
fn store_mut(&mut self) -> &mut sandbox::Store<Self::SupervisorFuncRef> {
&mut self.sandbox_store
}
fn allocate(&mut self, len: WordSize) -> Result<Pointer<u8>> {
self.heap.allocate(self.memory, len)
}
fn deallocate(&mut self, ptr: Pointer<u8>) -> Result<()> {
self.heap.deallocate(self.memory, ptr)
}
fn write_memory(&mut self, ptr: Pointer<u8>, data: &[u8]) -> Result<()> {
write_memory_from(self.memory, ptr, data)
}
fn read_memory(&self, ptr: Pointer<u8>, len: WordSize) -> Result<Vec<u8>> {
let mut output = vec![0; len as usize];
read_memory_into(self.memory, ptr, output.as_mut())?;
Ok(output)
}
fn invoke(
&mut self,
dispatch_thunk: &Self::SupervisorFuncRef,
invoke_args_ptr: Pointer<u8>,
invoke_args_len: WordSize,
state: u32,
func_idx: SupervisorFuncIndex,
) -> Result<i64>
{
let func_ptr = unsafe { (*dispatch_thunk.0).func_ptr };
let vmctx = unsafe { (*dispatch_thunk.0).vmctx };
// The following code is based on the wasmtime_jit::Context::invoke.
let value_size = mem::size_of::<VMInvokeArgument>();
let (signature, mut values_vec) = generate_signature_and_args(
&[
Value::I32(u32::from(invoke_args_ptr) as i32),
Value::I32(invoke_args_len as i32),
Value::I32(state as i32),
Value::I32(usize::from(func_idx) as i32),
],
Some(ValueType::I64),
self.compiler.frontend_config(),
);
// Get the trampoline to call for this function.
let exec_code_buf = self.compiler
.get_published_trampoline(func_ptr, &signature, value_size)
.map_err(ActionError::Setup)
.map_err(Error::Wasmtime)?;
// Call the trampoline.
if let Err(message) = unsafe {
wasmtime_call_trampoline(
vmctx,
exec_code_buf,
values_vec.as_mut_ptr() as *mut u8,
)
} {
return Err(Error::Other(message));
}
// Load the return value out of `values_vec`.
Ok(unsafe { ptr::read(values_vec.as_ptr() as *const i64) })
}
}
impl<'a> FunctionContext for FunctionExecutor<'a> {
fn read_memory_into(&self, address: Pointer<u8>, dest: &mut [u8]) -> WResult<()> {
read_memory_into(self.memory, address, dest).map_err(|e| e.to_string())
}
fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> WResult<()> {
write_memory_from(self.memory, address, data).map_err(|e| e.to_string())
}
fn allocate_memory(&mut self, size: WordSize) -> WResult<Pointer<u8>> {
self.heap.allocate(self.memory, size).map_err(|e| e.to_string())
}
fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> WResult<()> {
self.heap.deallocate(self.memory, ptr).map_err(|e| e.to_string())
}
fn sandbox(&mut self) -> &mut dyn Sandbox {
self
}
}
impl<'a> Sandbox for FunctionExecutor<'a> {
fn memory_get(
&mut self,
memory_id: MemoryId,
offset: WordSize,
buf_ptr: Pointer<u8>,
buf_len: WordSize,
) -> WResult<u32>
{
let sandboxed_memory = self.sandbox_store.memory(memory_id)
.map_err(|e| e.to_string())?;
sandboxed_memory.with_direct_access(|memory| {
let len = buf_len as usize;
let src_range = match checked_range(offset as usize, len, memory.len()) {
Some(range) => range,
None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
};
let dst_range = match checked_range(buf_ptr.into(), len, self.memory.len()) {
Some(range) => range,
None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
};
&mut self.memory[dst_range].copy_from_slice(&memory[src_range]);
Ok(sandbox_primitives::ERR_OK)
})
}
fn memory_set(
&mut self,
memory_id: MemoryId,
offset: WordSize,
val_ptr: Pointer<u8>,
val_len: WordSize,
) -> WResult<u32>
{
let sandboxed_memory = self.sandbox_store.memory(memory_id)
.map_err(|e| e.to_string())?;
sandboxed_memory.with_direct_access_mut(|memory| {
let len = val_len as usize;
let src_range = match checked_range(val_ptr.into(), len, self.memory.len()) {
Some(range) => range,
None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
};
let dst_range = match checked_range(offset as usize, len, memory.len()) {
Some(range) => range,
None => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
};
&mut memory[dst_range].copy_from_slice(&self.memory[src_range]);
Ok(sandbox_primitives::ERR_OK)
})
}
fn memory_teardown(&mut self, memory_id: MemoryId)
-> WResult<()>
{
self.sandbox_store.memory_teardown(memory_id).map_err(|e| e.to_string())
}
fn memory_new(&mut self, initial: u32, maximum: MemoryId) -> WResult<u32> {
self.sandbox_store.new_memory(initial, maximum).map_err(|e| e.to_string())
}
fn invoke(
&mut self,
instance_id: u32,
export_name: &str,
args: &[u8],
return_val: Pointer<u8>,
return_val_len: u32,
state: u32,
) -> WResult<u32> {
trace!(target: "sr-sandbox", "invoke, instance_idx={}", instance_id);
// Deserialize arguments and convert them into wasmi types.
let args = Vec::<sandbox_primitives::TypedValue>::decode(&mut &args[..])
.map_err(|_| "Can't decode serialized arguments for the invocation")?
.into_iter()
.map(Into::into)
.collect::<Vec<_>>();
let instance = self.sandbox_store.instance(instance_id).map_err(|e| e.to_string())?;
let result = instance.invoke(export_name, &args, self, state);
match result {
Ok(None) => Ok(sandbox_primitives::ERR_OK),
Ok(Some(val)) => {
// Serialize return value and write it back into the memory.
sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| {
if val.len() > return_val_len as usize {
Err("Return value buffer is too small")?;
}
FunctionContext::write_memory(self, return_val, val)?;
Ok(sandbox_primitives::ERR_OK)
})
}
Err(_) => Ok(sandbox_primitives::ERR_EXECUTION),
}
}
fn instance_teardown(&mut self, instance_id: u32) -> WResult<()> {
self.sandbox_store.instance_teardown(instance_id).map_err(|e| e.to_string())
}
fn instance_new(&mut self, dispatch_thunk_id: u32, wasm: &[u8], raw_env_def: &[u8], state: u32)
-> WResult<u32>
{
// Extract a dispatch thunk from instance's table by the specified index.
let dispatch_thunk = {
let table = self.table.as_ref()
.ok_or_else(|| "Runtime doesn't have a table; sandbox is unavailable")?;
let func_ref = table.get(dispatch_thunk_id as usize)
.ok_or_else(|| "dispatch_thunk_idx is out of the table bounds")?;
SupervisorFuncRef(func_ref)
};
let instance_idx_or_err_code =
match sandbox::instantiate(self, dispatch_thunk, wasm, raw_env_def, state) {
Ok(instance_idx) => instance_idx,
Err(sandbox::InstantiationError::StartTrapped) =>
sandbox_primitives::ERR_EXECUTION,
Err(_) => sandbox_primitives::ERR_MODULE,
};
Ok(instance_idx_or_err_code as u32)
}
}
// The storage for a Wasmtime invocation argument.
#[derive(Debug, Default, Copy, Clone)]
#[repr(C, align(8))]
struct VMInvokeArgument([u8; 8]);
fn generate_signature_and_args(
args: &[Value],
result_type: Option<ValueType>,
frontend_config: TargetFrontendConfig,
) -> (ir::Signature, Vec<VMInvokeArgument>)
{
// This code is based on the wasmtime_jit::Context::invoke.
let param_types = args.iter()
.map(|arg| arg.value_type())
.collect::<Vec<_>>();
let signature = translate_signature(
cranelift_ir_signature(
Signature::new(param_types, result_type),
&frontend_config.default_call_conv
),
frontend_config.pointer_type()
);
let mut values_vec = vec![
VMInvokeArgument::default();
cmp::max(args.len(), result_type.iter().len())
];
// Store the argument values into `values_vec`.
for (index, arg) in args.iter().enumerate() {
unsafe {
let ptr = values_vec.as_mut_ptr().add(index);
match arg {
Value::I32(x) => ptr::write(ptr as *mut i32, *x),
Value::I64(x) => ptr::write(ptr as *mut i64, *x),
Value::F32(x) => ptr::write(ptr as *mut u32, *x),
Value::F64(x) => ptr::write(ptr as *mut u64, *x),
}
}
}
(signature, values_vec)
}
@@ -0,0 +1,24 @@
// 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/>.
///! Defines a `WasmRuntime` that uses the Wasmtime JIT to execute.
mod function_executor;
mod runtime;
mod trampoline;
mod util;
pub use runtime::create_instance;
@@ -0,0 +1,398 @@
// 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/>.
//! 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;
use crate::wasmtime::trampoline::{EnvState, make_trampoline};
use crate::wasmtime::util::{cranelift_ir_signature, read_memory_into, write_memory_from};
use crate::{Externalities, RuntimeVersion};
use codec::Decode;
use cranelift_codegen::ir;
use cranelift_codegen::isa::TargetIsa;
use cranelift_entity::{EntityRef, PrimaryMap};
use cranelift_frontend::FunctionBuilderContext;
use cranelift_wasm::DefinedFuncIndex;
use std::cell::RefCell;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::panic::AssertUnwindSafe;
use std::rc::Rc;
use wasm_interface::{HostFunctions, Pointer, WordSize};
use wasmtime_environ::{Module, translate_signature};
use wasmtime_jit::{
ActionOutcome, ActionError, CodeMemory, CompilationStrategy, CompiledModule, Compiler, Context,
SetupError, RuntimeValue,
};
use wasmtime_runtime::{Export, Imports, InstanceHandle, VMFunctionBody};
/// A `WasmRuntime` implementation using the Wasmtime JIT to compile the runtime module to native
/// and execute the compiled code.
pub struct WasmtimeRuntime {
module: CompiledModule,
context: Context,
max_heap_pages: Option<u32>,
heap_pages: u32,
version: Option<RuntimeVersion>,
}
impl WasmRuntime for WasmtimeRuntime {
fn update_heap_pages(&mut self, heap_pages: u64) -> bool {
match heap_pages_valid(heap_pages, self.max_heap_pages) {
Some(heap_pages) => {
self.heap_pages = heap_pages;
true
}
None => false,
}
}
fn call(&mut self, ext: &mut dyn Externalities, method: &str, data: &[u8]) -> Result<Vec<u8>> {
call_method(
&mut self.context,
&mut self.module,
ext,
method,
data,
self.heap_pages,
)
}
fn version(&self) -> Option<RuntimeVersion> {
self.version.clone()
}
}
/// 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<E: Externalities>(ext: &mut E, code: &[u8], heap_pages: u64)
-> std::result::Result<WasmtimeRuntime, WasmError>
{
let (mut compiled_module, mut context) = create_compiled_unit(code)?;
// Inspect the module for the min and max memory sizes.
let (min_memory_size, max_memory_size) = {
let module = compiled_module.module_ref();
let memory_index = match module.exports.get("memory") {
Some(wasmtime_environ::Export::Memory(memory_index)) => *memory_index,
_ => return Err(WasmError::InvalidMemory),
};
let memory_plan = module.memory_plans.get(memory_index)
.expect("memory_index is retrieved from the module's exports map; qed");
(memory_plan.memory.minimum, memory_plan.memory.maximum)
};
// Check that heap_pages is within the allowed range.
let max_heap_pages = max_memory_size.map(|max| max.saturating_sub(min_memory_size));
let heap_pages = heap_pages_valid(heap_pages, max_heap_pages)
.ok_or_else(|| WasmError::InvalidHeapPages)?;
// Call to determine runtime version.
let version_result = {
// `ext` is already implicitly handled as unwind safe, as we store it in a global variable.
let mut ext = AssertUnwindSafe(ext);
// The following unwind safety assertions are OK because if the method call panics, the
// context and compiled module will be dropped.
let mut context = AssertUnwindSafe(&mut context);
let mut compiled_module = AssertUnwindSafe(&mut compiled_module);
crate::native_executor::safe_call(move || {
call_method(
&mut **context,
&mut **compiled_module,
&mut **ext,
"Core_version",
&[],
heap_pages
)
}).map_err(|_| {
WasmError::Instantiation("panic in call to get runtime version".to_string())
})?
};
let version = version_result
.ok()
.and_then(|v| RuntimeVersion::decode(&mut v.as_slice()).ok());
Ok(WasmtimeRuntime {
module: compiled_module,
context,
max_heap_pages,
heap_pages,
version,
})
}
fn create_compiled_unit(code: &[u8])
-> std::result::Result<(CompiledModule, Context), WasmError>
{
let compilation_strategy = CompilationStrategy::Cranelift;
let compiler = new_compiler(compilation_strategy)?;
let mut context = Context::new(Box::new(compiler));
// Enable/disable producing of debug info.
context.set_debug_info(false);
// 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)?;
context.name_instance("env".to_owned(), env_module);
// Compile the wasm module.
let module = context.compile_module(&code)
.map_err(WasmError::WasmtimeSetup)?;
Ok((module, context))
}
/// Call a function inside a precompiled Wasm module.
fn call_method(
context: &mut Context,
module: &mut CompiledModule,
ext: &mut dyn Externalities,
method: &str,
data: &[u8],
heap_pages: u32,
) -> Result<Vec<u8>> {
// Old exports get clobbered in `InstanceHandle::new` if we don't explicitly remove them first.
//
// The global exports mechanism is temporary in Wasmtime and expected to be removed.
// https://github.com/CraneStation/wasmtime/issues/332
clear_globals(&mut *context.get_global_exports().borrow_mut());
let mut instance = module.instantiate()
.map_err(SetupError::Instantiate)
.map_err(ActionError::Setup)
.map_err(Error::Wasmtime)?;
// Ideally there would be a way to set the heap pages during instantiation rather than
// growing the memory after the fact. Currently this may require an additional mmap and copy.
// However, the wasmtime API doesn't support modifying the size of memory on instantiation
// at this time.
grow_memory(&mut instance, heap_pages)?;
// Initialize the function executor state.
let heap_base = get_heap_base(&instance)?;
let executor_state = FunctionExecutorState::new(heap_base);
reset_env_state_and_take_trap(context, Some(executor_state))?;
// Write the input data into guest memory.
let (data_ptr, data_len) = inject_input_data(context, &mut instance, data)?;
let args = [RuntimeValue::I32(u32::from(data_ptr) as i32), RuntimeValue::I32(data_len as i32)];
// Invoke the function in the runtime.
let outcome = externalities::set_and_run_with_externalities(ext, || {
context
.invoke(&mut instance, method, &args[..])
.map_err(Error::Wasmtime)
})?;
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),
_ => return Err(Error::InvalidReturn),
}
ActionOutcome::Trapped { message } =>
return Err(trap_error.unwrap_or_else(||
format!("Wasm execution trapped: {}", message).into()
)),
};
// Read the output data from guest memory.
let mut output = vec![0; output_len as usize];
let memory = get_memory_mut(&mut instance)?;
read_memory_into(memory, output_ptr, &mut output)?;
Ok(output)
}
/// The implementation is based on wasmtime_wasi::instantiate_wasi.
fn instantiate_env_module(
global_exports: Rc<RefCell<HashMap<String, Option<Export>>>>,
compiler: Compiler,
) -> std::result::Result<InstanceHandle, WasmError>
{
let isa = target_isa()?;
let pointer_type = isa.pointer_type();
let call_conv = isa.default_call_conv();
let mut fn_builder_ctx = FunctionBuilderContext::new();
let mut module = Module::new();
let mut finished_functions = <PrimaryMap<DefinedFuncIndex, *const VMFunctionBody>>::new();
let mut code_memory = CodeMemory::new();
for function in SubstrateExternals::functions().iter() {
let sig = translate_signature(
cranelift_ir_signature(function.signature(), &call_conv),
pointer_type
);
let sig_id = module.signatures.push(sig.clone());
let func_id = module.functions.push(sig_id);
module
.exports
.insert(function.name().to_string(), wasmtime_environ::Export::Function(func_id));
let trampoline = make_trampoline(
isa.as_ref(),
&mut code_memory,
&mut fn_builder_ctx,
func_id.index() as u32,
&sig,
)?;
finished_functions.push(trampoline);
}
code_memory.publish();
let imports = Imports::none();
let data_initializers = Vec::new();
let signatures = PrimaryMap::new();
let env_state = EnvState::new::<SubstrateExternals>(code_memory, compiler);
let result = InstanceHandle::new(
Rc::new(module),
global_exports,
finished_functions.into_boxed_slice(),
imports,
&data_initializers,
signatures.into_boxed_slice(),
None,
Box::new(env_state),
);
result.map_err(|e| WasmError::WasmtimeSetup(SetupError::Instantiate(e)))
}
/// Build a new TargetIsa for the host machine.
fn target_isa() -> std::result::Result<Box<dyn TargetIsa>, WasmError> {
let isa_builder = cranelift_native::builder()
.map_err(WasmError::MissingCompilerSupport)?;
let flag_builder = cranelift_codegen::settings::builder();
Ok(isa_builder.finish(cranelift_codegen::settings::Flags::new(flag_builder)))
}
fn new_compiler(strategy: CompilationStrategy) -> std::result::Result<Compiler, WasmError> {
let isa = target_isa()?;
Ok(Compiler::new(isa, strategy))
}
fn clear_globals(global_exports: &mut HashMap<String, Option<Export>>) {
global_exports.remove("memory");
global_exports.remove("__heap_base");
global_exports.remove("__indirect_function_table");
}
fn grow_memory(instance: &mut InstanceHandle, pages: u32) -> Result<()> {
// This is safe to wrap in an unsafe block as:
// - The result of the `lookup_immutable` call is not mutated
// - The definition pointer is returned by a lookup on a valid instance
let memory_index = unsafe {
match instance.lookup_immutable("memory") {
Some(Export::Memory { definition, vmctx: _, memory: _ }) =>
instance.memory_index(&*definition),
_ => return Err(Error::InvalidMemoryReference),
}
};
instance.memory_grow(memory_index, pages)
.map(|_| ())
.ok_or_else(|| "requested heap_pages would exceed maximum memory size".into())
}
fn get_env_state(context: &mut Context) -> Result<&mut EnvState> {
let env_instance = context.get_instance("env")
.map_err(|err| format!("cannot find \"env\" module: {}", err))?;
env_instance
.host_state()
.downcast_mut::<EnvState>()
.ok_or_else(|| "cannot get \"env\" module host state".into())
}
fn reset_env_state_and_take_trap(
context: &mut Context,
executor_state: Option<FunctionExecutorState>,
) -> Result<Option<Error>>
{
let env_state = get_env_state(context)?;
env_state.executor_state = executor_state;
Ok(env_state.take_trap())
}
fn inject_input_data(
context: &mut Context,
instance: &mut InstanceHandle,
data: &[u8],
) -> Result<(Pointer<u8>, WordSize)> {
let env_state = get_env_state(context)?;
let executor_state = env_state.executor_state
.as_mut()
.ok_or_else(|| "cannot get \"env\" module executor state")?;
let memory = get_memory_mut(instance)?;
let data_len = data.len() as WordSize;
let data_ptr = executor_state.heap().allocate(memory, data_len)?;
write_memory_from(memory, data_ptr, data)?;
Ok((data_ptr, data_len))
}
fn get_memory_mut(instance: &mut InstanceHandle) -> Result<&mut [u8]> {
match instance.lookup("memory") {
// This is safe to wrap in an unsafe block as:
// - The definition pointer is returned by a lookup on a valid instance and thus points to
// a valid memory definition
Some(Export::Memory { definition, vmctx: _, memory: _ }) => unsafe {
Ok(std::slice::from_raw_parts_mut(
(*definition).base,
(*definition).current_length,
))
},
_ => Err(Error::InvalidMemoryReference),
}
}
fn get_heap_base(instance: &InstanceHandle) -> Result<u32> {
// This is safe to wrap in an unsafe block as:
// - The result of the `lookup_immutable` call is not mutated
// - The definition pointer is returned by a lookup on a valid instance
// - The defined value is checked to be an I32, which can be read safely as a u32
unsafe {
match instance.lookup_immutable("__heap_base") {
Some(Export::Global { definition, vmctx: _, global })
if global.ty == ir::types::I32 =>
Ok(*(*definition).as_u32()),
_ => return Err(Error::HeapBaseNotFoundOrInvalid),
}
}
}
/// Checks whether the heap_pages parameter is within the valid range and converts it to a u32.
/// Returns None if heaps_pages in not in range.
fn heap_pages_valid(heap_pages: u64, max_heap_pages: Option<u32>)
-> Option<u32>
{
let heap_pages = u32::try_from(heap_pages).ok()?;
if let Some(max_heap_pages) = max_heap_pages {
if heap_pages > max_heap_pages {
return None;
}
}
Some(heap_pages)
}
@@ -0,0 +1,329 @@
// 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/>.
//! The trampoline is the dynamically generated entry point to a runtime host call.
//!
//! This code is based on and large parts are copied from wasmtime's
//! wasmtime-api/src/trampoline/func.rs.
use cranelift_codegen::{Context, binemit, ir, isa};
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 crate::error::{Error, WasmError};
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;
/// The only Wasm types allowed in host function signatures (I32, I64, F32, F64) are all
/// represented in at most 8 bytes.
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],
compiler: Compiler,
// The code memory must be kept around on the state to prevent it from being dropped.
#[allow(dead_code)]
code_memory: CodeMemory,
trap: Option<Error>,
/// The executor state stored across host calls during a single Wasm runtime call.
/// During a runtime call, this MUST be `Some`.
pub executor_state: Option<FunctionExecutorState>,
}
impl EnvState {
/// Construct a new `EnvState` which owns the given code memory.
pub fn new<HF: HostFunctions>(code_memory: CodeMemory, compiler: Compiler) -> Self {
EnvState {
externals: HF::functions(),
trap: None,
compiler,
code_memory,
executor_state: None,
}
}
/// Resets the trap error to None and returns the current value.
pub fn take_trap(&mut self) -> Option<Error> {
self.trap.take()
}
}
/// This is called by the dynamically generated trampoline taking the function index and reference
/// 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>() {
match stub_fn_inner(
vmctx,
state.externals,
&mut state.compiler,
state.executor_state.as_mut(),
func_index,
values_vec,
) {
Ok(()) => CALL_SUCCESS,
Err(err) => {
state.trap = Some(err);
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)
}
/// Implements most of the logic in `stub_fn` but returning a `Result` instead of an integer error
/// for the sake of readability.
unsafe fn stub_fn_inner(
vmctx: *mut VMContext,
externals: &[&dyn Function],
compiler: &mut Compiler,
executor_state: Option<&mut FunctionExecutorState>,
func_index: u32,
values_vec: *mut i64,
) -> 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
.ok_or_else(|| "executor state is None during call to external function")?;
// 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, &param_type)| read_value_from(values_vec.offset(i as isize), param_type));
// Execute and write output back to the stack.
let return_val = func.execute(&mut context, &mut args)
.map_err(|e| Error::FunctionExecution(func.name().to_string(), e))?;
if let Some(val) = return_val {
write_value_to(values_vec, val);
}
Ok(())
}
/// Create a trampoline for invoking a host function.
///
/// The trampoline is a dynamically generated entry point to a runtime host call. The function is
/// generated by manually constructing Cranelift IR and using the Cranelift compiler. The
/// trampoline embeds the function index as a constant and delegates to a stub function in Rust,
/// which takes the function index and a memory reference to the stack arguments and return value
/// slots.
///
/// This code is of modified copy of wasmtime's wasmtime-api/src/trampoline/func.rs.
pub fn make_trampoline(
isa: &dyn isa::TargetIsa,
code_memory: &mut CodeMemory,
fn_builder_ctx: &mut FunctionBuilderContext,
func_index: u32,
signature: &ir::Signature,
) -> Result<*const VMFunctionBody, WasmError> {
// Mostly reverse copy of the similar method from wasmtime's
// wasmtime-jit/src/compiler.rs.
let pointer_type = isa.pointer_type();
let mut stub_sig = ir::Signature::new(isa.frontend_config().default_call_conv);
// Ensure that the first parameter of the generated function is the `VMContext` pointer.
assert_eq!(
signature.params[0],
ir::AbiParam::special(pointer_type, ir::ArgumentPurpose::VMContext)
);
// Add the `vmctx` parameter.
stub_sig.params.push(ir::AbiParam::special(
pointer_type,
ir::ArgumentPurpose::VMContext,
));
// Add the `func_index` parameter.
stub_sig.params.push(ir::AbiParam::new(ir::types::I32));
// Add the `values_vec` parameter.
stub_sig.params.push(ir::AbiParam::new(pointer_type));
// Add error/trap return.
stub_sig.returns.push(ir::AbiParam::new(ir::types::I32));
// Each parameter and return value gets a 64-bit (8-byte) wide slot on the stack, as that is
// large enough to fit all Wasm primitive types that can be used in host function signatures.
// The `VMContext` pointer, which is a parameter of the function signature, is excluded as it
// is passed directly to the stub function rather than being looked up on the caller stack from
// the `values_vec` pointer.
let values_vec_len = cmp::max(signature.params.len() - 1, signature.returns.len());
let values_vec_size = (MAX_WASM_TYPE_SIZE * values_vec_len) as u32;
let mut context = Context::new();
context.func =
ir::Function::with_name_signature(ir::ExternalName::user(0, 0), signature.clone());
let ss = context.func.create_stack_slot(StackSlotData::new(
StackSlotKind::ExplicitSlot,
values_vec_size,
));
{
let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx);
let block0 = builder.create_ebb();
builder.append_ebb_params_for_function_params(block0);
builder.switch_to_block(block0);
builder.seal_block(block0);
let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0);
let mflags = ir::MemFlags::trusted();
for i in 1..signature.params.len() {
let val = builder.func.dfg.ebb_params(block0)[i];
builder.ins().store(
mflags,
val,
values_vec_ptr_val,
((i - 1) * MAX_WASM_TYPE_SIZE) as i32,
);
}
let vmctx_ptr_val = builder.func.dfg.ebb_params(block0)[0];
let func_index_val = builder.ins().iconst(ir::types::I32, func_index as i64);
let callee_args = vec![vmctx_ptr_val, func_index_val, values_vec_ptr_val];
let new_sig = builder.import_signature(stub_sig.clone());
let callee_value = builder
.ins()
.iconst(pointer_type, stub_fn as *const VMFunctionBody as i64);
let call = builder
.ins()
.call_indirect(new_sig, callee_value, &callee_args);
let call_result = builder.func.dfg.inst_results(call)[0];
builder.ins().trapnz(call_result, TrapCode::User(TRAP_USER_CODE));
let mflags = ir::MemFlags::trusted();
let mut results = Vec::new();
for (i, r) in signature.returns.iter().enumerate() {
let load = builder.ins().load(
r.value_type,
mflags,
values_vec_ptr_val,
(i * MAX_WASM_TYPE_SIZE) as i32,
);
results.push(load);
}
builder.ins().return_(&results);
builder.finalize()
}
let mut code_buf: Vec<u8> = Vec::new();
let mut reloc_sink = RelocSink;
let mut trap_sink = binemit::NullTrapSink {};
let mut stackmap_sink = binemit::NullStackmapSink {};
context
.compile_and_emit(
isa,
&mut code_buf,
&mut reloc_sink,
&mut trap_sink,
&mut stackmap_sink,
)
.map_err(|e| WasmError::Instantiation(format!("failed to compile trampoline: {}", e)))?;
let func_ref = code_memory
.allocate_copy_of_byte_slice(&code_buf)
.map_err(|e| WasmError::Instantiation(format!("failed to allocate code memory: {}", e)))?;
Ok(func_ref.as_ptr())
}
/// We don't expect trampoline compilation to produce any relocations, so
/// this `RelocSink` just asserts that it doesn't recieve any.
struct RelocSink;
impl binemit::RelocSink for RelocSink {
fn reloc_ebb(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_ebb_offset: binemit::CodeOffset,
) {
panic!("trampoline compilation should not produce ebb relocs");
}
fn reloc_external(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_name: &ir::ExternalName,
_addend: binemit::Addend,
) {
panic!("trampoline compilation should not produce external symbol relocs");
}
fn reloc_constant(
&mut self,
_code_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_constant_offset: ir::ConstantOffset,
) {
panic!("trampoline compilation should not produce constant relocs");
}
fn reloc_jt(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_jt: ir::JumpTable,
) {
panic!("trampoline compilation should not produce jump table relocs");
}
}
unsafe fn write_value_to(p: *mut i64, val: Value) {
match val {
Value::I32(i) => ptr::write(p as *mut i32, i),
Value::I64(i) => ptr::write(p as *mut i64, i),
Value::F32(u) => ptr::write(p as *mut u32, u),
Value::F64(u) => ptr::write(p as *mut u64, u),
}
}
unsafe fn read_value_from(p: *const i64, ty: ValueType) -> Value {
match ty {
ValueType::I32 => Value::I32(ptr::read(p as *const i32)),
ValueType::I64 => Value::I64(ptr::read(p as *const i64)),
ValueType::F32 => Value::F32(ptr::read(p as *const u32)),
ValueType::F64 => Value::F64(ptr::read(p as *const u64)),
}
}
@@ -0,0 +1,113 @@
// 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::error::{Error, Result};
use cranelift_codegen::{ir, isa};
use std::ops::Range;
use wasm_interface::{Pointer, Signature, ValueType};
/// Read data from a slice of memory into a destination buffer.
///
/// Returns an error if the read would go out of the memory bounds.
pub fn read_memory_into(memory: &[u8], address: Pointer<u8>, dest: &mut [u8]) -> Result<()> {
let range = checked_range(address.into(), dest.len(), memory.len())
.ok_or_else(|| Error::Other("memory read is out of bounds".into()))?;
dest.copy_from_slice(&memory[range]);
Ok(())
}
/// Write data to a slice of memory.
///
/// Returns an error if the write would go out of the memory bounds.
pub fn write_memory_from(memory: &mut [u8], address: Pointer<u8>, data: &[u8]) -> Result<()> {
let range = checked_range(address.into(), data.len(), memory.len())
.ok_or_else(|| Error::Other("memory write is out of bounds".into()))?;
&mut memory[range].copy_from_slice(data);
Ok(())
}
/// Construct a range from an offset to a data length after the offset.
/// Returns None if the end of the range would exceed some maximum offset.
pub fn checked_range(offset: usize, len: usize, max: usize) -> Option<Range<usize>> {
let end = offset.checked_add(len)?;
if end <= max {
Some(offset..end)
} else {
None
}
}
/// Convert a wasm_interface Signature into a cranelift_codegen Signature.
pub fn cranelift_ir_signature(signature: Signature, call_conv: &isa::CallConv) -> ir::Signature {
ir::Signature {
params: signature.args.iter()
.map(cranelift_ir_type)
.map(ir::AbiParam::new)
.collect(),
returns: signature.return_value.iter()
.map(cranelift_ir_type)
.map(ir::AbiParam::new)
.collect(),
call_conv: call_conv.clone(),
}
}
/// Convert a wasm_interface ValueType into a cranelift_codegen Type.
pub fn cranelift_ir_type(value_type: &ValueType) -> ir::types::Type {
match value_type {
ValueType::I32 => ir::types::I32,
ValueType::I64 => ir::types::I64,
ValueType::F32 => ir::types::F32,
ValueType::F64 => ir::types::F64,
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
#[test]
fn test_read_memory_into() {
let mut memory = [0; 20];
let mut dest = [0; 5];
&mut memory[15..20].copy_from_slice(b"hello");
read_memory_into(&memory[..], Pointer::new(15), &mut dest[..]).unwrap();
// Test that out of bounds read fails.
assert_matches!(
read_memory_into(&memory[..], Pointer::new(16), &mut dest[..]),
Err(Error::Other(_))
)
}
#[test]
fn test_write_memory_from() {
let mut memory = [0; 20];
let data = b"hello";
write_memory_from(&mut memory[..], Pointer::new(15), data).unwrap();
// Test that out of bounds write fails.
assert_matches!(
write_memory_from(&mut memory[..], Pointer::new(16), data),
Err(Error::Other(_))
)
}
}
+3
View File
@@ -9,6 +9,9 @@ default = ["rocksdb"]
# The RocksDB feature activates the RocksDB database backend. If it is not activated, and you pass
# a path to a database, an error will be produced at runtime.
rocksdb = ["client_db/kvdb-rocksdb"]
wasmtime = [
"substrate-executor/wasmtime",
]
[dependencies]
derive_more = "0.15.0"
+13 -1
View File
@@ -49,6 +49,18 @@ pub enum Value {
F64(u64),
}
impl Value {
/// Returns the type of this value.
pub fn value_type(&self) -> ValueType {
match self {
Value::I32(_) => ValueType::I32,
Value::I64(_) => ValueType::I64,
Value::F32(_) => ValueType::F32,
Value::F64(_) => ValueType::F64,
}
}
}
/// Provides `Sealed` trait to prevent implementing trait `PointerType` outside of this crate.
mod private {
pub trait Sealed {}
@@ -212,7 +224,7 @@ pub type MemoryId = u32;
pub trait Sandbox {
/// Get sandbox memory from the `memory_id` instance at `offset` into the given buffer.
fn memory_get(
&self,
&mut self,
memory_id: MemoryId,
offset: WordSize,
buf_ptr: Pointer<u8>,
+6
View File
@@ -116,3 +116,9 @@ cli = [
"ctrlc",
"substrate-service/rocksdb"
]
wasmtime = [
"cli",
"node-executor/wasmtime",
"substrate-cli/wasmtime",
"substrate-service/wasmtime",
]
+9 -1
View File
@@ -31,7 +31,15 @@ contracts = { package = "srml-contracts", path = "../../srml/contracts" }
grandpa = { package = "srml-grandpa", path = "../../srml/grandpa" }
indices = { package = "srml-indices", path = "../../srml/indices" }
wabt = "0.9.2"
criterion = "0.3.0"
[features]
benchmarks = []
wasmtime = [
"substrate-executor/wasmtime",
]
stress-test = []
[[bench]]
name = "bench"
harness = false
+196
View File
@@ -0,0 +1,196 @@
// Copyright 2018-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 codec::{Decode, Encode};
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
use node_executor::Executor;
use node_primitives::{BlockNumber, Hash};
use node_runtime::{
Block, BuildStorage, Call, CheckedExtrinsic, GenesisConfig, Header, UncheckedExtrinsic,
};
use node_runtime::constants::currency::*;
use node_testing::keyring::*;
use primitives::{Blake2Hasher, NativeOrEncoded, NeverNativeValue};
use primitives::storage::well_known_keys;
use primitives::traits::CodeExecutor;
use runtime_support::Hashable;
use state_machine::TestExternalities as CoreTestExternalities;
use substrate_executor::{NativeExecutor, RuntimeInfo, WasmExecutionMethod, Externalities};
criterion_group!(benches, bench_execute_block);
criterion_main!(benches);
/// The wasm runtime code.
const COMPACT_CODE: &[u8] = node_runtime::WASM_BINARY;
const GENESIS_HASH: [u8; 32] = [69u8; 32];
const VERSION: u32 = node_runtime::VERSION.spec_version;
const HEAP_PAGES: u64 = 20;
type TestExternalities<H> = CoreTestExternalities<H, u64>;
#[derive(Debug)]
enum ExecutionMethod {
Native,
Wasm(WasmExecutionMethod),
}
fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic {
node_testing::keyring::sign(xt, VERSION, GENESIS_HASH)
}
fn new_test_ext(genesis_config: &GenesisConfig) -> TestExternalities<Blake2Hasher> {
let mut test_ext = TestExternalities::new_with_code(
COMPACT_CODE,
genesis_config.build_storage().unwrap(),
);
test_ext.ext().place_storage(well_known_keys::HEAP_PAGES.to_vec(), Some(HEAP_PAGES.encode()));
test_ext
}
fn construct_block<E: Externalities>(
executor: &NativeExecutor<Executor>,
ext: &mut E,
number: BlockNumber,
parent_hash: Hash,
extrinsics: Vec<CheckedExtrinsic>,
) -> (Vec<u8>, Hash) {
use trie::{TrieConfiguration, trie_types::Layout};
// sign extrinsics.
let extrinsics = extrinsics.into_iter().map(sign).collect::<Vec<_>>();
// calculate the header fields that we can.
let extrinsics_root = Layout::<Blake2Hasher>::ordered_trie_root(
extrinsics.iter().map(Encode::encode)
).to_fixed_bytes()
.into();
let header = Header {
parent_hash,
number,
extrinsics_root,
state_root: Default::default(),
digest: Default::default(),
};
// execute the block to get the real header.
executor.call::<_, NeverNativeValue, fn() -> _>(
ext,
"Core_initialize_block",
&header.encode(),
true,
None,
).0.unwrap();
for i in extrinsics.iter() {
executor.call::<_, NeverNativeValue, fn() -> _>(
ext,
"BlockBuilder_apply_extrinsic",
&i.encode(),
true,
None,
).0.unwrap();
}
let header = match executor.call::<_, NeverNativeValue, fn() -> _>(
ext,
"BlockBuilder_finalize_block",
&[0u8;0],
true,
None,
).0.unwrap() {
NativeOrEncoded::Native(_) => unreachable!(),
NativeOrEncoded::Encoded(h) => Header::decode(&mut &h[..]).unwrap(),
};
let hash = header.blake2_256();
(Block { header, extrinsics }.encode(), hash.into())
}
fn test_blocks(genesis_config: &GenesisConfig, executor: &NativeExecutor<Executor>)
-> Vec<(Vec<u8>, Hash)>
{
let mut test_ext = new_test_ext(genesis_config);
let mut block1_extrinsics = vec![
CheckedExtrinsic {
signed: None,
function: Call::Timestamp(timestamp::Call::set(42 * 1000)),
},
];
block1_extrinsics.extend((0..20).map(|i| {
CheckedExtrinsic {
signed: Some((alice(), signed_extra(i, 0))),
function: Call::Balances(balances::Call::transfer(bob().into(), 1 * DOLLARS)),
}
}));
let block1 = construct_block(
executor,
&mut test_ext.ext(),
1,
GENESIS_HASH.into(),
block1_extrinsics,
);
vec![block1]
}
fn bench_execute_block(c: &mut Criterion) {
c.bench_function_over_inputs(
"execute blocks",
|b, strategy| {
let genesis_config = node_testing::genesis::config(false, Some(COMPACT_CODE));
let (use_native, wasm_method) = match strategy {
ExecutionMethod::Native => (true, WasmExecutionMethod::Interpreted),
ExecutionMethod::Wasm(wasm_method) => (false, *wasm_method),
};
let executor = NativeExecutor::new(wasm_method, None);
// Get the runtime version to initialize the runtimes cache.
{
let mut test_ext = new_test_ext(&genesis_config);
executor.runtime_version(&mut test_ext.ext());
}
let blocks = test_blocks(&genesis_config, &executor);
b.iter_batched_ref(
|| new_test_ext(&genesis_config),
|test_ext| {
for block in blocks.iter() {
executor.call::<_, NeverNativeValue, fn() -> _>(
&mut test_ext.ext(),
"Core_execute_block",
&block.0,
use_native,
None,
).0.unwrap();
}
},
BatchSize::LargeInput,
);
},
vec![
ExecutionMethod::Native,
ExecutionMethod::Wasm(WasmExecutionMethod::Interpreted),
#[cfg(feature = "wasmtime")]
ExecutionMethod::Wasm(WasmExecutionMethod::Compiled),
],
);
}
-21
View File
@@ -17,10 +17,6 @@
//! A `CodeExecutor` specialization which uses natively compiled runtime when the wasm to be
//! executed is equivalent to the natively compiled code.
#![cfg_attr(feature = "benchmarks", feature(test))]
#[cfg(feature = "benchmarks")] extern crate test;
pub use substrate_executor::NativeExecutor;
use substrate_executor::native_executor_instance;
@@ -1208,21 +1204,4 @@ mod tests {
block_number += 1;
}
}
#[cfg(feature = "benchmarks")]
mod benches {
use super::*;
use test::Bencher;
#[bench]
fn wasm_execute_block(b: &mut Bencher) {
let (block1, block2) = blocks();
b.iter(|| {
let mut t = new_test_ext(COMPACT_CODE, false);
WasmExecutor::new().call(&mut t, "Core_execute_block", &block1.0).unwrap();
WasmExecutor::new().call(&mut t, "Core_execute_block", &block2.0).unwrap();
});
}
}
}