Sandboxing and the simplest smart-contract runtime (#140)

* Add primitives for sandboxing.

* Add sandbox module.

* Implement the runtime part of the sandbox.

* Rebuild binaries.

* Implement smart-contract execution.

* Add more documentation.
This commit is contained in:
Sergey Pepyakin
2018-05-01 21:32:01 +03:00
committed by Robert Habermeier
parent f116f67382
commit 5a56fbcea3
39 changed files with 2470 additions and 56 deletions
+52 -4
View File
@@ -176,6 +176,14 @@ dependencies = [
"yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cmake"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "coco"
version = "0.1.1"
@@ -791,7 +799,7 @@ dependencies = [
[[package]]
name = "integer-sqrt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
source = "git+https://github.com/paritytech/integer-sqrt-rs.git#f4cf61482096dc98c1273f46a10849d182b4c23c"
[[package]]
name = "iovec"
@@ -1840,6 +1848,7 @@ dependencies = [
"substrate-serializer 0.1.0",
"substrate-state-machine 0.1.0",
"triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"wabt 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1909,6 +1918,7 @@ dependencies = [
"substrate-serializer 0.1.0",
"twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"uint 0.1.2 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)",
"wasmi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1962,7 +1972,7 @@ name = "substrate-runtime-council"
version = "0.1.0"
dependencies = [
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"integer-sqrt 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)",
"safe-mix 0.1.0",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-codec 0.1.0",
@@ -2034,7 +2044,7 @@ dependencies = [
name = "substrate-runtime-primitives"
version = "0.1.0"
dependencies = [
"integer-sqrt 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)",
"num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2045,6 +2055,18 @@ dependencies = [
"substrate-runtime-support 0.1.0",
]
[[package]]
name = "substrate-runtime-sandbox"
version = "0.1.0"
dependencies = [
"rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-codec 0.1.0",
"substrate-primitives 0.1.0",
"substrate-runtime-io 0.1.0",
"substrate-runtime-std 0.1.0",
"wasmi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "substrate-runtime-session"
version = "0.1.0"
@@ -2076,10 +2098,12 @@ dependencies = [
"substrate-runtime-consensus 0.1.0",
"substrate-runtime-io 0.1.0",
"substrate-runtime-primitives 0.1.0",
"substrate-runtime-sandbox 0.1.0",
"substrate-runtime-session 0.1.0",
"substrate-runtime-std 0.1.0",
"substrate-runtime-support 0.1.0",
"substrate-runtime-system 0.1.0",
"wabt 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -2476,6 +2500,27 @@ name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wabt"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"wabt-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wabt-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cmake 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasmi"
version = "0.1.0"
@@ -2589,6 +2634,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536"
"checksum cmake 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "5cf678ceebedde428000cb3a34465cf3606d1a48da17014948a916deac39da7c"
"checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd"
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
@@ -2639,7 +2685,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum hyper 0.11.25 (registry+https://github.com/rust-lang/crates.io-index)" = "549dbb86397490ce69d908425b9beebc85bbaad25157d67479d4995bb56fdf9a"
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
"checksum igd 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "356a0dc23a4fa0f8ce4777258085d00a01ea4923b2efd93538fc44bf5e1bda76"
"checksum integer-sqrt 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8833702c315192502093b244e29c6ab9c55454adfe21b879a87a039ea8fe8520"
"checksum integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)" = "<none>"
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
"checksum ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2134e210e2a024b5684f90e1556d5f71a1ce7f8b12e9ac9924c67fb36f63b336"
"checksum isatty 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f2a233726c7bb76995cec749d59582e5664823b7245d4970354408f1d79a7a2"
@@ -2766,6 +2812,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum wabt 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "e902997144209c90311321b90dd658d964dd8e58b23a5919e66a1d068a0050e5"
"checksum wabt-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fc67b1d96cd7839be6996edf94be66351d83f614e9cc7c6edc33accd9f5e6529"
"checksum wasmi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba06def0c95a653122299e68a44f2f227eeac2d1f707df61f33abbaf6dd55992"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
+1
View File
@@ -37,6 +37,7 @@ members = [
"substrate/rpc-servers",
"substrate/rpc",
"substrate/runtime-io",
"substrate/runtime-sandbox",
"substrate/runtime-std",
"substrate/runtime-support",
"substrate/runtime/consensus",
+1
View File
@@ -1,6 +1,7 @@
#!/bin/sh
# NOTE `cargo install wasm-gc` before running this script.
# NOTE `cargo install --git https://github.com/pepyakin/wasm-export-table.git`
set -e
export CARGO_INCREMENTAL=0
+54 -8
View File
@@ -260,11 +260,6 @@ name = "integer-sqrt"
version = "0.1.0"
source = "git+https://github.com/paritytech/integer-sqrt-rs.git#f4cf61482096dc98c1273f46a10849d182b4c23c"
[[package]]
name = "integer-sqrt"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "isatty"
version = "0.1.6"
@@ -334,6 +329,11 @@ dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memory_units"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memorydb"
version = "0.1.1"
@@ -379,6 +379,16 @@ dependencies = [
"stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parity-wasm"
version = "0.27.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot"
version = "0.4.8"
@@ -388,6 +398,15 @@ dependencies = [
"parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot_core"
version = "0.2.10"
@@ -657,6 +676,7 @@ dependencies = [
"substrate-runtime-std 0.1.0",
"twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"uint 0.1.2 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)",
"wasmi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -679,7 +699,7 @@ name = "substrate-runtime-council"
version = "0.1.0"
dependencies = [
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"integer-sqrt 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)",
"safe-mix 0.1.0",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-codec 0.1.0",
@@ -747,7 +767,7 @@ dependencies = [
name = "substrate-runtime-primitives"
version = "0.1.0"
dependencies = [
"integer-sqrt 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)",
"num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -758,6 +778,18 @@ dependencies = [
"substrate-runtime-support 0.1.0",
]
[[package]]
name = "substrate-runtime-sandbox"
version = "0.1.0"
dependencies = [
"rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-codec 0.1.0",
"substrate-primitives 0.1.0",
"substrate-runtime-io 0.1.0",
"substrate-runtime-std 0.1.0",
"wasmi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "substrate-runtime-session"
version = "0.1.0"
@@ -789,6 +821,7 @@ dependencies = [
"substrate-runtime-consensus 0.1.0",
"substrate-runtime-io 0.1.0",
"substrate-runtime-primitives 0.1.0",
"substrate-runtime-sandbox 0.1.0",
"substrate-runtime-session 0.1.0",
"substrate-runtime-std 0.1.0",
"substrate-runtime-support 0.1.0",
@@ -969,6 +1002,16 @@ name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wasmi"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.2.8"
@@ -1027,7 +1070,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd546ef520ab3745f1aae5f2cdc6de9e6498e94d1ab138b9eb3ddfbf335847fb"
"checksum hex-literal-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2ea76da4c7f1a54d01d54985566d3fdd960b2bbd7b970da024821c883c2d9631"
"checksum integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)" = "<none>"
"checksum integer-sqrt 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7536fe7f78abedb82f609d87f46f0e0ca0ad31e84597deb8dabd8ed9ad047257"
"checksum isatty 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f2a233726c7bb76995cec749d59582e5664823b7245d4970354408f1d79a7a2"
"checksum keccak-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f300c1f149cd9ca5214eed24f6e713a597517420fb8b15499824aa916259ec1"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
@@ -1037,13 +1079,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882"
"checksum memorydb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "013b7e4c5e10c764936ebc6bd3662d8e3c92292d267bf6a42ef3f5cad9c793ee"
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
"checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364"
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
"checksum odds 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bd4dc02a80a0315b109e48992c46942c79bcdb8fac416dd575d330ed9ced6cbd"
"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
"checksum parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd9d732f2de194336fb02fe11f9eed13d9e76f13f4315b4d88a14ca411750cd"
"checksum parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "9f35048d735bb93dd115a0030498785971aab3234d311fbe273d020084d26bd8"
"checksum patricia-trie 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1e2f638d79aba5c4a71a4f373df6e3cd702250a53b7f0ed4da1e2a7be9737ae"
"checksum plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83ae80873992f511142c07d0ec6c44de5636628fdb7e204abd655932ea79d995"
@@ -1085,6 +1130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum wasmi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26b20dbeb7caee04597a5d2c93e2b3e64872c6ea2af732d7ad49dbec44067c35"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+1
View File
@@ -4,3 +4,4 @@ rustup update nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
rustup update stable
cargo install --git https://github.com/alexcrichton/wasm-gc
cargo install --git https://github.com/pepyakin/wasm-export-table.git
+54 -8
View File
@@ -225,11 +225,6 @@ name = "integer-sqrt"
version = "0.1.0"
source = "git+https://github.com/paritytech/integer-sqrt-rs.git#f4cf61482096dc98c1273f46a10849d182b4c23c"
[[package]]
name = "integer-sqrt"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "isatty"
version = "0.1.6"
@@ -299,6 +294,11 @@ dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memory_units"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memorydb"
version = "0.1.1"
@@ -344,6 +344,16 @@ dependencies = [
"stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parity-wasm"
version = "0.27.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot"
version = "0.4.8"
@@ -353,6 +363,15 @@ dependencies = [
"parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot_core"
version = "0.2.10"
@@ -657,6 +676,7 @@ dependencies = [
"substrate-runtime-std 0.1.0",
"twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"uint 0.1.2 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)",
"wasmi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -679,7 +699,7 @@ name = "substrate-runtime-council"
version = "0.1.0"
dependencies = [
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"integer-sqrt 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)",
"safe-mix 0.1.0",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-codec 0.1.0",
@@ -747,7 +767,7 @@ dependencies = [
name = "substrate-runtime-primitives"
version = "0.1.0"
dependencies = [
"integer-sqrt 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)",
"num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -758,6 +778,18 @@ dependencies = [
"substrate-runtime-support 0.1.0",
]
[[package]]
name = "substrate-runtime-sandbox"
version = "0.1.0"
dependencies = [
"rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-codec 0.1.0",
"substrate-primitives 0.1.0",
"substrate-runtime-io 0.1.0",
"substrate-runtime-std 0.1.0",
"wasmi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "substrate-runtime-session"
version = "0.1.0"
@@ -789,6 +821,7 @@ dependencies = [
"substrate-runtime-consensus 0.1.0",
"substrate-runtime-io 0.1.0",
"substrate-runtime-primitives 0.1.0",
"substrate-runtime-sandbox 0.1.0",
"substrate-runtime-session 0.1.0",
"substrate-runtime-std 0.1.0",
"substrate-runtime-support 0.1.0",
@@ -969,6 +1002,16 @@ name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wasmi"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.2.8"
@@ -1027,7 +1070,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd546ef520ab3745f1aae5f2cdc6de9e6498e94d1ab138b9eb3ddfbf335847fb"
"checksum hex-literal-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2ea76da4c7f1a54d01d54985566d3fdd960b2bbd7b970da024821c883c2d9631"
"checksum integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)" = "<none>"
"checksum integer-sqrt 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7536fe7f78abedb82f609d87f46f0e0ca0ad31e84597deb8dabd8ed9ad047257"
"checksum isatty 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f2a233726c7bb76995cec749d59582e5664823b7245d4970354408f1d79a7a2"
"checksum keccak-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f300c1f149cd9ca5214eed24f6e713a597517420fb8b15499824aa916259ec1"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
@@ -1037,13 +1079,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882"
"checksum memorydb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "013b7e4c5e10c764936ebc6bd3662d8e3c92292d267bf6a42ef3f5cad9c793ee"
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
"checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364"
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
"checksum odds 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bd4dc02a80a0315b109e48992c46942c79bcdb8fac416dd575d330ed9ced6cbd"
"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
"checksum parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd9d732f2de194336fb02fe11f9eed13d9e76f13f4315b4d88a14ca411750cd"
"checksum parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "9f35048d735bb93dd115a0030498785971aab3234d311fbe273d020084d26bd8"
"checksum patricia-trie 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1e2f638d79aba5c4a71a4f373df6e3cd702250a53b7f0ed4da1e2a7be9737ae"
"checksum plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83ae80873992f511142c07d0ec6c44de5636628fdb7e204abd655932ea79d995"
@@ -1085,6 +1130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum wasmi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26b20dbeb7caee04597a5d2c93e2b3e64872c6ea2af732d7ad49dbec44067c35"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
+1
View File
@@ -4,3 +4,4 @@ rustup update nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
rustup update stable
cargo install --git https://github.com/alexcrichton/wasm-gc
cargo install --git https://github.com/pepyakin/wasm-export-table.git
+25
View File
@@ -61,6 +61,31 @@ pub trait Slicable: Sized {
}
}
impl<T: Slicable, E: Slicable> Slicable for Result<T, E> {
fn decode<I: Input>(input: &mut I) -> Option<Self> {
match input.read_byte()? {
0 => Some(Ok(T::decode(input)?)),
1 => Some(Err(E::decode(input)?)),
_ => None,
}
}
fn encode(&self) -> Vec<u8> {
let mut v = Vec::new();
match *self {
Ok(ref t) => {
v.push(0);
t.using_encoded(|s| v.extend(s));
}
Err(ref e) => {
v.push(1);
e.using_encoded(|s| v.extend(s));
}
}
v
}
}
impl Slicable for Option<bool> {
fn decode<I: Input>(input: &mut I) -> Option<Self> {
match input.read_byte()? {
+1
View File
@@ -22,3 +22,4 @@ log = "0.3"
[dev-dependencies]
assert_matches = "1.1"
wabt = "0.1.7"
+3
View File
@@ -47,12 +47,15 @@ extern crate error_chain;
#[cfg(test)]
extern crate assert_matches;
#[cfg(test)]
extern crate wabt;
#[macro_use]
mod wasm_utils;
mod wasm_executor;
#[macro_use]
mod native_executor;
mod sandbox;
pub mod error;
pub use wasm_executor::WasmExecutor;
+483
View File
@@ -0,0 +1,483 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! This module implements sandboxing support in the runtime.
use codec::Slicable;
use primitives::sandbox as sandbox_primitives;
use std::collections::HashMap;
use wasm_utils::DummyUserError;
use wasmi::memory_units::Pages;
use wasmi::{Externals, FuncRef, ImportResolver, MemoryInstance, MemoryRef, Module, ModuleInstance,
ModuleRef, RuntimeArgs, RuntimeValue, Trap, TrapKind};
/// Index of a function inside the supervisor.
///
/// This is a typically an index in the default table of the supervisor, however
/// the exact meaning of this index is depends on the implementation of dispatch function.
#[derive(Copy, Clone, Debug, PartialEq)]
struct SupervisorFuncIndex(usize);
/// Index of a function within guest index space.
///
/// This index is supposed to be used with as index for `Externals`.
#[derive(Copy, Clone, Debug, PartialEq)]
struct GuestFuncIndex(usize);
/// This struct holds a mapping from guest index space to supervisor.
struct GuestToSuperviserFunctionMapping {
funcs: Vec<SupervisorFuncIndex>,
}
impl GuestToSuperviserFunctionMapping {
fn new() -> GuestToSuperviserFunctionMapping {
GuestToSuperviserFunctionMapping { funcs: Vec::new() }
}
fn define(&mut self, supervisor_func: SupervisorFuncIndex) -> GuestFuncIndex {
let idx = self.funcs.len();
self.funcs.push(supervisor_func);
GuestFuncIndex(idx)
}
fn func_by_guest_index(&self, guest_func_idx: GuestFuncIndex) -> Option<SupervisorFuncIndex> {
self.funcs.get(guest_func_idx.0).cloned()
}
}
struct Imports {
func_map: HashMap<(Vec<u8>, Vec<u8>), GuestFuncIndex>,
memories_map: HashMap<(Vec<u8>, Vec<u8>), MemoryRef>,
}
impl ImportResolver for Imports {
fn resolve_func(
&self,
module_name: &str,
field_name: &str,
signature: &::wasmi::Signature,
) -> Result<FuncRef, ::wasmi::Error> {
let key = (
module_name.as_bytes().to_owned(),
field_name.as_bytes().to_owned(),
);
let idx = *self.func_map.get(&key).ok_or_else(|| {
::wasmi::Error::Instantiation(format!(
"Export {}:{} not found",
module_name, field_name
))
})?;
Ok(::wasmi::FuncInstance::alloc_host(signature.clone(), idx.0))
}
fn resolve_memory(
&self,
module_name: &str,
field_name: &str,
_memory_type: &::wasmi::MemoryDescriptor,
) -> Result<MemoryRef, ::wasmi::Error> {
let key = (
module_name.as_bytes().to_vec(),
field_name.as_bytes().to_vec(),
);
let mem = self.memories_map
.get(&key)
.ok_or_else(|| {
::wasmi::Error::Instantiation(format!(
"Export {}:{} not found",
module_name, field_name
))
})?
.clone();
Ok(mem)
}
fn resolve_global(
&self,
_module_name: &str,
_field_name: &str,
_global_type: &::wasmi::GlobalDescriptor,
) -> Result<::wasmi::GlobalRef, ::wasmi::Error> {
// TODO:
unimplemented!()
}
fn resolve_table(
&self,
_module_name: &str,
_field_name: &str,
_table_type: &::wasmi::TableDescriptor,
) -> Result<::wasmi::TableRef, ::wasmi::Error> {
// TODO:
unimplemented!()
}
}
/// This trait encapsulates sandboxing capabilities.
///
/// Note that this functions are only called in the `supervisor` context.
pub trait SandboxCapabilities {
/// Returns associated sandbox `Store`.
fn store(&self) -> &Store;
/// Allocate space of the specified length in the supervisor memory.
///
/// Returns pointer to the allocated block.
fn allocate(&mut self, len: u32) -> u32;
/// Deallocate space specified by the pointer that was previously returned by [`allocate`].
///
/// [`allocate`]: #tymethod.allocate
fn deallocate(&mut self, ptr: u32);
/// Write `data` into the supervisor memory at offset specified by `ptr`.
///
/// # Errors
///
/// Returns `Err` if `ptr + data.len()` is out of bounds.
fn write_memory(&mut self, ptr: u32, data: &[u8]) -> Result<(), DummyUserError>;
/// Read `len` bytes from the supervisor memory.
///
/// # Errors
///
/// Returns `Err` if `ptr + len` is out of bounds.
fn read_memory(&self, ptr: u32, len: u32) -> Result<Vec<u8>, DummyUserError>;
}
/// Implementation of [`Externals`] that allows execution of guest module with
/// [externals][`Externals`] that might refer functions defined by supervisor.
///
/// [`Externals`]: ../../wasmi/trait.Externals.html
pub struct GuestExternals<'a, FE: SandboxCapabilities + Externals + 'a> {
supervisor_externals: &'a mut FE,
instance_idx: u32,
state: u32,
}
impl<'a, FE: SandboxCapabilities + Externals + 'a> GuestExternals<'a, FE> {
/// Create a new instance of `GuestExternals`.
///
/// It will use `supervisor_externals` to execute calls from guest to supervisor.
/// `instance_idx` required to fetch settings for this particular instance, e.g
/// associated dispatch thunk funtion and mapping between externals function ids to
/// functions in supervisor module.
/// `state` is just an integer that allows supervisor to have arbitrary state associated with the call,
/// typically used for implementing runtime functions.
pub fn new(supervisor_externals: &mut FE, instance_idx: u32, state: u32) -> GuestExternals<FE> {
GuestExternals {
supervisor_externals,
instance_idx,
state,
}
}
}
fn trap() -> Trap {
TrapKind::Host(Box::new(DummyUserError)).into()
}
fn deserialize_result(serialized_result: &[u8]) -> Result<Option<RuntimeValue>, Trap> {
use self::sandbox_primitives::{HostError, ReturnValue};
let result_val = Result::<ReturnValue, HostError>::decode(&mut &serialized_result[..])
.ok_or_else(|| trap())?;
match result_val {
Ok(return_value) => Ok(match return_value {
ReturnValue::Unit => None,
ReturnValue::Value(typed_value) => Some(RuntimeValue::from(typed_value)),
}),
Err(HostError) => Err(trap()),
}
}
impl<'a, FE: SandboxCapabilities + Externals + 'a> Externals for GuestExternals<'a, FE> {
fn invoke_index(
&mut self,
index: usize,
args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Trap> {
let (func_idx, dispatch_thunk) = {
let instance = &self.supervisor_externals.store().instances[self.instance_idx as usize];
let dispatch_thunk = instance.dispatch_thunk.clone();
let func_idx = instance
.guest_to_supervisor_mapping
.func_by_guest_index(
GuestFuncIndex(index)
)
.expect(
"`invoke_index` is called with indexes registered via `FuncInstance::alloc_host`;
`FuncInstance::alloc_host` is called with indexes that was obtained from `guest_to_supervisor_mapping`;
`func_by_guest_index` called with `index` can't return `None`;
qed"
);
(func_idx, dispatch_thunk)
};
// Serialize arguments into a byte vector.
let invoke_args_data: Vec<u8> = args.as_ref()
.iter()
.cloned()
.map(sandbox_primitives::TypedValue::from)
.collect::<Vec<_>>()
.encode();
let state = self.state;
// Move serialized arguments inside the memory and invoke dispatch thunk and
// then free allocated memory.
let invoke_args_ptr = self.supervisor_externals
.allocate(invoke_args_data.len() as u32);
self.supervisor_externals
.write_memory(invoke_args_ptr, &invoke_args_data)?;
let result = ::wasmi::FuncInstance::invoke(
&dispatch_thunk,
&[
RuntimeValue::I32(invoke_args_ptr as i32),
RuntimeValue::I32(invoke_args_data.len() as i32),
RuntimeValue::I32(state as i32),
RuntimeValue::I32(func_idx.0 as i32),
],
self.supervisor_externals,
);
self.supervisor_externals.deallocate(invoke_args_ptr);
// dispatch_thunk returns pointer to serialized arguments.
let (serialized_result_val_ptr, serialized_result_val_len) = match result {
// Unpack pointer and len of the serialized result data.
Ok(Some(RuntimeValue::I64(v))) => {
// Cast to u64 to use zero-extension.
let v = v as u64;
let ptr = (v as u64 >> 32) as u32;
let len = (v & 0xFFFFFFFF) as u32;
(ptr, len)
}
_ => return Err(trap()),
};
let serialized_result_val = self.supervisor_externals
.read_memory(serialized_result_val_ptr, serialized_result_val_len)?;
self.supervisor_externals
.deallocate(serialized_result_val_ptr);
// TODO: check the signature?
deserialize_result(&serialized_result_val)
}
}
struct SandboxInstance {
instance: ModuleRef,
dispatch_thunk: FuncRef,
guest_to_supervisor_mapping: GuestToSuperviserFunctionMapping,
}
fn decode_environment_definition(
raw_env_def: &[u8],
memories: &[MemoryRef],
) -> Result<(Imports, GuestToSuperviserFunctionMapping), DummyUserError> {
let env_def = sandbox_primitives::EnvironmentDefinition::decode(&mut &raw_env_def[..]).ok_or_else(|| DummyUserError)?;
let mut func_map = HashMap::new();
let mut memories_map = HashMap::new();
let mut guest_to_supervisor_mapping = GuestToSuperviserFunctionMapping::new();
for entry in &env_def.entries {
let module = entry.module_name.clone();
let field = entry.field_name.clone();
match entry.entity {
sandbox_primitives::ExternEntity::Function(func_idx) => {
let externals_idx =
guest_to_supervisor_mapping.define(SupervisorFuncIndex(func_idx as usize));
func_map.insert((module, field), externals_idx);
}
sandbox_primitives::ExternEntity::Memory(memory_idx) => {
let memory_ref = memories
.get(memory_idx as usize)
.ok_or_else(|| DummyUserError)?;
memories_map.insert((module, field), memory_ref.clone());
}
}
}
Ok((
Imports {
func_map,
memories_map,
},
guest_to_supervisor_mapping,
))
}
/// This struct keeps track of all sandboxed components.
pub struct Store {
instances: Vec<SandboxInstance>,
memories: Vec<MemoryRef>,
}
impl Store {
/// Create new empty sandbox store.
pub fn new() -> Store {
Store {
instances: Vec::new(),
memories: Vec::new(),
}
}
/// Instantiate a guest module and return it's index in the store.
///
/// The guest module's code is specified in `wasm`. Environment that will be available to
/// guest module is specified in `raw_env_def` (serialized version of [`EnvironmentDefinition`]).
/// `dispatch_thunk` is used as function that handle calls from guests.
///
/// # Errors
///
/// Returns `Err` if any of the following conditions happens:
///
/// - `raw_env_def` can't be deserialized as a [`EnvironmentDefinition`].
/// - Module in `wasm` is invalid or couldn't be instantiated.
///
/// [`EnvironmentDefinition`]: ../../sandbox/struct.EnvironmentDefinition.html
pub fn instantiate(
&mut self,
dispatch_thunk: FuncRef,
wasm: &[u8],
raw_env_def: &[u8],
_state: u32,
) -> Result<u32, DummyUserError> {
let (imports, guest_to_supervisor_mapping) =
decode_environment_definition(raw_env_def, &self.memories)?;
// TODO: Run `start`.
let module = Module::from_buffer(wasm).map_err(|_| DummyUserError)?;
let instance = ModuleInstance::new(&module, &imports)
.map_err(|_| DummyUserError)?
.assert_no_start();
let instance_idx = self.instances.len();
self.instances.push(SandboxInstance {
instance,
dispatch_thunk,
guest_to_supervisor_mapping,
});
Ok(instance_idx as u32)
}
/// Create a new memory instance and return it's index.
///
/// # Errors
///
/// Returns `Err` if the memory couldn't be created.
/// Typically happens if `initial` is more than `maximum`.
pub fn new_memory(&mut self, initial: u32, maximum: u32) -> Result<u32, DummyUserError> {
let maximum = match maximum {
sandbox_primitives::MEM_UNLIMITED => None,
specified_limit => Some(Pages(specified_limit as usize)),
};
let mem = MemoryInstance::alloc(Pages(initial as usize), maximum).map_err(|_| DummyUserError)?;
self.memories.push(mem);
let mem_idx = self.memories.len() - 1;
Ok(mem_idx as u32)
}
/// Returns `ModuleRef` by `instance_idx`.
///
/// # Errors
///
/// Returns `Err` If `instance_idx` isn't a valid index of an instance.
pub fn instance(&self, instance_idx: u32) -> Result<ModuleRef, DummyUserError> {
self.instances
.get(instance_idx as usize)
.map(|i| i.instance.clone())
.ok_or_else(|| DummyUserError)
}
/// Returns reference to a memory instance by `memory_idx`.
///
/// # Errors
///
/// Returns `Err` If `memory_idx` isn't a valid index of an instance.
pub fn memory(&self, memory_idx: u32) -> Result<MemoryRef, DummyUserError> {
self.memories
.get(memory_idx as usize)
.cloned()
.ok_or_else(|| DummyUserError)
}
}
#[cfg(test)]
mod tests {
use wasm_executor::WasmExecutor;
use state_machine::{TestExternalities, CodeExecutor};
use wabt;
#[test]
fn sandbox_should_work() {
let mut ext = TestExternalities::default();
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
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();
assert_eq!(
WasmExecutor.call(&mut ext, &test_code[..], "test_sandbox", &code).unwrap(),
vec![1],
);
}
#[test]
fn sandbox_trap() {
let mut ext = TestExternalities::default();
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
let code = wabt::wat2wasm(r#"
(module
(import "env" "assert" (func $assert (param i32)))
(func (export "call")
i32.const 0
call $assert
)
)
"#).unwrap();
assert_eq!(
WasmExecutor.call(&mut ext, &test_code[..], "test_sandbox", &code).unwrap(),
vec![0],
);
}
}
@@ -18,7 +18,9 @@
use std::cmp::Ordering;
use std::collections::HashMap;
use wasmi::{Module, ModuleInstance, MemoryInstance, MemoryRef, ImportsBuilder};
use wasmi::{
Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder,
};
use wasmi::RuntimeValue::{I32, I64};
use wasmi::memory_units::{Pages, Bytes};
use state_machine::{Externalities, CodeExecutor};
@@ -26,7 +28,9 @@ use error::{Error, ErrorKind, Result};
use wasm_utils::{DummyUserError};
use primitives::{blake2_256, twox_128, twox_256};
use primitives::hexdisplay::HexDisplay;
use primitives::sandbox as sandbox_primitives;
use triehash::ordered_trie_root;
use sandbox;
struct Heap {
end: u32,
@@ -60,23 +64,45 @@ impl Heap {
}
struct FunctionExecutor<'e, E: Externalities + 'e> {
sandbox_store: sandbox::Store,
heap: Heap,
memory: MemoryRef,
table: Option<TableRef>,
ext: &'e mut E,
hash_lookup: HashMap<Vec<u8>, Vec<u8>>,
}
impl<'e, E: Externalities> FunctionExecutor<'e, E> {
fn new(m: MemoryRef, e: &'e mut E) -> Result<Self> {
fn new(m: MemoryRef, t: Option<TableRef>, e: &'e mut E) -> Result<Self> {
Ok(FunctionExecutor {
sandbox_store: sandbox::Store::new(),
heap: Heap::new(&m)?,
memory: m,
table: t,
ext: e,
hash_lookup: HashMap::new(),
})
}
}
impl<'e, E: Externalities> sandbox::SandboxCapabilities for FunctionExecutor<'e, E> {
fn store(&self) -> &sandbox::Store {
&self.sandbox_store
}
fn allocate(&mut self, len: u32) -> u32 {
self.heap.allocate(len)
}
fn deallocate(&mut self, ptr: u32) {
self.heap.deallocate(ptr)
}
fn write_memory(&mut self, ptr: u32, data: &[u8]) -> ::std::result::Result<(), DummyUserError> {
self.memory.set(ptr, data).map_err(|_| DummyUserError)
}
fn read_memory(&self, ptr: u32, len: u32) -> ::std::result::Result<Vec<u8>, DummyUserError> {
self.memory.get(ptr, len as usize).map_err(|_| DummyUserError)
}
}
trait WritePrimitive<T: Sized> {
fn write_primitive(&self, offset: u32, t: T) -> ::std::result::Result<(), DummyUserError>;
}
@@ -310,6 +336,73 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
5
})
},
ext_sandbox_instantiate(dispatch_thunk_idx: usize, wasm_ptr: *const u8, wasm_len: usize, imports_ptr: *const u8, imports_len: usize, state: usize) -> u32 => {
let wasm = this.memory.get(wasm_ptr, wasm_len as usize).map_err(|_| DummyUserError)?;
let raw_env_def = this.memory.get(imports_ptr, imports_len as usize).map_err(|_| DummyUserError)?;
let table = this.table.as_ref().ok_or_else(|| DummyUserError)?;
let dispatch_thunk = table.get(dispatch_thunk_idx)
.map_err(|_| DummyUserError)?
.ok_or_else(|| DummyUserError)?
.clone();
let instance_idx = this.sandbox_store.instantiate(dispatch_thunk, &wasm, &raw_env_def, state)?;
Ok(instance_idx as u32)
},
ext_sandbox_invoke(instance_idx: u32, export_ptr: *const u8, export_len: usize, state: usize) -> u32 => {
trace!(target: "runtime-sandbox", "invoke, instance_idx={}", instance_idx);
let export = this.memory.get(export_ptr, export_len as usize)
.map_err(|_| DummyUserError)
.and_then(|b|
String::from_utf8(b)
.map_err(|_| DummyUserError)
)?;
let instance = this.sandbox_store.instance(instance_idx)?;
let mut guest_externals = sandbox::GuestExternals::new(this, instance_idx, state);
let result = instance.invoke_export(&export, &[], &mut guest_externals);
match result {
Ok(None) => Ok(sandbox_primitives::ERR_OK),
// TODO: Return value
Ok(_) => unimplemented!(),
Err(_) => Ok(sandbox_primitives::ERR_EXECUTION),
}
},
ext_sandbox_memory_new(initial: u32, maximum: u32) -> u32 => {
let mem_idx = this.sandbox_store.new_memory(initial, maximum)?;
Ok(mem_idx)
},
ext_sandbox_memory_get(memory_idx: u32, offset: u32, buf_ptr: *mut u8, buf_len: usize) -> u32 => {
let dst_memory = this.sandbox_store.memory(memory_idx)?;
let data: Vec<u8> = match dst_memory.get(offset, buf_len as usize) {
Ok(data) => data,
Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
};
match this.memory.set(buf_ptr, &data) {
Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
_ => {},
}
Ok(sandbox_primitives::ERR_OK)
},
ext_sandbox_memory_set(memory_idx: u32, offset: u32, val_ptr: *const u8, val_len: usize) -> u32 => {
let dst_memory = this.sandbox_store.memory(memory_idx)?;
let data = match this.memory.get(offset, val_len as usize) {
Ok(data) => data,
Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
};
match dst_memory.set(val_ptr, &data) {
Err(_) => return Ok(sandbox_primitives::ERR_OUT_OF_BOUNDS),
_ => {},
}
Ok(sandbox_primitives::ERR_OK)
},
=> <'e, E: Externalities + 'e>
);
@@ -341,7 +434,8 @@ impl CodeExecutor for WasmExecutor {
.with_resolver("env", FunctionExecutor::<E>::resolver())
)?;
// extract a reference to a linear memory and initialize FunctionExecutor.
// extract a reference to a linear memory, optional reference to a table
// and then initialize FunctionExecutor.
let memory = intermediate_instance
.not_started_instance()
.export_by_name("memory")
@@ -349,7 +443,11 @@ impl CodeExecutor for WasmExecutor {
.as_memory()
.expect("in module generated by rustc export named 'memory' should be a memory; qed")
.clone();
let mut fec = FunctionExecutor::new(memory.clone(), ext)?;
let table: Option<TableRef> = intermediate_instance
.not_started_instance()
.export_by_name("table")
.and_then(|e| e.as_table().cloned());
let mut fec = FunctionExecutor::new(memory.clone(), table, ext)?;
// finish instantiation by running 'start' function (if any).
let instance = intermediate_instance.run_start(&mut fec)?;
+12
View File
@@ -30,6 +30,7 @@ name = "runtime-test"
version = "0.1.0"
dependencies = [
"substrate-runtime-io 0.1.0",
"substrate-runtime-sandbox 0.1.0",
]
[[package]]
@@ -94,6 +95,17 @@ dependencies = [
"substrate-runtime-std 0.1.0",
]
[[package]]
name = "substrate-runtime-sandbox"
version = "0.1.0"
dependencies = [
"rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-codec 0.1.0",
"substrate-primitives 0.1.0",
"substrate-runtime-io 0.1.0",
"substrate-runtime-std 0.1.0",
]
[[package]]
name = "substrate-runtime-std"
version = "0.1.0"
@@ -8,6 +8,7 @@ crate-type = ["cdylib"]
[dependencies]
substrate-runtime-io = { path = "../../runtime-io", version = "0.1", default_features = false }
substrate-runtime-sandbox = { path = "../../runtime-sandbox", version = "0.1", default_features = false }
[profile.release]
panic = "abort"
@@ -4,5 +4,8 @@ set -e
cargo +nightly build --target=wasm32-unknown-unknown --release
for i in test
do
# Add export of the default table under name 'table'.
wasm-export-table target/wasm32-unknown-unknown/release/runtime_$i.wasm target/wasm32-unknown-unknown/release/runtime_$i.table.wasm
cp target/wasm32-unknown-unknown/release/runtime_$i.table.wasm target/wasm32-unknown-unknown/release/runtime_$i.wasm
wasm-gc target/wasm32-unknown-unknown/release/runtime_$i.wasm target/wasm32-unknown-unknown/release/runtime_$i.compact.wasm
done
@@ -8,6 +8,8 @@ use alloc::vec::Vec;
#[macro_use]
extern crate substrate_runtime_io as runtime_io;
extern crate substrate_runtime_sandbox as sandbox;
use runtime_io::{
set_storage, storage, print, blake2_256,
twox_128, twox_256, ed25519_verify, enumerated_trie_root
@@ -50,5 +52,46 @@ impl_stubs!(
},
test_enumerated_trie_root NO_DECODE => |_| {
enumerated_trie_root(&[&b"zero"[..], &b"one"[..], &b"two"[..]]).to_vec()
},
test_sandbox NO_DECODE => |code: &[u8]| {
let result = execute_sandboxed(code).is_ok();
[result as u8].to_vec()
}
);
fn execute_sandboxed(code: &[u8]) -> Result<sandbox::ReturnValue, sandbox::HostError> {
struct State {
counter: u32,
}
fn env_assert(_e: &mut State, args: &[sandbox::TypedValue]) -> Result<sandbox::ReturnValue, sandbox::HostError> {
if args.len() != 1 {
return Err(sandbox::HostError);
}
let condition = args[0].as_i32().ok_or_else(|| sandbox::HostError)?;
if condition != 0 {
Ok(sandbox::ReturnValue::Unit)
} else {
Err(sandbox::HostError)
}
}
fn env_inc_counter(e: &mut State, args: &[sandbox::TypedValue]) -> Result<sandbox::ReturnValue, sandbox::HostError> {
if args.len() != 1 {
return Err(sandbox::HostError);
}
let inc_by = args[0].as_i32().ok_or_else(|| sandbox::HostError)?;
e.counter += inc_by as u32;
Ok(sandbox::ReturnValue::Value(sandbox::TypedValue::I32(e.counter as i32)))
}
let mut state = State { counter: 0 };
let mut env_builder = sandbox::EnvironmentDefinitionBuilder::new();
env_builder.add_host_func("env", "assert", env_assert);
env_builder.add_host_func("env", "inc_counter", env_inc_counter);
let mut instance = sandbox::Instance::new(code, &env_builder, &mut state)?;
let result = instance.invoke(b"call", &[], &mut state);
result.map_err(|_| sandbox::HostError)
}
@@ -15,6 +15,7 @@ uint = { git = "https://github.com/rphmeier/primitives.git", branch = "compile-f
twox-hash = { version = "1.1.0", optional = true }
byteorder = { version = "1.1", default_features = false }
blake2-rfc = { version = "0.2.18", optional = true }
wasmi = { version = "0.1", optional = true }
[dev-dependencies]
substrate-serializer = { path = "../serializer" }
@@ -23,6 +24,7 @@ pretty_assertions = "0.4"
[features]
default = ["std"]
std = [
"wasmi",
"uint/std",
"fixed-hash/std",
"substrate-codec/std",
@@ -42,6 +42,8 @@ extern crate blake2_rfc;
extern crate serde_derive;
#[cfg(feature = "std")]
extern crate core;
#[cfg(feature = "std")]
extern crate wasmi;
extern crate substrate_runtime_std as rstd;
@@ -83,6 +85,7 @@ pub mod hexdisplay;
pub mod bft;
pub mod block;
pub mod hash;
pub mod sandbox;
pub mod storage;
pub mod uint;
@@ -0,0 +1,357 @@
// Copyright 2018 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/>.
//! Definition of a sandbox environment.
use codec::{Slicable, Input};
use rstd::vec::Vec;
/// Error error that can be returned from host function.
#[cfg_attr(feature = "std", derive(Debug))]
pub struct HostError;
impl Slicable for HostError {
fn decode<I: Input>(_: &mut I) -> Option<Self> {
Some(HostError)
}
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
f(&[])
}
fn encode(&self) -> Vec<u8> {
Vec::new()
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
#[repr(i8)]
enum ValueType {
I32 = 1,
I64 = 2,
F32 = 3,
F64 = 4,
}
/// Representation of a typed wasm value.
#[derive(Clone, Copy, PartialEq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum TypedValue {
/// Value of 32-bit signed or unsigned integer.
I32(i32),
/// Value of 64-bit signed or unsigned integer.
I64(i64),
/// Value of 32-bit IEEE 754-2008 floating point number represented as a bit pattern.
F32(i32),
/// Value of 64-bit IEEE 754-2008 floating point number represented as a bit pattern.
F64(i64),
}
impl TypedValue {
/// Returns `Some` if this value of type `I32`.
pub fn as_i32(&self) -> Option<i32> {
match *self {
TypedValue::I32(v) => Some(v),
_ => None,
}
}
}
#[cfg(feature = "std")]
impl From<::wasmi::RuntimeValue> for TypedValue {
fn from(val: ::wasmi::RuntimeValue) -> TypedValue {
use ::wasmi::RuntimeValue;
match val {
RuntimeValue::I32(v) => TypedValue::I32(v),
RuntimeValue::I64(v) => TypedValue::I64(v),
RuntimeValue::F32(v) => TypedValue::F32(v.to_bits() as i32),
RuntimeValue::F64(v) => TypedValue::F64(v.to_bits() as i64),
}
}
}
#[cfg(feature = "std")]
impl From<TypedValue> for ::wasmi::RuntimeValue {
fn from(val: TypedValue) -> ::wasmi::RuntimeValue {
use ::wasmi::RuntimeValue;
match val {
TypedValue::I32(v) => RuntimeValue::I32(v),
TypedValue::I64(v) => RuntimeValue::I64(v),
TypedValue::F32(v_bits) => RuntimeValue::F32(f32::from_bits(v_bits as u32)),
TypedValue::F64(v_bits) => RuntimeValue::F64(f64::from_bits(v_bits as u64)),
}
}
}
impl Slicable for TypedValue {
fn encode(&self) -> Vec<u8> {
let mut v = Vec::new();
match *self {
TypedValue::I32(i) => {
v.push(ValueType::I32 as u8);
i.using_encoded(|s| v.extend(s));
}
TypedValue::I64(i) => {
v.push(ValueType::I64 as u8);
i.using_encoded(|s| v.extend(s));
}
TypedValue::F32(f_bits) => {
v.push(ValueType::F32 as u8);
f_bits.using_encoded(|s| v.extend(s));
}
TypedValue::F64(f_bits) => {
v.push(ValueType::F64 as u8);
f_bits.using_encoded(|s| v.extend(s));
}
}
v
}
fn decode<I: Input>(value: &mut I) -> Option<Self> {
let typed_value = match i8::decode(value) {
Some(x) if x == ValueType::I32 as i8 => TypedValue::I32(i32::decode(value)?),
Some(x) if x == ValueType::I64 as i8 => TypedValue::I64(i64::decode(value)?),
Some(x) if x == ValueType::F32 as i8 => TypedValue::F32(i32::decode(value)?),
Some(x) if x == ValueType::F64 as i8 => TypedValue::F64(i64::decode(value)?),
_ => return None,
};
Some(typed_value)
}
}
/// Typed value that can be returned from a function.
///
/// Basically a `TypedValue` plus `Unit`, for functions which return nothing.
#[derive(Clone, Copy, PartialEq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum ReturnValue {
/// For returning some concrete value.
Value(TypedValue),
/// For returning nothing.
Unit,
}
impl From<TypedValue> for ReturnValue {
fn from(v: TypedValue) -> ReturnValue {
ReturnValue::Value(v)
}
}
impl Slicable for ReturnValue {
fn encode(&self) -> Vec<u8> {
let mut v = Vec::new();
match *self {
ReturnValue::Unit => {
v.push(0);
}
ReturnValue::Value(ref val) => {
v.push(1);
val.using_encoded(|s| v.extend(s));
}
}
v
}
fn decode<I: Input>(value: &mut I) -> Option<Self> {
match i8::decode(value) {
Some(0) => Some(ReturnValue::Unit),
Some(1) => Some(ReturnValue::Value(TypedValue::decode(value)?)),
_ => return None,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
#[repr(i8)]
enum ExternEntityKind {
Function = 1,
Memory = 2,
}
/// Describes an entity to define or import into the environment.
#[derive(Clone, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum ExternEntity {
/// Function that is specified by an index in a default table of
/// a module that creates the sandbox.
Function(u32),
/// Linear memory that is specified by some identifier returned by sandbox
/// module upon creation new sandboxed memory.
Memory(u32),
}
impl Slicable for ExternEntity {
fn encode(&self) -> Vec<u8> {
let mut v = Vec::new();
match *self {
ExternEntity::Function(ref index) => {
v.push(ExternEntityKind::Function as u8);
index.using_encoded(|s| v.extend(s));
}
ExternEntity::Memory(ref mem_id) => {
v.push(ExternEntityKind::Memory as u8);
mem_id.using_encoded(|s| v.extend(s));
}
}
v
}
fn decode<I: Input>(value: &mut I) -> Option<Self> {
match i8::decode(value) {
Some(x) if x == ExternEntityKind::Function as i8 => {
let idx = u32::decode(value)?;
Some(ExternEntity::Function(idx))
}
Some(x) if x == ExternEntityKind::Memory as i8 => {
let mem_id = u32::decode(value)?;
Some(ExternEntity::Memory(mem_id))
}
_ => None,
}
}
}
/// An entry in a environment definition table.
///
/// Each entry has a two-level name and description of an entity
/// being defined.
#[derive(Clone, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct Entry {
/// Module name of which corresponding entity being defined.
pub module_name: Vec<u8>,
/// Field name in which corresponding entity being defined.
pub field_name: Vec<u8>,
/// External entity being defined.
pub entity: ExternEntity,
}
impl Slicable for Entry {
fn encode(&self) -> Vec<u8> {
let mut v = Vec::new();
self.module_name.using_encoded(|s| v.extend(s));
self.field_name.using_encoded(|s| v.extend(s));
self.entity.using_encoded(|s| v.extend(s));
v
}
fn decode<I: Input>(value: &mut I) -> Option<Self> {
let module_name = Vec::decode(value)?;
let field_name = Vec::decode(value)?;
let entity = ExternEntity::decode(value)?;
Some(Entry {
module_name,
field_name,
entity,
})
}
}
/// Definition of runtime that could be used by sandboxed code.
#[derive(Clone, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct EnvironmentDefinition {
/// Vector of all entries in the environment defintion.
pub entries: Vec<Entry>,
}
impl Slicable for EnvironmentDefinition {
fn encode(&self) -> Vec<u8> {
self.entries.encode()
}
fn decode<I: Input>(value: &mut I) -> Option<Self> {
let entries = Vec::decode(value)?;
Some(EnvironmentDefinition {
entries,
})
}
}
/// Constant for specifying no limit when creating a sandboxed
/// memory instance. For FFI purposes.
pub const MEM_UNLIMITED: u32 = -1i32 as u32;
/// No error happened.
///
/// For FFI purposes.
pub const ERR_OK: u32 = 0;
/// Validation or instantiation error occured when creating new
/// sandboxed module instance.
///
/// For FFI purposes.
pub const ERR_MODULE: u32 = -1i32 as u32;
/// Out-of-bounds access attempted with memory or table.
///
/// For FFI purposes.
pub const ERR_OUT_OF_BOUNDS: u32 = -2i32 as u32;
/// Execution error occured (typically trap).
///
/// For FFI purposes.
pub const ERR_EXECUTION: u32 = -3i32 as u32;
#[cfg(test)]
mod tests {
use super::*;
use std::fmt;
fn roundtrip<S: Slicable + PartialEq + fmt::Debug>(s: S) {
let encoded = s.encode();
assert_eq!(S::decode(&mut &encoded[..]).unwrap(), s);
}
#[test]
fn env_def_roundtrip() {
roundtrip(EnvironmentDefinition {
entries: vec![],
});
roundtrip(EnvironmentDefinition {
entries: vec![
Entry {
module_name: b"kernel"[..].into(),
field_name: b"memory"[..].into(),
entity: ExternEntity::Memory(1337),
},
],
});
roundtrip(EnvironmentDefinition {
entries: vec![
Entry {
module_name: b"env"[..].into(),
field_name: b"abort"[..].into(),
entity: ExternEntity::Function(228),
},
],
});
}
}
+27
View File
@@ -0,0 +1,27 @@
[package]
name = "substrate-runtime-sandbox"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"
[build-dependencies]
rustc_version = "0.2"
[dependencies]
wasmi = { version = "0.1", optional = true }
substrate-primitives = { path = "../primitives", default_features = false }
substrate-runtime-std = { path = "../runtime-std", default_features = false }
substrate-runtime-io = { path = "../runtime-io", default_features = false }
substrate-codec = { path = "../codec", default_features = false }
[features]
default = ["std"]
std = [
"wasmi",
"substrate-primitives/std",
"substrate-runtime-std/std",
"substrate-codec/std",
"substrate-runtime-io/std",
]
nightly = []
strict = []
+14
View File
@@ -0,0 +1,14 @@
//! Set a nightly feature
extern crate rustc_version;
use rustc_version::{version, version_meta, Channel};
fn main() {
// Assert we haven't travelled back in time
assert!(version().unwrap().major >= 1);
// Set cfg flags depending on release channel
if let Channel::Nightly = version_meta().unwrap().channel {
println!("cargo:rustc-cfg=feature=\"nightly\"");
}
}
+203
View File
@@ -0,0 +1,203 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! This crate provides means of instantiation and execution of wasm modules.
//!
//! It works even when the user of this library is itself executes
//! inside the wasm VM. In this case same VM is used for execution
//! of both the sandbox owner and the sandboxed module, without compromising security
//! and without performance penalty of full wasm emulation inside wasm.
//!
//! This is achieved by using bindings to wasm VM which are published by the host API.
//! This API is thin and consists of only handful functions. It contains functions for instantiating
//! modules and executing them and for example doesn't contain functions for inspecting the module
//! structure. The user of this library is supposed to read wasm module by it's own means.
//!
//! When this crate is used in `std` environment all these functions are implemented by directly
//! calling wasm VM.
//!
//! Typical use-case for this library might be used for implementing smart-contract runtimes
//! which uses wasm for contract code.
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(feature = "std"), feature(lang_items))]
#![cfg_attr(not(feature = "std"), feature(core_intrinsics))]
#![cfg_attr(not(feature = "std"), feature(alloc))]
extern crate substrate_codec as codec;
extern crate substrate_runtime_io as runtime_io;
extern crate substrate_runtime_std as rstd;
extern crate substrate_primitives as primitives;
use rstd::prelude::*;
pub use primitives::sandbox::{TypedValue, ReturnValue, HostError};
mod imp {
#[cfg(feature = "std")]
include!("../with_std.rs");
#[cfg(not(feature = "std"))]
include!("../without_std.rs");
}
/// Error that can occur while using this crate.
#[cfg_attr(feature = "std", derive(Debug))]
pub enum Error {
/// Module is not valid, couldn't be instantiated or it's `start` function trapped
/// when executed.
Module,
/// Access to a memory or table was made with an address or an index which is out of bounds.
///
/// Note that if wasm module makes an out-of-bounds access then trap will occur.
OutOfBounds,
/// Failed to invoke an exported function for some reason.
Execution,
}
impl From<Error> for HostError {
fn from(_e: Error) -> HostError {
HostError
}
}
/// Function pointer for specifying functions by the
/// supervisor in [`EnvironmentDefinitionBuilder`].
///
/// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html
pub type HostFuncType<T> = fn(&mut T, &[TypedValue]) -> Result<ReturnValue, HostError>;
/// Reference to a sandboxed linear memory, that
/// will be used by the guest module.
///
/// The memory can't be directly accessed by supervisor, but only
/// through designated functions [`get`] and [`set`].
///
/// [`get`]: #method.get
/// [`set`]: #method.set
#[derive(Clone)]
pub struct Memory {
inner: imp::Memory,
}
impl Memory {
/// Construct a new linear memory instance.
///
/// The memory allocated with initial number of pages specified by `initial`.
/// Minimal possible value for `initial` is 0 and maximum possible is `65536`.
/// (Since maximum addressible memory is 2<sup>32</sup> = 4GiB = 65536 * 64KiB).
///
/// It is possible to limit maximum number of pages this memory instance can have by specifying
/// `maximum`. If not specified, this memory instance would be able to allocate up to 4GiB.
///
/// Allocated memory is always zeroed.
pub fn new(initial: u32, maximum: Option<u32>) -> Result<Memory, Error> {
Ok(Memory {
inner: imp::Memory::new(initial, maximum)?,
})
}
/// Read a memory area at the address `ptr` with the size of the provided slice `buf`.
///
/// Returns `Err` if the range is out-of-bounds.
pub fn get(&self, ptr: u32, buf: &mut [u8]) -> Result<(), Error> {
self.inner.get(ptr, buf)
}
/// Write a memory area at the address `ptr` with contents of the provided slice `buf`.
///
/// Returns `Err` if the range is out-of-bounds.
pub fn set(&self, ptr: u32, value: &[u8]) -> Result<(), Error> {
self.inner.set(ptr, value)
}
}
/// Struct that can be used for defining an environment for a sandboxed module.
///
/// The sandboxed module can access only the entities which were defined and passed
/// to the module at the instantiation time.
pub struct EnvironmentDefinitionBuilder<T> {
inner: imp::EnvironmentDefinitionBuilder<T>,
}
impl<T> EnvironmentDefinitionBuilder<T> {
/// Construct a new `EnvironmentDefinitionBuilder`.
pub fn new() -> EnvironmentDefinitionBuilder<T> {
EnvironmentDefinitionBuilder {
inner: imp::EnvironmentDefinitionBuilder::new(),
}
}
/// Register a host function in this environment defintion.
pub fn add_host_func<N1, N2>(&mut self, module: N1, field: N2, f: HostFuncType<T>)
where
N1: Into<Vec<u8>>,
N2: Into<Vec<u8>>,
{
self.inner.add_host_func(module, field, f);
}
/// Register a memory in this environment definition.
pub fn add_memory<N1, N2>(&mut self, module: N1, field: N2, mem: Memory)
where
N1: Into<Vec<u8>>,
N2: Into<Vec<u8>>,
{
self.inner.add_memory(module, field, mem.inner);
}
}
/// Sandboxed instance of a wasm module.
///
/// This instance can be used for invoking exported functions.
pub struct Instance<T> {
inner: imp::Instance<T>,
}
impl<T> Instance<T> {
/// Instantiate a module with the given [`EnvironmentDefinitionBuilder`].
///
/// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html
pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder<T>, state: &mut T) -> Result<Instance<T>, Error> {
Ok(Instance {
inner: imp::Instance::new(code, &env_def_builder.inner, state)?,
})
}
/// Invoke an exported function with the given name.
///
/// # Errors
///
/// Returns `Err(Error::Execution)` if:
///
/// - An export function name isn't a proper utf8 byte sequence,
/// - This module doesn't have an exported function with the given name,
/// - If types of the arguments passed to the function doesn't match function signature
/// then trap occurs (as if the exported function was called via call_indirect),
/// - Trap occured at the execution time.
pub fn invoke(
&mut self,
name: &[u8],
args: &[TypedValue],
state: &mut T,
) -> Result<ReturnValue, Error> {
self.inner.invoke(name, args, state)
}
}
+309
View File
@@ -0,0 +1,309 @@
// Copyright 2018 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/>.
extern crate wasmi;
use rstd::collections::btree_map::BTreeMap;
use rstd::fmt;
use self::wasmi::{Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalRef, ImportResolver,
MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleInstance, ModuleRef,
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableRef, Trap, TrapKind};
use self::wasmi::memory_units::Pages;
use super::{Error, TypedValue, ReturnValue, HostFuncType, HostError};
#[derive(Clone)]
pub struct Memory {
memref: MemoryRef,
}
impl Memory {
pub fn new(initial: u32, maximum: Option<u32>) -> Result<Memory, Error> {
Ok(Memory {
memref: MemoryInstance::alloc(
Pages(initial as usize),
maximum.map(|m| Pages(m as usize)),
).map_err(|_| Error::Module)?,
})
}
pub fn get(&self, ptr: u32, buf: &mut [u8]) -> Result<(), Error> {
self.memref.get_into(ptr, buf).map_err(|_| Error::OutOfBounds)?;
Ok(())
}
pub fn set(&self, ptr: u32, value: &[u8]) -> Result<(), Error> {
self.memref.set(ptr, value).map_err(|_| Error::OutOfBounds)?;
Ok(())
}
}
struct HostFuncIndex(usize);
struct DefinedHostFunctions<T> {
funcs: Vec<HostFuncType<T>>,
}
impl<T> Clone for DefinedHostFunctions<T> {
fn clone(&self) -> DefinedHostFunctions<T> {
DefinedHostFunctions {
funcs: self.funcs.clone(),
}
}
}
impl<T> DefinedHostFunctions<T> {
fn new() -> DefinedHostFunctions<T> {
DefinedHostFunctions {
funcs: Vec::new(),
}
}
fn define(&mut self, f: HostFuncType<T>) -> HostFuncIndex {
let idx = self.funcs.len();
self.funcs.push(f);
HostFuncIndex(idx)
}
}
#[derive(Debug)]
struct DummyHostError;
impl fmt::Display for DummyHostError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DummyHostError")
}
}
impl self::wasmi::HostError for DummyHostError {
}
fn from_runtime_value(v: RuntimeValue) -> TypedValue {
match v {
RuntimeValue::I32(v) => TypedValue::I32(v),
RuntimeValue::I64(v) => TypedValue::I64(v),
RuntimeValue::F32(v) => TypedValue::F32(v.to_bits() as i32),
RuntimeValue::F64(v) => TypedValue::F64(v.to_bits() as i64),
}
}
fn to_runtime_value(v: TypedValue) -> RuntimeValue {
match v {
TypedValue::I32(v) => RuntimeValue::I32(v as i32),
TypedValue::I64(v) => RuntimeValue::I64(v as i64),
TypedValue::F32(v_bits) => RuntimeValue::F32(f32::from_bits(v_bits as u32)),
TypedValue::F64(v_bits) => RuntimeValue::F64(f64::from_bits(v_bits as u64)),
}
}
struct GuestExternals<'a, T: 'a> {
state: &'a mut T,
defined_host_functions: &'a DefinedHostFunctions<T>,
}
impl<'a, T> Externals for GuestExternals<'a, T> {
fn invoke_index(
&mut self,
index: usize,
args: RuntimeArgs,
) -> Result<Option<RuntimeValue>, Trap> {
let args = args.as_ref()
.iter()
.cloned()
.map(from_runtime_value)
.collect::<Vec<_>>();
let result = (self.defined_host_functions.funcs[index])(self.state, &args);
match result {
Ok(value) => Ok(match value {
ReturnValue::Value(v) => Some(to_runtime_value(v)),
ReturnValue::Unit => None,
}),
Err(HostError) => Err(TrapKind::Host(Box::new(DummyHostError)).into()),
}
}
}
enum ExternVal {
HostFunc(HostFuncIndex),
Memory(Memory),
}
pub struct EnvironmentDefinitionBuilder<T> {
map: BTreeMap<(Vec<u8>, Vec<u8>), ExternVal>,
defined_host_functions: DefinedHostFunctions<T>,
}
impl<T> EnvironmentDefinitionBuilder<T> {
pub fn new() -> EnvironmentDefinitionBuilder<T> {
EnvironmentDefinitionBuilder {
map: BTreeMap::new(),
defined_host_functions: DefinedHostFunctions::new(),
}
}
pub fn add_host_func<N1, N2>(&mut self, module: N1, field: N2, f: HostFuncType<T>)
where
N1: Into<Vec<u8>>,
N2: Into<Vec<u8>>,
{
let idx = self.defined_host_functions.define(f);
self.map
.insert((module.into(), field.into()), ExternVal::HostFunc(idx));
}
pub fn add_memory<N1, N2>(&mut self, module: N1, field: N2, mem: Memory)
where
N1: Into<Vec<u8>>,
N2: Into<Vec<u8>>,
{
self.map
.insert((module.into(), field.into()), ExternVal::Memory(mem));
}
}
impl<T> ImportResolver for EnvironmentDefinitionBuilder<T> {
fn resolve_func(
&self,
module_name: &str,
field_name: &str,
signature: &Signature,
) -> Result<FuncRef, wasmi::Error> {
let key = (
module_name.as_bytes().to_owned(),
field_name.as_bytes().to_owned(),
);
let externval = self.map.get(&key).ok_or_else(|| {
wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name))
})?;
let host_func_idx = match *externval {
ExternVal::HostFunc(ref idx) => idx,
_ => {
return Err(wasmi::Error::Instantiation(format!(
"Export {}:{} is not a host func",
module_name, field_name
)))
}
};
Ok(FuncInstance::alloc_host(signature.clone(), host_func_idx.0))
}
fn resolve_global(
&self,
_module_name: &str,
_field_name: &str,
_global_type: &GlobalDescriptor,
) -> Result<GlobalRef, wasmi::Error> {
// TODO: Implement sandboxed globals.
unimplemented!()
}
fn resolve_memory(
&self,
module_name: &str,
field_name: &str,
_memory_type: &MemoryDescriptor,
) -> Result<MemoryRef, wasmi::Error> {
let key = (
module_name.as_bytes().to_owned(),
field_name.as_bytes().to_owned(),
);
let externval = self.map.get(&key).ok_or_else(|| {
wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name))
})?;
let memory = match *externval {
ExternVal::Memory(ref m) => m,
_ => {
return Err(wasmi::Error::Instantiation(format!(
"Export {}:{} is not a memory",
module_name, field_name
)))
}
};
Ok(memory.memref.clone())
}
fn resolve_table(
&self,
_module_name: &str,
_field_name: &str,
_table_type: &TableDescriptor,
) -> Result<TableRef, wasmi::Error> {
// TODO: Implement sandboxed tables.
unimplemented!()
}
}
pub struct Instance<T> {
instance: ModuleRef,
defined_host_functions: DefinedHostFunctions<T>,
_marker: ::std::marker::PhantomData<T>,
}
impl<T> Instance<T> {
pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder<T>, state: &mut T) -> Result<Instance<T>, Error> {
let module = Module::from_buffer(code).map_err(|_| Error::Module)?;
let not_started_instance = ModuleInstance::new(&module, env_def_builder)
.map_err(|_| Error::Module)?;
let defined_host_functions = env_def_builder.defined_host_functions.clone();
let instance = {
let mut externals = GuestExternals {
state,
defined_host_functions: &defined_host_functions,
};
let instance = not_started_instance.run_start(&mut externals).map_err(|_| Error::Module)?;
instance
};
Ok(Instance {
instance,
defined_host_functions,
_marker: ::std::marker::PhantomData::<T>,
})
}
pub fn invoke(
&mut self,
name: &[u8],
args: &[TypedValue],
state: &mut T,
) -> Result<ReturnValue, Error> {
if args.len() > 0 {
// TODO: Convert args into `RuntimeValue` and use it.
unimplemented!();
}
let name = ::std::str::from_utf8(name).map_err(|_| Error::Execution)?;
let mut externals = GuestExternals {
state,
defined_host_functions: &self.defined_host_functions,
};
let result = self.instance
.invoke_export(&name, &[], &mut externals);
match result {
Ok(None) => Ok(ReturnValue::Unit),
Ok(_val) => {
// TODO: Convert result value into `TypedValue` and return it.
unimplemented!();
}
Err(_err) => Err(Error::Execution),
}
}
}
+267
View File
@@ -0,0 +1,267 @@
// Copyright 2018 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 rstd::prelude::*;
use rstd::{slice, marker, mem};
use codec::Slicable;
use primitives::sandbox as sandbox_primitives;
use super::{Error, TypedValue, ReturnValue, HostFuncType};
mod ffi {
use rstd::mem;
use super::HostFuncType;
/// Index into the default table that points to a `HostFuncType`.
pub type HostFuncIndex = usize;
/// Coerce `HostFuncIndex` to a callable host function pointer.
///
/// # Safety
///
/// This function should be only called with a `HostFuncIndex` that was previously registered
/// in the environment defintion. Typically this should only
/// be called with an argument received in `dispatch_thunk`.
pub unsafe fn coerce_host_index_to_func<T>(idx: HostFuncIndex) -> HostFuncType<T> {
// We need to ensure that sizes of a callable function pointer and host function index is
// indeed equal.
// We can't use `static_assertions` create because it makes compiler panic, fallback to runtime assert.
// const_assert!(mem::size_of::<HostFuncIndex>() == mem::size_of::<HostFuncType<T>>(),);
assert!(mem::size_of::<HostFuncIndex>() == mem::size_of::<HostFuncType<T>>());
mem::transmute::<HostFuncIndex, HostFuncType<T>>(idx)
}
extern "C" {
pub fn ext_sandbox_instantiate(
dispatch_thunk: extern "C" fn(
serialized_args_ptr: *const u8,
serialized_args_len: usize,
state: usize,
f: HostFuncIndex,
) -> u64,
wasm_ptr: *const u8,
wasm_len: usize,
imports_ptr: *const u8,
imports_len: usize,
state: usize,
) -> u32;
pub fn ext_sandbox_invoke(
instance_idx: u32,
export_ptr: *const u8,
export_len: usize,
state: usize,
) -> u32;
pub fn ext_sandbox_memory_new(initial: u32, maximum: u32) -> u32;
pub fn ext_sandbox_memory_get(
memory_idx: u32,
offset: u32,
buf_ptr: *mut u8,
buf_len: usize,
) -> u32;
pub fn ext_sandbox_memory_set(
memory_idx: u32,
offset: u32,
val_ptr: *const u8,
val_len: usize,
) -> u32;
// TODO: ext_instance_teardown
// TODO: ext_memory_teardown
}
}
#[derive(Clone)]
pub struct Memory {
memory_idx: u32,
}
impl Memory {
pub fn new(initial: u32, maximum: Option<u32>) -> Result<Memory, Error> {
let result = unsafe {
let maximum = if let Some(maximum) = maximum {
maximum
} else {
sandbox_primitives::MEM_UNLIMITED
};
ffi::ext_sandbox_memory_new(initial, maximum)
};
match result {
sandbox_primitives::ERR_MODULE => Err(Error::Module),
memory_idx => Ok(Memory { memory_idx }),
}
}
pub fn get(&self, offset: u32, buf: &mut [u8]) -> Result<(), Error> {
let result = unsafe { ffi::ext_sandbox_memory_get(self.memory_idx, offset, buf.as_mut_ptr(), buf.len()) };
match result {
sandbox_primitives::ERR_OK => Ok(()),
sandbox_primitives::ERR_OUT_OF_BOUNDS => Err(Error::OutOfBounds),
_ => unreachable!(),
}
}
pub fn set(&self, offset: u32, val: &[u8]) -> Result<(), Error> {
let result = unsafe { ffi::ext_sandbox_memory_set(self.memory_idx, offset, val.as_ptr(), val.len()) };
match result {
sandbox_primitives::ERR_OK => Ok(()),
sandbox_primitives::ERR_OUT_OF_BOUNDS => Err(Error::OutOfBounds),
_ => unreachable!(),
}
}
}
pub struct EnvironmentDefinitionBuilder<T> {
env_def: sandbox_primitives::EnvironmentDefinition,
_marker: marker::PhantomData<T>,
}
impl<T> EnvironmentDefinitionBuilder<T> {
pub fn new() -> EnvironmentDefinitionBuilder<T> {
EnvironmentDefinitionBuilder {
env_def: sandbox_primitives::EnvironmentDefinition {
entries: Vec::new(),
},
_marker: marker::PhantomData::<T>,
}
}
fn add_entry<N1, N2>(
&mut self,
module: N1,
field: N2,
extern_entity: sandbox_primitives::ExternEntity,
) where
N1: Into<Vec<u8>>,
N2: Into<Vec<u8>>,
{
let entry = sandbox_primitives::Entry {
module_name: module.into(),
field_name: field.into(),
entity: extern_entity,
};
self.env_def.entries.push(entry);
}
pub fn add_host_func<N1, N2>(&mut self, module: N1, field: N2, f: HostFuncType<T>)
where
N1: Into<Vec<u8>>,
N2: Into<Vec<u8>>,
{
let f = sandbox_primitives::ExternEntity::Function(f as u32);
self.add_entry(module, field, f);
}
pub fn add_memory<N1, N2>(&mut self, module: N1, field: N2, mem: Memory)
where
N1: Into<Vec<u8>>,
N2: Into<Vec<u8>>,
{
let mem = sandbox_primitives::ExternEntity::Memory(mem.memory_idx as u32);
self.add_entry(module, field, mem);
}
}
pub struct Instance<T> {
instance_idx: u32,
_marker: marker::PhantomData<T>,
}
/// The primary responsibility of this thunk is to deserialize arguments and
/// call the original function, specified by the index.
extern "C" fn dispatch_thunk<T>(
serialized_args_ptr: *const u8,
serialized_args_len: usize,
state: usize,
f: ffi::HostFuncIndex,
) -> u64 {
let serialized_args = unsafe {
if serialized_args_len == 0 {
&[]
} else {
slice::from_raw_parts(serialized_args_ptr, serialized_args_len)
}
};
let args = Vec::<TypedValue>::decode(&mut &serialized_args[..]).expect(
"serialized args should be provided by the runtime;
correctly serialized data should be deserializable;
qed",
);
unsafe {
// This should be safe since `coerce_host_index_to_func` is called with an argument
// received in an `dispatch_thunk` implementation, so `f` should point
// on a valid host function.
let f = ffi::coerce_host_index_to_func(f);
// This should be safe since mutable reference to T is passed upon the invocation.
let state = &mut *(state as *mut T);
// Pass control flow to the designated function.
let result = f(state, &args).encode();
// Leak the result vector and return the pointer to return data.
let result_ptr = result.as_ptr() as u64;
let result_len = result.len() as u64;
mem::forget(result);
(result_ptr << 32) | result_len
}
}
impl<T> Instance<T> {
pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder<T>, state: &mut T) -> Result<Instance<T>, Error> {
let serialized_env_def: Vec<u8> = env_def_builder.env_def.encode();
let result = unsafe {
// It's very important to instantiate thunk with the right type.
let dispatch_thunk = dispatch_thunk::<T>;
ffi::ext_sandbox_instantiate(
dispatch_thunk,
code.as_ptr(),
code.len(),
serialized_env_def.as_ptr(),
serialized_env_def.len(),
state as *const T as usize,
)
};
let instance_idx = match result {
sandbox_primitives::ERR_MODULE => return Err(Error::Module),
instance_idx => instance_idx,
};
Ok(Instance {
instance_idx,
_marker: marker::PhantomData::<T>,
})
}
pub fn invoke(
&mut self,
name: &[u8],
_args: &[TypedValue],
state: &mut T,
) -> Result<ReturnValue, Error> {
// TODO: Serialize arguments and pass them thru.
let result =
unsafe { ffi::ext_sandbox_invoke(self.instance_idx, name.as_ptr(), name.len(), state as *const T as usize) };
match result {
sandbox_primitives::ERR_OK => {
// TODO: Fetch the result of the execution.
Ok(ReturnValue::Unit)
}
sandbox_primitives::ERR_EXECUTION => Err(Error::Execution),
_ => unreachable!(),
}
}
}
@@ -5,7 +5,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
hex-literal = "0.1.0"
integer-sqrt = "0.1.0"
integer-sqrt = { git = "https://github.com/paritytech/integer-sqrt-rs.git", branch = "master" }
serde = { version = "1.0", default_features = false }
safe-mix = { path = "../../../safe-mix", default_features = false}
substrate-keyring = { path = "../../keyring", optional = true }
@@ -5,7 +5,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
num-traits = { version = "0.2", default_features = false }
integer-sqrt = "0.1.0"
integer-sqrt = { git = "https://github.com/paritytech/integer-sqrt-rs.git", branch = "master" }
serde = { version = "1.0", optional = true }
serde_derive = { version = "1.0", optional = true }
substrate-codec = { path = "../../codec", default_features = false }
@@ -12,12 +12,16 @@ substrate-codec = { path = "../../codec", default_features = false }
substrate-primitives = { path = "../../primitives", default_features = false }
substrate-runtime-std = { path = "../../runtime-std", default_features = false }
substrate-runtime-io = { path = "../../runtime-io", default_features = false }
substrate-runtime-sandbox = { path = "../../runtime-sandbox", default_features = false }
substrate-runtime-support = { path = "../../runtime-support", default_features = false }
substrate-runtime-primitives = { path = "../primitives", default_features = false }
substrate-runtime-consensus = { path = "../consensus", default_features = false }
substrate-runtime-system = { path = "../system", default_features = false }
substrate-runtime-session = { path = "../session", default_features = false }
[dev-dependencies]
wabt = "0.1.7"
[features]
default = ["std"]
std = [
@@ -28,6 +32,7 @@ std = [
"substrate-primitives/std",
"substrate-runtime-std/std",
"substrate-runtime-io/std",
"substrate-runtime-sandbox/std",
"substrate-runtime-support/std",
"substrate-runtime-primitives/std",
"substrate-runtime-session/std",
@@ -0,0 +1,181 @@
// Copyright 2018 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/>.
//! Smart-contract execution module.
use codec::Slicable;
use primitives::traits::As;
use rstd::prelude::*;
use sandbox;
use {AccountDb, Module, OverlayAccountDb, Trait};
struct ExecutionExt<'a, 'b: 'a, T: Trait + 'b> {
account_db: &'a mut OverlayAccountDb<'b, T>,
account: T::AccountId,
memory: sandbox::Memory,
}
impl<'a, 'b: 'a, T: Trait> ExecutionExt<'a, 'b, T> {
fn account(&self) -> &T::AccountId {
&self.account
}
fn account_db(&self) -> &OverlayAccountDb<T> {
self.account_db
}
fn account_db_mut(&mut self) -> &mut OverlayAccountDb<'b, T> {
self.account_db
}
fn memory(&self) -> &sandbox::Memory {
&self.memory
}
}
pub(crate) fn execute<'a, 'b: 'a, T: Trait>(
code: &[u8],
account: &T::AccountId,
account_db: &'a mut OverlayAccountDb<'b, T>,
) -> bool {
// ext_put_storage(location_ptr: u32, value_non_null: u32, value_ptr: u32);
//
// Change the value at the given location in storage or remove it.
//
// - location_ptr: pointer into the linear
// memory where the location of the requested value is placed.
// - value_non_null: if set to 0, then the entry
// at the given location will be removed.
// - value_ptr: pointer into the linear memory
// where the value to set is placed. If `value_non_null` is set to 0, then this parameter is ignored.
fn ext_set_storage<T: Trait>(e: &mut ExecutionExt<T>, args: &[sandbox::TypedValue]) -> Result<sandbox::ReturnValue, sandbox::HostError> {
let location_ptr = args[0].as_i32().unwrap() as u32;
let value_non_null = args[1].as_i32().unwrap() as u32;
let value_ptr = args[2].as_i32().unwrap() as u32;
let mut location = [0; 32];
e.memory().get(location_ptr, &mut location)?;
let account = e.account().clone();
if value_non_null != 0 {
let mut value = [0; 32];
e.memory().get(value_ptr, &mut value)?;
e.account_db_mut()
.set_storage(&account, location.to_vec(), Some(value.to_vec()));
} else {
e.account_db_mut()
.set_storage(&account, location.to_vec(), None);
}
Ok(sandbox::ReturnValue::Unit)
}
// ext_get_storage(location_ptr: u32, dest_ptr: u32);
//
// Retrieve the value at the given location from the strorage.
// If there is no entry at the given location then all-zero-value
// will be returned.
//
// - location_ptr: pointer into the linear
// memory where the location of the requested value is placed.
// - dest_ptr: pointer where contents of the specified storage location
// should be placed.
fn ext_get_storage<T: Trait>(e: &mut ExecutionExt<T>, args: &[sandbox::TypedValue]) -> Result<sandbox::ReturnValue, sandbox::HostError> {
let location_ptr = args[0].as_i32().unwrap() as u32;
let dest_ptr = args[1].as_i32().unwrap() as u32;
let mut location = [0; 32];
e.memory().get(location_ptr, &mut location)?;
let account = e.account().clone();
if let Some(value) = e.account_db_mut().get_storage(&account, &location) {
e.memory().set(dest_ptr, &value)?;
} else {
e.memory().set(dest_ptr, &[0u8; 32])?;
}
Ok(sandbox::ReturnValue::Unit)
}
// ext_transfer(transfer_to: u32, transfer_to_len: u32, value: u32)
fn ext_transfer<T: Trait>(e: &mut ExecutionExt<T>, args: &[sandbox::TypedValue]) -> Result<sandbox::ReturnValue, sandbox::HostError> {
let transfer_to_ptr = args[0].as_i32().unwrap() as u32;
let transfer_to_len = args[1].as_i32().unwrap() as u32;
let value = args[2].as_i32().unwrap() as u64;
// TODO: slicable
let mut transfer_to = Vec::new();
transfer_to.resize(transfer_to_len as usize, 0);
e.memory().get(transfer_to_ptr, &mut transfer_to)?;
let value = T::Balance::sa(value as usize);
let transfer_to = T::AccountId::decode(&mut &transfer_to[..]).unwrap();
let account = e.account().clone();
if let Some(commit_state) =
Module::<T>::effect_transfer(&account, &transfer_to, value, e.account_db())
{
e.account_db_mut().merge(commit_state);
}
Ok(sandbox::ReturnValue::Unit)
}
// ext_create(code_ptr: u32, code_len: u32, value: u32)
fn ext_create<T: Trait>(e: &mut ExecutionExt<T>, args: &[sandbox::TypedValue]) -> Result<sandbox::ReturnValue, sandbox::HostError> {
let code_ptr = args[0].as_i32().unwrap() as u32;
let code_len = args[1].as_i32().unwrap() as u32;
let value = args[2].as_i32().unwrap() as u32;
// TODO: slicable
let value = T::Balance::sa(value as usize);
let mut code = Vec::new();
code.resize(code_len as usize, 0u8);
e.memory().get(code_ptr, &mut code)?;
let account = e.account().clone();
if let Some(commit_state) =
Module::<T>::effect_create(&account, &code, value, e.account_db())
{
e.account_db_mut().merge(commit_state);
}
Ok(sandbox::ReturnValue::Unit)
}
// TODO: Inspect the binary to extract the initial page count.
let memory = match sandbox::Memory::new(1, None) {
Ok(memory) => memory,
Err(_) => return false,
};
let mut imports = sandbox::EnvironmentDefinitionBuilder::new();
imports.add_host_func("env", "ext_set_storage", ext_set_storage::<T>);
imports.add_host_func("env", "ext_get_storage", ext_get_storage::<T>);
imports.add_host_func("env", "ext_transfer", ext_transfer::<T>);
imports.add_host_func("env", "ext_create", ext_create::<T>);
// TODO: ext_balance, ext_address, ext_callvalue, etc.
imports.add_memory("env", "memory", memory.clone());
let mut exec_ext = ExecutionExt {
account: account.clone(),
account_db,
memory,
};
let mut instance = match sandbox::Instance::new(code, &imports, &mut exec_ext) {
Ok(instance) => instance,
Err(_err) => return false,
};
instance.invoke(b"call", &[], &mut exec_ext).is_ok()
}
+223 -30
View File
@@ -21,6 +21,9 @@
#[cfg(feature = "std")]
extern crate serde;
#[cfg(test)]
extern crate wabt;
#[macro_use]
extern crate substrate_runtime_support as runtime_support;
@@ -32,6 +35,7 @@ extern crate substrate_primitives;
extern crate substrate_runtime_io as runtime_io;
extern crate substrate_runtime_primitives as primitives;
extern crate substrate_runtime_consensus as consensus;
extern crate substrate_runtime_sandbox as sandbox;
extern crate substrate_runtime_session as session;
extern crate substrate_runtime_system as system;
@@ -44,6 +48,8 @@ use codec::Slicable;
use runtime_support::{StorageValue, StorageMap, Parameter};
use primitives::traits::{Zero, One, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment};
mod contract;
#[cfg(test)]
#[derive(Debug, PartialEq, Clone)]
pub enum LockStatus<BlockNumber: Debug + PartialEq + Clone> {
@@ -169,8 +175,8 @@ impl<T: Trait> Module<T> {
/// Create a smart-contract account.
pub fn create(aux: &T::PublicAux, code: &[u8], value: T::Balance) {
// commit anything that made it this far to storage
if let Some(commit) = Self::effect_create(aux.ref_into(), code, value, DirectAccountDb) {
<AccountDb<T>>::merge(&DirectAccountDb, commit);
if let Some(commit) = Self::effect_create(aux.ref_into(), code, value, &DirectAccountDb) {
<AccountDb<T>>::merge(&mut DirectAccountDb, commit);
}
}
@@ -180,8 +186,8 @@ impl<T: Trait> Module<T> {
/// TODO: probably want to state gas-limit and gas-price.
fn transfer(aux: &T::PublicAux, dest: T::AccountId, value: T::Balance) {
// commit anything that made it this far to storage
if let Some(commit) = Self::effect_transfer(aux.ref_into(), &dest, value, DirectAccountDb) {
<AccountDb<T>>::merge(&DirectAccountDb, commit);
if let Some(commit) = Self::effect_transfer(aux.ref_into(), &dest, value, &DirectAccountDb) {
<AccountDb<T>>::merge(&mut DirectAccountDb, commit);
}
}
@@ -382,11 +388,11 @@ trait AccountDb<T: Trait> {
fn get_code(&self, account: &T::AccountId) -> Vec<u8>;
fn get_balance(&self, account: &T::AccountId) -> T::Balance;
fn set_storage(&self, account: &T::AccountId, location: Vec<u8>, value: Option<Vec<u8>>);
fn set_code(&self, account: &T::AccountId, code: Vec<u8>);
fn set_balance(&self, account: &T::AccountId, balance: T::Balance);
fn set_storage(&mut self, account: &T::AccountId, location: Vec<u8>, value: Option<Vec<u8>>);
fn set_code(&mut self, account: &T::AccountId, code: Vec<u8>);
fn set_balance(&mut self, account: &T::AccountId, balance: T::Balance);
fn merge(&self, state: State<T>);
fn merge(&mut self, state: State<T>);
}
struct DirectAccountDb;
@@ -400,20 +406,20 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
fn get_balance(&self, account: &T::AccountId) -> T::Balance {
<FreeBalance<T>>::get(account)
}
fn set_storage(&self, account: &T::AccountId, location: Vec<u8>, value: Option<Vec<u8>>) {
fn set_storage(&mut self, account: &T::AccountId, location: Vec<u8>, value: Option<Vec<u8>>) {
if let Some(value) = value {
<StorageOf<T>>::insert(&(account.clone(), location), &value);
} else {
<StorageOf<T>>::remove(&(account.clone(), location));
}
}
fn set_code(&self, account: &T::AccountId, code: Vec<u8>) {
fn set_code(&mut self, account: &T::AccountId, code: Vec<u8>) {
<CodeOf<T>>::insert(account, &code);
}
fn set_balance(&self, account: &T::AccountId, balance: T::Balance) {
fn set_balance(&mut self, account: &T::AccountId, balance: T::Balance) {
<FreeBalance<T>>::insert(account, balance);
}
fn merge(&self, s: State<T>) {
fn merge(&mut self, s: State<T>) {
for (address, changed) in s.into_iter() {
if let Some(balance) = changed.balance {
<FreeBalance<T>>::insert(&address, balance);
@@ -450,40 +456,50 @@ impl<'a, T: Trait> OverlayAccountDb<'a, T> {
}
impl<'a, T: Trait> AccountDb<T> for OverlayAccountDb<'a, T> {
fn get_storage(&self, account: &T::AccountId, location: &[u8]) -> Option<Vec<u8>> {
self.local.borrow().get(account)
self.local
.borrow()
.get(account)
.and_then(|a| a.storage.get(location))
.cloned()
.unwrap_or_else(|| self.underlying.get_storage(account, location))
}
fn get_code(&self, account: &T::AccountId) -> Vec<u8> {
self.local.borrow().get(account)
self.local
.borrow()
.get(account)
.and_then(|a| a.code.clone())
.unwrap_or_else(|| self.underlying.get_code(account))
}
fn get_balance(&self, account: &T::AccountId) -> T::Balance {
self.local.borrow().get(account)
self.local
.borrow()
.get(account)
.and_then(|a| a.balance)
.unwrap_or_else(|| self.underlying.get_balance(account))
}
fn set_storage(&self, account: &T::AccountId, location: Vec<u8>, value: Option<Vec<u8>>) {
self.local.borrow_mut()
fn set_storage(&mut self, account: &T::AccountId, location: Vec<u8>, value: Option<Vec<u8>>) {
self.local
.borrow_mut()
.entry(account.clone())
.or_insert(Default::default())
.storage.insert(location, value);
.storage
.insert(location, value);
}
fn set_code(&self, account: &T::AccountId, code: Vec<u8>) {
self.local.borrow_mut()
fn set_code(&mut self, account: &T::AccountId, code: Vec<u8>) {
self.local
.borrow_mut()
.entry(account.clone())
.or_insert(Default::default())
.code = Some(code);
}
fn set_balance(&self, account: &T::AccountId, balance: T::Balance) {
self.local.borrow_mut()
fn set_balance(&mut self, account: &T::AccountId, balance: T::Balance) {
self.local
.borrow_mut()
.entry(account.clone())
.or_insert(Default::default())
.balance = Some(balance);
}
fn merge(&self, s: State<T>) {
fn merge(&mut self, s: State<T>) {
let mut local = self.local.borrow_mut();
for (address, changed) in s.into_iter() {
@@ -511,7 +527,7 @@ impl<T: Trait> Module<T> {
transactor: &T::AccountId,
code: &[u8],
value: T::Balance,
account_db: DB
account_db: &DB,
) -> Option<State<T>> {
let from_balance = account_db.get_balance(transactor);
// TODO: a fee.
@@ -538,32 +554,34 @@ impl<T: Trait> Module<T> {
transactor: &T::AccountId,
dest: &T::AccountId,
value: T::Balance,
account_db: DB
account_db: &DB,
) -> Option<State<T>> {
let from_balance = account_db.get_balance(transactor);
assert!(from_balance >= value);
let to_balance = account_db.get_balance(dest);
assert!(<Bondage<T>>::get(transactor) <= <Bondage<T>>::get(dest));
assert!(to_balance + value > to_balance); // no overflow
assert!(to_balance + value > to_balance); // no overflow
// TODO: a fee, based upon gaslimit/gasprice.
// TODO: consider storing upper-bound for contract's gas limit in fixed-length runtime
// code in contract itself and use that.
// Our local overlay: Should be used for any transfers and creates that happen internally.
let overlay = OverlayAccountDb::new(&account_db);
let mut overlay = OverlayAccountDb::new(account_db);
if transactor != dest {
overlay.set_balance(transactor, from_balance - value);
overlay.set_balance(dest, to_balance + value);
}
let should_commit = {
let dest_code = overlay.get_code(dest);
let should_commit = if dest_code.is_empty() {
true
} else {
// TODO: logging (logs are just appended into a notable storage-based vector and cleared every
// block).
// TODO: if `overlay.get_code(dest)` isn't empty then execute code with `overlay`.
true
contract::execute(&dest_code, dest, &mut overlay)
};
if should_commit {
@@ -1025,4 +1043,179 @@ mod tests {
assert_eq!(Staking::free_balance(&2), 42);
});
}
#[test]
fn contract_transfer() {
let code_transfer = wabt::wat2wasm(
r#"
(module
;; ext_transfer(transfer_to: u32, transfer_to_len: u32, value: u32)
(import "env" "ext_transfer" (func $ext_transfer (param i32 i32 i32)))
(import "env" "memory" (memory 1))
(func (export "call")
(call $ext_transfer
(i32.const 4) ;; Pointer to "Transfer to" address.
(i32.const 8) ;; Length of "Transfer to" address.
(i32.const 6) ;; value to transfer
)
)
;; Destination AccountId to transfer the funds.
;; Represented by u64 (8 bytes long) in little endian.
(data (i32.const 4) "\02\00\00\00\00\00\00\00")
)
"#,
).unwrap();
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(0, 111);
<FreeBalance<Test>>::insert(1, 0);
<FreeBalance<Test>>::insert(2, 30);
<CodeOf<Test>>::insert(1, code_transfer.to_vec());
Staking::transfer(&0, 1, 11);
assert_eq!(Staking::balance(&0), 100);
assert_eq!(Staking::balance(&1), 5);
assert_eq!(Staking::balance(&2), 36);
});
}
fn escaped_bytestring(bytes: &[u8]) -> String {
use std::fmt::Write;
let mut result = String::new();
for b in bytes {
write!(result, "\\{:02x}", b).unwrap();
}
result
}
#[test]
fn contract_create() {
let code_transfer = wabt::wat2wasm(
r#"
(module
;; ext_transfer(transfer_to: u32, transfer_to_len: u32, value: u32)
(import "env" "ext_transfer" (func $ext_transfer (param i32 i32 i32)))
(import "env" "memory" (memory 1))
(func (export "call")
(call $ext_transfer
(i32.const 4) ;; Pointer to "Transfer to" address.
(i32.const 8) ;; Length of "Transfer to" address.
(i32.const 6) ;; value to transfer
)
)
;; Destination AccountId to transfer the funds.
;; Represented by u64 (8 bytes long) in little endian.
(data (i32.const 4) "\02\00\00\00\00\00\00\00")
)
"#,
).unwrap();
let code_create = wabt::wat2wasm(format!(
r#"
(module
;; ext_create(code_ptr: u32, code_len: u32, value: u32)
(import "env" "ext_create" (func $ext_create (param i32 i32 i32)))
(import "env" "memory" (memory 1))
(func (export "call")
(call $ext_create
(i32.const 4) ;; Pointer to `code`
(i32.const {code_length}) ;; Length of `code`
(i32.const 3) ;; Value to transfer
)
)
(data (i32.const 4) "{escaped_code_transfer}")
)
"#,
escaped_code_transfer = escaped_bytestring(&code_transfer),
code_length = code_transfer.len(),
)).unwrap();
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(0, 111);
<FreeBalance<Test>>::insert(1, 0);
<CodeOf<Test>>::insert(1, code_create.to_vec());
// When invoked, the contract at address `1` must create a contract with 'transfer' code.
Staking::transfer(&0, 1, 11);
let derived_address =
<Test as Trait>::DetermineContractAddress::contract_address_for(&code_transfer, &1);
assert_eq!(Staking::balance(&0), 100);
assert_eq!(Staking::balance(&1), 8);
assert_eq!(Staking::balance(&derived_address), 3);
});
}
#[test]
fn contract_adder() {
let code_adder = wabt::wat2wasm(r#"
(module
;; ext_set_storage(location_ptr: i32, value_non_null: bool, value_ptr: i32)
(import "env" "ext_set_storage" (func $ext_set_storage (param i32 i32 i32)))
;; ext_get_storage(location_ptr: i32, value_ptr: i32)
(import "env" "ext_get_storage" (func $ext_get_storage (param i32 i32)))
(import "env" "memory" (memory 1))
(func (export "call")
(call $ext_get_storage
(i32.const 4) ;; Point to a location of the storage.
(i32.const 36) ;; The result will be written at this address.
)
(i32.store
(i32.const 36)
(i32.add
(i32.load
(i32.const 36)
)
(i32.const 1)
)
)
(call $ext_set_storage
(i32.const 4) ;; Pointer to a location of the storage.
(i32.const 1) ;; Value is not null.
(i32.const 36) ;; Pointer to a data we want to put in the storage.
)
)
;; Location of storage to put the data. 32 bytes.
(data (i32.const 4) "\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01\01")
)
"#).unwrap();
with_externalities(&mut new_test_ext(1, 3, 1, false), || {
<FreeBalance<Test>>::insert(0, 111);
<FreeBalance<Test>>::insert(1, 0);
<CodeOf<Test>>::insert(1, code_adder);
Staking::transfer(&0, 1, 1);
Staking::transfer(&0, 1, 1);
let storage_addr = [0x01u8; 32];
let value =
AccountDb::<Test>::get_storage(&DirectAccountDb, &1, &storage_addr).unwrap();
assert_eq!(
&value,
&[
2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]
);
});
}
}
+39
View File
@@ -289,6 +289,11 @@ dependencies = [
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memory_units"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memorydb"
version = "0.1.1"
@@ -329,6 +334,16 @@ dependencies = [
"stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parity-wasm"
version = "0.27.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot"
version = "0.4.8"
@@ -338,6 +353,15 @@ dependencies = [
"parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "parking_lot_core"
version = "0.2.10"
@@ -591,6 +615,7 @@ dependencies = [
"substrate-runtime-std 0.1.0",
"twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"uint 0.1.2 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)",
"wasmi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -765,6 +790,16 @@ name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wasmi"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.2.8"
@@ -831,12 +866,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882"
"checksum memorydb 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "013b7e4c5e10c764936ebc6bd3662d8e3c92292d267bf6a42ef3f5cad9c793ee"
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
"checksum odds 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parity-wasm 0.27.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bd4dc02a80a0315b109e48992c46942c79bcdb8fac416dd575d330ed9ced6cbd"
"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
"checksum parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd9d732f2de194336fb02fe11f9eed13d9e76f13f4315b4d88a14ca411750cd"
"checksum parking_lot_core 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "9f35048d735bb93dd115a0030498785971aab3234d311fbe273d020084d26bd8"
"checksum patricia-trie 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f1e2f638d79aba5c4a71a4f373df6e3cd702250a53b7f0ed4da1e2a7be9737ae"
"checksum plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83ae80873992f511142c07d0ec6c44de5636628fdb7e204abd655932ea79d995"
@@ -878,6 +916,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum wasmi 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "26b20dbeb7caee04597a5d2c93e2b3e64872c6ea2af732d7ad49dbec44067c35"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
@@ -4,3 +4,4 @@ rustup update nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
rustup update stable
cargo install --git https://github.com/alexcrichton/wasm-gc
cargo install --git https://github.com/pepyakin/wasm-export-table.git