mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 20:57:59 +00:00
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:
committed by
Robert Habermeier
parent
f116f67382
commit
5a56fbcea3
Generated
+52
-4
@@ -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"
|
||||
|
||||
@@ -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,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
|
||||
|
||||
Generated
+54
-8
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
+54
-8
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -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()? {
|
||||
|
||||
@@ -22,3 +22,4 @@ log = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.1"
|
||||
wabt = "0.1.7"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -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
@@ -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 = []
|
||||
Executable
+14
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user