diff --git a/Cargo.lock b/Cargo.lock index 976b1ea5ee5d2..d832d629e9347 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1129,6 +1129,14 @@ dependencies = [ "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-wasm" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parity-wordlist" version = "1.2.0" @@ -1499,6 +1507,16 @@ dependencies = [ name = "pwasm-libc" version = "0.1.0" +[[package]] +name = "pwasm-utils" +version = "0.2.0" +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.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quick-error" version = "1.2.1" @@ -2179,7 +2197,10 @@ dependencies = [ name = "substrate-runtime-staking" version = "0.1.0" dependencies = [ + "assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pwasm-utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 0.1.0", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-codec 0.1.0", @@ -2824,6 +2845,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum parity-wasm 0.27.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba1ceaec13865445bcf05117867e4c6456d91c3617cdff2f3ef77b92b18cd12" +"checksum parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41083957b80abb8a01fac4d2773d5f92653aed8f0b740c8d3da1da62c7857abe" "checksum parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0dec124478845b142f68b446cbee953d14d4b41f1bc0425024417720dce693" "checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" "checksum parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e7f7c9857874e54afeb950eebeae662b1e51a2493666d2ea4c0a5d91dcf0412" @@ -2837,6 +2859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" "checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" "checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07" +"checksum pwasm-utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3a822d2a1624b10c46572c231c149575bcc261c37d84fd3f1a2f5ae1f65515" "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" diff --git a/demo/runtime/wasm/Cargo.lock b/demo/runtime/wasm/Cargo.lock index 461b9c9645875..eeff3dee611df 100644 --- a/demo/runtime/wasm/Cargo.lock +++ b/demo/runtime/wasm/Cargo.lock @@ -389,6 +389,14 @@ dependencies = [ "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-wasm" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot" version = "0.4.8" @@ -469,6 +477,16 @@ dependencies = [ name = "pwasm-libc" version = "0.1.0" +[[package]] +name = "pwasm-utils" +version = "0.2.0" +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.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "0.3.15" @@ -813,6 +831,8 @@ name = "substrate-runtime-staking" version = "0.1.0" dependencies = [ "hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pwasm-utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 0.1.0", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-codec 0.1.0", @@ -1087,6 +1107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "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 parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41083957b80abb8a01fac4d2773d5f92653aed8f0b740c8d3da1da62c7857abe" "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" @@ -1094,6 +1115,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83ae80873992f511142c07d0ec6c44de5636628fdb7e204abd655932ea79d995" "checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" "checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" +"checksum pwasm-utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3a822d2a1624b10c46572c231c149575bcc261c37d84fd3f1a2f5ae1f65515" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" diff --git a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm index e017912fbaa07..ea052a72d3539 100644 Binary files a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm and b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm differ diff --git a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm index d4dcdde39a444..cd5b6a31d26c4 100755 Binary files a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm and b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm differ diff --git a/polkadot/runtime/wasm/Cargo.lock b/polkadot/runtime/wasm/Cargo.lock index 6e14575403a4a..b667adc8edcb7 100644 --- a/polkadot/runtime/wasm/Cargo.lock +++ b/polkadot/runtime/wasm/Cargo.lock @@ -354,6 +354,14 @@ dependencies = [ "parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-wasm" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot" version = "0.4.8" @@ -469,6 +477,16 @@ dependencies = [ name = "pwasm-libc" version = "0.1.0" +[[package]] +name = "pwasm-utils" +version = "0.2.0" +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.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "0.3.15" @@ -813,6 +831,8 @@ name = "substrate-runtime-staking" version = "0.1.0" dependencies = [ "hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pwasm-utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 0.1.0", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-codec 0.1.0", @@ -1087,6 +1107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "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 parity-wasm 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41083957b80abb8a01fac4d2773d5f92653aed8f0b740c8d3da1da62c7857abe" "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" @@ -1094,6 +1115,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum plain_hasher 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83ae80873992f511142c07d0ec6c44de5636628fdb7e204abd655932ea79d995" "checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" "checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" +"checksum pwasm-utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3a822d2a1624b10c46572c231c149575bcc261c37d84fd3f1a2f5ae1f65515" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm index 07c28e8cff597..a45d2f1b65f01 100644 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm differ diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm index 777e44d49c173..48622a5114b17 100755 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm differ diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm index 056404f6f4e86..aac5a0d9da495 100644 Binary files a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm and b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm differ diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm index d8868180bb90f..c755d6e9e58fd 100755 Binary files a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm and b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm differ diff --git a/substrate/runtime/staking/Cargo.toml b/substrate/runtime/staking/Cargo.toml index 9c2d3d6a20c86..9c81d072fee73 100644 --- a/substrate/runtime/staking/Cargo.toml +++ b/substrate/runtime/staking/Cargo.toml @@ -18,9 +18,12 @@ substrate-runtime-primitives = { path = "../primitives", default_features = fals substrate-runtime-consensus = { path = "../consensus", default_features = false } substrate-runtime-system = { path = "../system", default_features = false } substrate-runtime-session = { path = "../session", default_features = false } +parity-wasm = { version = "0.30", default_features = false } +pwasm-utils = { version = "0.2", default_features = false } [dev-dependencies] wabt = "0.1.7" +assert_matches = "1.1" [features] default = ["std"] @@ -37,4 +40,6 @@ std = [ "substrate-runtime-primitives/std", "substrate-runtime-session/std", "substrate-runtime-system/std", + "pwasm-utils/std", + "parity-wasm/std", ] diff --git a/substrate/runtime/staking/src/contract.rs b/substrate/runtime/staking/src/contract.rs index 1235a1e52f3d2..41a9ddef6d95a 100644 --- a/substrate/runtime/staking/src/contract.rs +++ b/substrate/runtime/staking/src/contract.rs @@ -16,16 +16,64 @@ //! Smart-contract execution module. +// TODO: Extract to it's own crate? + use codec::Slicable; use primitives::traits::As; use rstd::prelude::*; use sandbox; use {AccountDb, Module, OverlayAccountDb, Trait}; +use parity_wasm::elements::{self, External, MemoryType}; +use pwasm_utils; +use pwasm_utils::rules; + +/// Error that can occur while preparing or executing wasm smart-contract. +#[derive(Debug, PartialEq, Eq)] +pub enum Error { + /// Error happened while serializing the module. + Serialization, + + /// Error happened while deserializing the module. + Deserialization, + + /// Internal memory declaration has been found in the module. + InternalMemoryDeclared, + + /// Gas instrumentation failed. + /// + /// This most likely indicates the module isn't valid. + GasInstrumentation, + + /// Stack instrumentation failed. + /// + /// This most likely indicates the module isn't valid. + StackHeightInstrumentation, + + /// Error happened during invocation of the contract's entrypoint. + /// + /// Most likely because of trap. + Invoke, + + /// Error happened during instantiation. + /// + /// This might indicate that `start` function trapped, or module isn't + /// instantiable and/or unlinkable. + Instantiate, + + /// Memory creation error. + /// + /// This might happen when the memory import has invalid descriptor or + /// requested too much resources. + Memory, +} + struct ExecutionExt<'a, 'b: 'a, T: Trait + 'b> { account_db: &'a mut OverlayAccountDb<'b, T>, account: T::AccountId, memory: sandbox::Memory, + gas_used: u64, + gas_limit: u64, } impl<'a, 'b: 'a, T: Trait> ExecutionExt<'a, 'b, T> { fn account(&self) -> &T::AccountId { @@ -40,13 +88,44 @@ impl<'a, 'b: 'a, T: Trait> ExecutionExt<'a, 'b, T> { fn memory(&self) -> &sandbox::Memory { &self.memory } + /// Account for used gas. + /// + /// Returns `false` if there is not enough gas or addition of the specified + /// amount of gas has lead to overflow. On success returns `true`. + /// + /// Intuition about the return value sense is to answer the question 'are we allowed to continue?' + fn charge_gas(&mut self, amount: u64) -> bool { + match self.gas_used.checked_add(amount) { + None => false, + Some(val) if val > self.gas_limit => false, + Some(val) => { + self.gas_used = val; + true + } + } + } } pub(crate) fn execute<'a, 'b: 'a, T: Trait>( code: &[u8], account: &T::AccountId, account_db: &'a mut OverlayAccountDb<'b, T>, -) -> bool { + gas_limit: u64, +) -> Result<(), Error> { + // ext_gas(amount: u32) + // + // Account for used gas. Traps if gas used is greater than gas limit. + // + // - amount: How much gas is used. + fn ext_gas(e: &mut ExecutionExt, args: &[sandbox::TypedValue]) -> Result { + let amount = args[0].as_i32().unwrap() as u32; + if e.charge_gas(amount as u64) { + Ok(sandbox::ReturnValue::Unit) + } else { + Err(sandbox::HostError) + } + } + // 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. @@ -153,13 +232,13 @@ pub(crate) fn execute<'a, 'b: 'a, T: Trait>( 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 PreparedContract { + instrumented_code, + memory, + } = prepare_contract(code)?; let mut imports = sandbox::EnvironmentDefinitionBuilder::new(); + imports.add_host_func("env", "gas", ext_gas::); imports.add_host_func("env", "ext_set_storage", ext_set_storage::); imports.add_host_func("env", "ext_get_storage", ext_get_storage::); imports.add_host_func("env", "ext_transfer", ext_transfer::); @@ -171,11 +250,514 @@ pub(crate) fn execute<'a, 'b: 'a, T: Trait>( account: account.clone(), account_db, memory, + gas_limit, + gas_used: 0, }; - 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() + let mut instance = + sandbox::Instance::new(&instrumented_code, &imports, &mut exec_ext) + .map_err(|_| Error::Instantiate)?; + instance + .invoke(b"call", &[], &mut exec_ext) + .map(|_| ()) + .map_err(|_| Error::Invoke) +} + +#[derive(Clone)] +struct Config { + /// Gas cost of a growing memory by single page. + grow_mem_cost: u32, + + /// Gas cost of a regular operation. + regular_op_cost: u32, + + /// How tall the stack is allowed to grow? + /// + /// See https://wiki.parity.io/WebAssembly-StackHeight to find out + /// how the stack frame cost is calculated. + max_stack_height: u32, + + //// What is the maximal memory pages amount is allowed to have for + /// a contract. + max_memory_pages: u32, +} + +impl Default for Config { + fn default() -> Config { + Config { + grow_mem_cost: 1, + regular_op_cost: 1, + max_stack_height: 64 * 1024, + max_memory_pages: 16, + } + } +} + +struct ContractModule { + // An `Option` is used here for loaning (`take()`-ing) the module. + // Invariant: Can't be `None` (i.e. on enter and on exit from the function + // the value *must* be `Some`). + module: Option, + config: Config, +} + +impl ContractModule { + fn new(original_code: &[u8], config: Config) -> Result { + let module = + elements::deserialize_buffer(original_code).map_err(|_| Error::Deserialization)?; + Ok(ContractModule { + module: Some(module), + config, + }) + } + + /// Ensures that module doesn't declare internal memories. + /// + /// In this runtime we only allow wasm module to import memory from the environment. + /// Memory section contains declarations of internal linear memories, so if we find one + /// we reject such a module. + fn ensure_no_internal_memory(&self) -> Result<(), Error> { + let module = self.module + .as_ref() + .expect("On entry to the function `module` can't be None; qed"); + if module + .memory_section() + .map_or(false, |ms| ms.entries().len() > 0) + { + return Err(Error::InternalMemoryDeclared); + } + Ok(()) + } + + fn inject_gas_metering(&mut self) -> Result<(), Error> { + let gas_rules = rules::Set::new(self.config.regular_op_cost, Default::default()) + .with_grow_cost(self.config.grow_mem_cost) + .with_forbidden_floats(); + + let module = self.module + .take() + .expect("On entry to the function `module` can't be `None`; qed"); + + let contract_module = pwasm_utils::inject_gas_counter(module, &gas_rules) + .map_err(|_| Error::GasInstrumentation)?; + + self.module = Some(contract_module); + Ok(()) + } + + fn inject_stack_height_metering(&mut self) -> Result<(), Error> { + let module = self.module + .take() + .expect("On entry to the function `module` can't be `None`; qed"); + + let contract_module = + pwasm_utils::stack_height::inject_limiter(module, self.config.max_stack_height) + .map_err(|_| Error::StackHeightInstrumentation)?; + + self.module = Some(contract_module); + Ok(()) + } + + /// Find the memory import entry and return it's descriptor. + fn find_mem_import(&self) -> Option<&MemoryType> { + let import_section = self.module + .as_ref() + .expect("On entry to the function `module` can't be `None`; qed") + .import_section()?; + for import in import_section.entries() { + if let ("env", "memory", &External::Memory(ref memory_type)) = + (import.module(), import.field(), import.external()) + { + return Some(memory_type); + } + } + None + } + + fn into_wasm_code(mut self) -> Result, Error> { + elements::serialize( + self.module + .take() + .expect("On entry to the function `module` can't be `None`; qed"), + ).map_err(|_| Error::Serialization) + } +} + +struct PreparedContract { + instrumented_code: Vec, + memory: sandbox::Memory, +} + +fn prepare_contract(original_code: &[u8]) -> Result { + let config = Config::default(); + + let mut contract_module = ContractModule::new(original_code, config.clone())?; + contract_module.ensure_no_internal_memory()?; + contract_module.inject_gas_metering()?; + contract_module.inject_stack_height_metering()?; + + // Inspect the module to extract the initial and maximum page count. + let memory = match contract_module.find_mem_import() { + Some(memory_type) => { + let limits = memory_type.limits(); + match (limits.initial(), limits.maximum()) { + (initial, Some(maximum)) if initial > maximum => { + // Requested initial number of pages should not exceed the requested maximum. + return Err(Error::Memory); + } + (_, Some(maximum)) if maximum > config.max_memory_pages => { + // Maximum number of pages should not exceed the configured maximum. + return Err(Error::Memory); + } + (_, None) => { + // Maximum number of pages should be always declared. + // This isn't a hard requirement and can be treated as a maxiumum set + // to configured maximum. + return Err(Error::Memory) + } + (initial, maximum) => sandbox::Memory::new( + initial, + maximum, + ) + } + }, + + // If none memory imported then just crate an empty placeholder. + // Any access to it will lead to out of bounds trap. + None => sandbox::Memory::new(0, Some(0)), + }.map_err(|_| Error::Memory)?; + + Ok(PreparedContract { + instrumented_code: contract_module.into_wasm_code()?, + memory, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fmt; + use wabt; + use runtime_io::with_externalities; + use mock::{Staking, Test, new_test_ext}; + use ::{CodeOf, ContractAddressFor, DirectAccountDb, FreeBalance, StorageMap}; + + impl fmt::Debug for PreparedContract { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "PreparedContract {{ .. }}") + } + } + + fn parse_and_prepare_wat(wat: &str) -> Result { + let wasm = wabt::Wat2Wasm::new() + .validate(false) + .convert(wat) + .unwrap(); + prepare_contract(wasm.as_ref()) + } + + #[test] + fn internal_memory_declaration() { + let r = parse_and_prepare_wat( + r#"(module (memory 1 1))"#, + ); + assert_matches!(r, Err(Error::InternalMemoryDeclared)); + } + + #[test] + fn memory() { + // This test assumes that maximum page number is configured to a certain number. + assert_eq!(Config::default().max_memory_pages, 16); + + let r = parse_and_prepare_wat( + r#"(module (import "env" "memory" (memory 1 1)))"#, + ); + assert_matches!(r, Ok(_)); + + // No memory import + let r = parse_and_prepare_wat( + r#"(module)"#, + ); + assert_matches!(r, Ok(_)); + + // incorrect import name. That's kinda ok, since this will fail + // at later stage when imports will be resolved. + let r = parse_and_prepare_wat( + r#"(module (import "vne" "memory" (memory 1 1)))"#, + ); + assert_matches!(r, Ok(_)); + + // initial exceed maximum + let r = parse_and_prepare_wat( + r#"(module (import "env" "memory" (memory 16 1)))"#, + ); + assert_matches!(r, Err(Error::Memory)); + + // no maximum + let r = parse_and_prepare_wat( + r#"(module (import "env" "memory" (memory 1)))"#, + ); + assert_matches!(r, Err(Error::Memory)); + + // requested maximum exceed configured maximum + let r = parse_and_prepare_wat( + r#"(module (import "env" "memory" (memory 1 17)))"#, + ); + assert_matches!(r, Err(Error::Memory)); + } + + #[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 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), || { + >::insert(0, 111); + >::insert(1, 0); + >::insert(2, 30); + + >::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 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 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), || { + >::insert(0, 111); + >::insert(1, 0); + + >::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 = + ::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 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), || { + >::insert(0, 111); + >::insert(1, 0); + >::insert(1, code_adder); + + Staking::transfer(&0, 1, 1); + Staking::transfer(&0, 1, 1); + + let storage_addr = [0x01u8; 32]; + let value = + AccountDb::::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, + ] + ); + }); + } + + #[test] + fn contract_out_of_gas() { + // This code should make 100_000 iterations so it should + // consume more than 100_000 units of gas. + let code_loop = wabt::wat2wasm( + r#" +(module + (func (export "call") + (local $i i32) + + loop $l + ;; $i = $i + 1 + (set_local $i + (i32.add + (get_local $i) + (i32.const 1) + ) + ) + + ;; if $i < 100_000u32: goto $l + (br_if $l + (i32.lt_u + (get_local $i) + (i32.const 100000) + ) + ) + end + ) +) + "#, + ).unwrap(); + + with_externalities(&mut new_test_ext(1, 3, 1, false), || { + // Set initial balances. + >::insert(0, 111); + >::insert(1, 0); + + >::insert(1, code_loop.to_vec()); + + // Transfer some balance from 0 to 1. This will trigger execution + // of the smart-contract code at address 1. + Staking::transfer(&0, 1, 11); + + // The balance should remain unchanged since we are expecting + // out-of-gas error which will revert transfer. + assert_eq!(Staking::balance(&0), 111); + }); + } + + #[test] + fn contract_internal_mem() { + let code_mem = wabt::wat2wasm( + r#" +(module + ;; Internal memory is not allowed. + (memory 1 1) + + (func (export "call") + nop + ) +) + "#, + ).unwrap(); + + with_externalities(&mut new_test_ext(1, 3, 1, false), || { + // Set initial balances. + >::insert(0, 111); + >::insert(1, 0); + + >::insert(1, code_mem.to_vec()); + + // Transfer some balance from 0 to 1. + Staking::transfer(&0, 1, 11); + + // The balance should remain unchanged since we are expecting + // validation error caused by internal memory declaration. + assert_eq!(Staking::balance(&0), 111); + }); + } } diff --git a/substrate/runtime/staking/src/lib.rs b/substrate/runtime/staking/src/lib.rs index 67f8eeef0b16b..742326a4f53c0 100644 --- a/substrate/runtime/staking/src/lib.rs +++ b/substrate/runtime/staking/src/lib.rs @@ -24,6 +24,10 @@ extern crate serde; #[cfg(test)] extern crate wabt; +#[cfg(test)] +#[macro_use] +extern crate assert_matches; + #[macro_use] extern crate substrate_runtime_support as runtime_support; @@ -38,6 +42,8 @@ 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; +extern crate pwasm_utils; +extern crate parity_wasm; #[cfg(test)] use std::fmt::Debug; use rstd::prelude::*; @@ -49,6 +55,8 @@ use runtime_support::{StorageValue, StorageMap, Parameter}; use primitives::traits::{Zero, One, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment}; mod contract; +#[cfg(test)] +mod mock; #[cfg(test)] #[derive(Debug, PartialEq, Clone)] @@ -564,6 +572,8 @@ impl Module { assert!(to_balance + value > to_balance); // no overflow // TODO: a fee, based upon gaslimit/gasprice. + let gas_limit = 100_000; + // TODO: consider storing upper-bound for contract's gas limit in fixed-length runtime // code in contract itself and use that. @@ -581,7 +591,7 @@ impl Module { } else { // TODO: logging (logs are just appended into a notable storage-based vector and cleared every // block). - contract::execute(&dest_code, dest, &mut overlay) + contract::execute(&dest_code, dest, &mut overlay, gas_limit).is_ok() }; if should_commit { @@ -703,61 +713,7 @@ impl primitives::BuildExternalities for GenesisConfig { mod tests { use super::*; use runtime_io::with_externalities; - use substrate_primitives::H256; - use primitives::BuildExternalities; - use primitives::traits::{HasPublicAux, Identity}; - use primitives::testing::{Digest, Header}; - - pub struct Test; - impl HasPublicAux for Test { - type PublicAux = u64; - } - impl consensus::Trait for Test { - type PublicAux = ::PublicAux; - type SessionKey = u64; - } - impl system::Trait for Test { - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = runtime_io::BlakeTwo256; - type Digest = Digest; - type AccountId = u64; - type Header = Header; - } - impl session::Trait for Test { - type ConvertAccountIdToSessionKey = Identity; - } - impl Trait for Test { - type Balance = u64; - type DetermineContractAddress = DummyContractAddressFor; - } - - fn new_test_ext(session_length: u64, sessions_per_era: u64, current_era: u64, monied: bool) -> runtime_io::TestExternalities { - let mut t = system::GenesisConfig::::default().build_externalities(); - t.extend(consensus::GenesisConfig::{ - code: vec![], - authorities: vec![], - }.build_externalities()); - t.extend(session::GenesisConfig::{ - session_length, - validators: vec![10, 20], - }.build_externalities()); - t.extend(GenesisConfig::{ - sessions_per_era, - current_era, - balances: if monied { vec![(1, 10), (2, 20), (3, 30), (4, 40)] } else { vec![] }, - intentions: vec![], - validator_count: 2, - bonding_duration: 3, - transaction_fee: 0, - }.build_externalities()); - t - } - - type System = system::Module; - type Session = session::Module; - type Staking = Module; + use mock::*; #[test] fn staking_should_work() { @@ -1043,179 +999,4 @@ 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), || { - >::insert(0, 111); - >::insert(1, 0); - >::insert(2, 30); - - >::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), || { - >::insert(0, 111); - >::insert(1, 0); - - >::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 = - ::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), || { - >::insert(0, 111); - >::insert(1, 0); - >::insert(1, code_adder); - - Staking::transfer(&0, 1, 1); - Staking::transfer(&0, 1, 1); - - let storage_addr = [0x01u8; 32]; - let value = - AccountDb::::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, - ] - ); - }); - } } diff --git a/substrate/runtime/staking/src/mock.rs b/substrate/runtime/staking/src/mock.rs new file mode 100644 index 0000000000000..5769409519e53 --- /dev/null +++ b/substrate/runtime/staking/src/mock.rs @@ -0,0 +1,78 @@ +// 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 . + +//! Test utilities + +#![cfg(test)] + + +use primitives::BuildExternalities; +use primitives::traits::{HasPublicAux, Identity}; +use primitives::testing::{Digest, Header}; +use substrate_primitives::H256; +use runtime_io; +use {DummyContractAddressFor, GenesisConfig, Module, Trait, consensus, session, system}; + +pub struct Test; +impl HasPublicAux for Test { + type PublicAux = u64; +} +impl consensus::Trait for Test { + type PublicAux = ::PublicAux; + type SessionKey = u64; +} +impl system::Trait for Test { + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = runtime_io::BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Header = Header; +} +impl session::Trait for Test { + type ConvertAccountIdToSessionKey = Identity; +} +impl Trait for Test { + type Balance = u64; + type DetermineContractAddress = DummyContractAddressFor; +} + +pub fn new_test_ext(session_length: u64, sessions_per_era: u64, current_era: u64, monied: bool) -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default().build_externalities(); + t.extend(consensus::GenesisConfig::{ + code: vec![], + authorities: vec![], + }.build_externalities()); + t.extend(session::GenesisConfig::{ + session_length, + validators: vec![10, 20], + }.build_externalities()); + t.extend(GenesisConfig::{ + sessions_per_era, + current_era, + balances: if monied { vec![(1, 10), (2, 20), (3, 30), (4, 40)] } else { vec![] }, + intentions: vec![], + validator_count: 2, + bonding_duration: 3, + transaction_fee: 0, + }.build_externalities()); + t +} + +pub type System = system::Module; +pub type Session = session::Module; +pub type Staking = Module; diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index 4607caa9b3f8a..130c248c11651 100644 Binary files a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm and b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm index b7541aeb582fd..548f361da024f 100755 Binary files a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm and b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm differ