diff --git a/Cargo.lock b/Cargo.lock index 5a17eaed3199d..080a88f622a16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2569,6 +2569,7 @@ dependencies = [ "substrate-primitives 2.0.0", "substrate-session 2.0.0", "substrate-wasm-builder-runner 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-proof 2.0.0", ] [[package]] @@ -3826,12 +3827,14 @@ dependencies = [ "libsecp256k1 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-sandbox 2.0.0", "sr-std 2.0.0", "substrate-externalities 2.0.0", "substrate-primitives 2.0.0", "substrate-state-machine 2.0.0", "substrate-trie 2.0.0", "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -6248,6 +6251,17 @@ name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "wabt" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "wabt-sys 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "wabt" version = "0.9.2" @@ -6259,6 +6273,16 @@ dependencies = [ "wabt-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wabt-sys" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "wabt-sys" version = "0.7.0" @@ -6370,6 +6394,21 @@ dependencies = [ "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wasm-proof" +version = "2.0.0" +dependencies = [ + "hash-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-primitives 2.0.0", + "sr-sandbox 2.0.0", + "sr-std 2.0.0", + "substrate-primitives 2.0.0", + "substrate-state-machine 2.0.0", + "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "wasm-timer" version = "0.1.2" @@ -7055,7 +7094,9 @@ dependencies = [ "checksum vergen 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6aba5e34f93dc7051dfad05b98a18e9156f27e7b431fe1d2398cb6061c0a1dba" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "74e463a508e390cc7447e70f640fbf44ad52e1bd095314ace1fdf99516d32add" "checksum wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5c5c1286c6e578416982609f47594265f9d489f9b836157d403ad605a46693" +"checksum wabt-sys 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a6265b25719e82598d104b3717375e37661d41753e2c84cde3f51050c7ed7e3c" "checksum wabt-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "af5d153dc96aad7dc13ab90835b892c69867948112d95299e522d370c4e13a08" "checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" "checksum want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" diff --git a/Cargo.toml b/Cargo.toml index 7e34a0bd6ccd0..875057b4f819b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ vergen = "3.0.4" [workspace] members = [ + "core/wasm-proof", "core/authority-discovery", "core/application-crypto", "core/chain-spec", diff --git a/core/executor/src/host_interface.rs b/core/executor/src/host_interface.rs index 7c99415f6c752..4e6a8cc4b1584 100644 --- a/core/executor/src/host_interface.rs +++ b/core/executor/src/host_interface.rs @@ -132,6 +132,11 @@ impl_wasm_host_interface! { context.sandbox().memory_teardown(memory_idx) } + ext_run_wasm() { + runtime_io::run_wasm(); + Ok(()) + } + ext_print_utf8(utf8_data: Pointer, utf8_len: WordSize) { if let Ok(utf8) = context.read_memory(utf8_data, utf8_len) { runtime_io::print_utf8(&utf8); diff --git a/core/rpc/api/src/state/mod.rs b/core/rpc/api/src/state/mod.rs index 0d06092ca1659..29760a2ccb465 100644 --- a/core/rpc/api/src/state/mod.rs +++ b/core/rpc/api/src/state/mod.rs @@ -99,6 +99,10 @@ pub trait StateApi { #[rpc(name = "state_getRuntimeVersion", alias("chain_getRuntimeVersion"))] fn runtime_version(&self, hash: Option) -> FutureResult; + /// Reads storage value at a given block + key, returning read proof. + #[rpc(name = "state_getReadProof")] + fn read_proof(&self, id: Option, key: StorageKey) -> FutureResult>>; + /// Query historical storage entries (by key) starting from a block given as the second parameter. /// /// NOTE This first returned result contains the initial state of storage for all keys. diff --git a/core/rpc/src/state/mod.rs b/core/rpc/src/state/mod.rs index b922601b0a5b2..a65b5ada982f3 100644 --- a/core/rpc/src/state/mod.rs +++ b/core/rpc/src/state/mod.rs @@ -136,6 +136,9 @@ pub trait StateBackend: Send + Sync + 'static /// Get the runtime version. fn runtime_version(&self, block: Option) -> FutureResult; + /// Reads storage value at a given block + key, returning read proof. + fn read_proof(&self, block: Option, key: StorageKey) -> FutureResult>>; + /// Query historical storage entries (by key) starting from a block given as the second parameter. /// /// NOTE This first returned result contains the initial state of storage for all keys. @@ -323,6 +326,10 @@ impl StateApi for State self.backend.runtime_version(at) } + fn read_proof(&self, block: Option, key: StorageKey) -> FutureResult>> { + self.backend.read_proof(block, key) + } + fn subscribe_runtime_version(&self, meta: Self::Metadata, subscriber: Subscriber) { self.backend.subscribe_runtime_version(meta, subscriber); } diff --git a/core/rpc/src/state/state_full.rs b/core/rpc/src/state/state_full.rs index cd05093c3a8cb..d70e5179327a1 100644 --- a/core/rpc/src/state/state_full.rs +++ b/core/rpc/src/state/state_full.rs @@ -337,6 +337,17 @@ impl StateBackend for FullState, + key: StorageKey, + ) -> FutureResult>> { + Box::new(result( + self.block_or_best(block) + .and_then(|block| self.client.read_proof(&BlockId::Hash(block), &[key.0])) + .map_err(client_err))) + } + fn query_storage( &self, from: Block::Hash, diff --git a/core/rpc/src/state/state_light.rs b/core/rpc/src/state/state_light.rs index 3d0c7979e3995..051fc4d5fdb83 100644 --- a/core/rpc/src/state/state_light.rs +++ b/core/rpc/src/state/state_light.rs @@ -304,6 +304,14 @@ impl StateBackend for LightState, + key: StorageKey, + ) -> FutureResult>> { + Box::new(result(Err(client_err(ClientError::NotAvailableOnLightClient)))) + } + fn query_storage( &self, _from: Block::Hash, diff --git a/core/sr-io/Cargo.toml b/core/sr-io/Cargo.toml index e2b681b75bfd1..bab2da2650613 100644 --- a/core/sr-io/Cargo.toml +++ b/core/sr-io/Cargo.toml @@ -18,10 +18,15 @@ tiny-keccak = { version = "1.5.0", optional = true } substrate-state-machine = { path = "../state-machine", optional = true } trie = { package = "substrate-trie", path = "../trie", optional = true } externalities = { package = "substrate-externalities", path = "../externalities", optional = true } +sandbox = { package = "sr-sandbox", path = "../sr-sandbox", default-features = false } + +[dev-dependencies] +wabt = "~0.7.4" [features] default = ["std"] std = [ + "sandbox/std", "primitives/std", "codec/std", "rstd/std", diff --git a/core/sr-io/src/lib.rs b/core/sr-io/src/lib.rs index 24f964c7b5823..fb78f88fdf9cd 100644 --- a/core/sr-io/src/lib.rs +++ b/core/sr-io/src/lib.rs @@ -151,6 +151,7 @@ export_api! { pub(crate) trait OtherApi { /// The current relay chain identifier. fn chain_id() -> u64; + fn run_wasm(); /// Print a number. fn print_num(val: u64); diff --git a/core/sr-io/with_std.rs b/core/sr-io/with_std.rs index e431ae1f6e6bf..3bd48214bc6ea 100644 --- a/core/sr-io/with_std.rs +++ b/core/sr-io/with_std.rs @@ -28,6 +28,74 @@ use trie::{TrieConfiguration, trie_types::Layout}; use std::{collections::HashMap, convert::TryFrom}; use externalities::{with_externalities, set_and_run_with_externalities, ExternalitiesExt}; +use sandbox::{EnvironmentDefinitionBuilder, Error, HostError, Instance, ReturnValue, TypedValue}; + +// environmental!(ext: trait Externalities); + +fn execute_wasm(code: &[u8], args: &[TypedValue]) -> Result { + struct State { + counter: u32, + } + fn check_read_proof(_e: &mut State, _args: &[TypedValue]) -> Result { + // TODO: Add true verification here + Ok(ReturnValue::Value(TypedValue::I32(1))) + } + + let mut env_builder = EnvironmentDefinitionBuilder::new(); + + let mut state = State {counter: 0}; + + env_builder.add_host_func("env", "ext_check_read_proof", check_read_proof); + + let memory = match sandbox::Memory::new(100, Some(100)) { + Ok(m) => m, + Err(_) => unreachable!(" + Memory::new() can return Err only if parameters are borked; \ + We passing params here explicitly and they're correct; \ + Memory::new() can't return a Error qed" + ), + }; + + env_builder.add_memory("env", "memory", memory); + let mut instance = Instance::new(code, &env_builder, &mut state)?; + let result = instance.invoke(b"check_read_proof", args, &mut state); + + result.map_err(|err| { + HostError + }) +} + +#[test] +fn invoke_proof() { + let code = wabt::wat2wasm(r#" +(module + (type $t0 (func (result i32))) + (import "env" "memory" (memory $env.memory 17)) + (import "env" "ext_check_read_proof" (func $ext_check_read_proof (type $t0))) + (func $check_read_proof (type $t0) (result i32) + (local $l0 i32) + call $ext_check_read_proof + set_local $l0 + get_local $l0 + return) + (table $__indirect_function_table 1 1 anyfunc) + (global $__data_end i32 (i32.const 1048610)) + (global $__heap_base i32 (i32.const 1048610)) + (global $__rustc_debug_gdb_scripts_section__ i32 (i32.const 1048576)) + (export "__indirect_function_table" (table 0)) + (export "__data_end" (global 0)) + (export "__heap_base" (global 1)) + (export "__rustc_debug_gdb_scripts_section__" (global 2)) + (export "check_read_proof" (func $check_read_proof)) +) "#).unwrap(); + + let result = execute_wasm( + &code, + &[], + ); + assert_eq!(result.unwrap(), ReturnValue::Value(TypedValue::I32(1))); +} + /// Additional bounds for `Hasher` trait for with_std. pub trait HasherBounds {} @@ -179,6 +247,16 @@ impl OtherApi for () { ).unwrap_or(0) } + fn run_wasm() { + use std::fs; + + // TODO: Read wasm from chain + let code = fs::read("/tmp/proof.compact.wasm").expect("Wasm file not found"); + let args = []; + let res = execute_wasm(&code, &args); + println!("result: {:?}", res); + } + fn print_num(val: u64) { println!("{}", val); } diff --git a/core/sr-io/without_std.rs b/core/sr-io/without_std.rs index 90ec5a9ee4501..e03a0902c5666 100644 --- a/core/sr-io/without_std.rs +++ b/core/sr-io/without_std.rs @@ -154,6 +154,7 @@ pub mod ext { /// (most importantly, storage) or perform heavy hash calculations. /// See also "ext_" functions in sr-sandbox and sr-std extern_functions! { + fn ext_run_wasm(); /// Host functions for printing, useful for debugging. fn ext_print_utf8(utf8_data: *const u8, utf8_len: u32); /// Print data as hex. @@ -763,6 +764,12 @@ impl OtherApi for () { } } + fn run_wasm() { + unsafe { + ext_run_wasm.get()(); + } + } + fn print_num(val: u64) { unsafe { ext_print_num.get()(val); diff --git a/core/wasm-proof/Cargo.toml b/core/wasm-proof/Cargo.toml new file mode 100644 index 0000000000000..f0099ef78439d --- /dev/null +++ b/core/wasm-proof/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "wasm-proof" +version = "2.0.0" +authors = ["Parity Technologies "] +build = "build.rs" +edition = "2018" + +[build-dependencies] +rustc_version = "0.2.3" + +[dependencies] +rstd = { package = "sr-std", path = "../sr-std", default-features = false } +primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } +hash-db = { version = "0.15.2", default-features = false } +substrate-state-machine = { path = "../state-machine", optional = true } +sandbox = { package = "sr-sandbox", path = "../sr-sandbox", default-features = false } +sr_primitives = { package = "sr-primitives", path = "../sr-primitives", default-features = false } +#wasm-interface = { package = "substrate-wasm-interface", path = "../wasm-interface"} + +[dev-dependencies] +wabt = "~0.7.4" + +[features] +default = ["std"] +std = [ + "sandbox/std", + "primitives/std", + "codec/std", + "rstd/std", + "hash-db/std", + "substrate-state-machine", +] +nightly = [] +strict = [] + +# These two features are used for `no_std` builds for the environments which already provides +# `#[panic_handler]` and `#[alloc_error_handler]`. +# +# For the regular wasm runtime builds those are not used. +no_panic_handler = [] +no_oom = [] diff --git a/core/wasm-proof/build.rs b/core/wasm-proof/build.rs new file mode 100644 index 0000000000000..5b5d06b65a253 --- /dev/null +++ b/core/wasm-proof/build.rs @@ -0,0 +1,13 @@ +//! Set a nightly feature + +use rustc_version::{version, version_meta, Channel}; + +fn main() { + // Assert we haven't traveled back in time + assert!(version().unwrap().major >= 1); + + // Set cfg flags depending on release channel + if let Channel::Nightly = version_meta().unwrap().channel { + println!("cargo:rustc-cfg=feature=\"nightly\""); + } +} diff --git a/core/wasm-proof/src/lib.rs b/core/wasm-proof/src/lib.rs new file mode 100644 index 0000000000000..f6f257b31d55c --- /dev/null +++ b/core/wasm-proof/src/lib.rs @@ -0,0 +1,107 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! This is part of the Substrate runtime. + +#![warn(missing_docs)] + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(lang_items))] +#![cfg_attr(not(feature = "std"), feature(alloc_error_handler))] +#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] + +#![cfg_attr(feature = "std", doc = "Substrate runtime standard library as compiled when linked with Rust's standard library.")] +#![cfg_attr(not(feature = "std"), doc = "Substrate's runtime standard library as compiled without Rust's standard library.")] + +use sr_primitives::traits::{Block as BlockT, Header as HeaderT, BlakeTwo256}; +use sr_primitives::generic; +use rstd::vec::Vec; + +use sr_primitives::OpaqueExtrinsic as UncheckedExtrinsic; + +pub type Hash = primitives::H256; +pub type BlockNumber = u32; +pub type Header = generic::Header; +pub type Block = generic::Block; + +/// Remote storage read request. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct RemoteReadRequest { + /// Read at state of given block. + pub block: Header::Hash, + /// Header of block at which read is performed. + pub header: Header, + /// Storage key to read. + pub keys: Vec>, + /// Number of times to retry request. None means that default RETRY_COUNT is used. + pub retry_count: Option, +} + + +/// Converts a public trait definition into a private trait and set of public functions +/// that assume the trait is implemented for `()` for ease of calling. +macro_rules! export_api { + ( + $( #[$trait_attr:meta] )* + pub(crate) trait $trait_name:ident { + $( + $( #[$attr:meta] )* + fn $name:ident + ( $( $arg:ident : $arg_ty:ty ),* $(,)? ) + $( -> $ret:ty )? + $( where $( $w_name:path : $w_ty:path ),+ )?; + )* + } + ) => { + $( #[$trait_attr] )* + pub(crate) trait $trait_name { + $( + $( #[$attr] )* + fn $name ( $($arg : $arg_ty ),* ) $( -> $ret )? + $( where $( $w_name : $w_ty ),+ )?; + )* + } + + $( + $( #[$attr] )* + pub fn $name ( $($arg : $arg_ty ),* ) $( -> $ret )? + $( where $( $w_name : $w_ty ),+ )? + { + #[allow(deprecated)] + <()>:: $name ( $( $arg ),* ) + } + )* + } +} + +export_api! { + pub(crate) trait OtherApi { + fn run_wasm(request: &RemoteReadRequest
, remote_proof: Vec>); + } +} + +mod imp { + use super::*; + + #[cfg(feature = "std")] + include!("../with_std.rs"); + + #[cfg(not(feature = "std"))] + include!("../without_std.rs"); +} + +#[cfg(not(feature = "std"))] +pub use self::imp::ext::*; \ No newline at end of file diff --git a/core/wasm-proof/with_std.rs b/core/wasm-proof/with_std.rs new file mode 100644 index 0000000000000..0d401f70953b4 --- /dev/null +++ b/core/wasm-proof/with_std.rs @@ -0,0 +1,154 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . +use hash_db::Hasher; +use primitives::H256; +use sr_primitives::traits::{Block as BlockT, Header as HeaderT, BlakeTwo256}; +use sr_primitives::generic; +use substrate_state_machine::read_proof_check; +use primitives::Blake2Hasher; + +use std::collections::HashMap; +use std::marker::PhantomData; +use core::mem::transmute; + +use sandbox::{EnvironmentDefinitionBuilder, HostError, Instance, ReturnValue, TypedValue}; + +#[derive(Debug)] +pub enum ProofError { + Proof, +} + +/// Hash conversion. Used to convert between unbound associated hash types in traits, +/// implemented by the same hash type. +/// Panics if used to convert between different hash types. +fn convert_hash, H2: AsRef<[u8]>>(src: &H2) -> H1 { + let mut dest = H1::default(); + assert_eq!(dest.as_mut().len(), src.as_ref().len()); + dest.as_mut().copy_from_slice(src.as_ref()); + dest +} + +pub fn check_read_proof_>( + request: &RemoteReadRequest, + remote_proof: Vec>, +) -> Result, Option>>, ProofError> { + let _hasher: PhantomData<(Block, H)> = PhantomData; + read_proof_check::( + convert_hash(request.header.state_root()), + remote_proof, + request.keys.iter(), + ) + .map_err(|_e| ProofError::Proof) +} + + +fn execute_wasm(code: &[u8], args: &[TypedValue]) -> Result { + struct State { + counter: u32, + } + + fn check_read_proof(_e: &mut State, args: &[TypedValue]) -> Result { + if args.len() != 2 { + return Err(HostError); + } + let arg1 = match args[0] { + TypedValue::I64(p) => unsafe {std::mem::transmute::>(p) } , + _ => unreachable!(), + }; + + use std::vec::Vec; + let arg2 = match args[1] { + TypedValue::I64(p) => unsafe {std::mem::transmute::>>(p) }, + _ => unreachable!(), + }; + + let res = if check_read_proof_::(arg1, arg2.clone()).is_err() { + 0 + } else { + 1 + }; + + Ok(ReturnValue::Value(TypedValue::I32(res))) + } + + + let mut env_builder = EnvironmentDefinitionBuilder::new(); + + let mut state = State {counter: 0}; + + env_builder.add_host_func("env", "ext_check_read_proof", check_read_proof); + + let memory = match sandbox::Memory::new(100, Some(100)) { + Ok(m) => m, + Err(_) => unreachable!(" + Memory::new() can return Err only if parameters are borked; \ + We passing params here explicitly and they're correct; \ + Memory::new() can't return a Error qed" + ), + }; + + env_builder.add_memory("env", "memory", memory); + let mut instance = Instance::new(code, &env_builder, &mut state)?; + // we call check_read_proof there + let result = instance.invoke(b"check_read_proof", args, &mut state); + + result.map_err(|err| { + HostError + }) +} + +impl OtherApi for () { + fn run_wasm(request: &RemoteReadRequest
, remote_proof: Vec>) { + let arg1 = request as *const RemoteReadRequest
; + let arg2 = remote_proof.as_ptr(); + let args = [TypedValue::I64(arg1 as i64), TypedValue::I64(arg2 as i64)]; + let res = execute_wasm(&code, &args); + println!("result: {:?}", res); + } +} + + +// FIXME +#[test] +fn invoke_proof() { + let code = wabt::wat2wasm(r#" +(module + (type $t0 (func (result i32))) + (import "env" "memory" (memory $env.memory 17)) + (import "env" "ext_check_read_proof" (func $ext_check_read_proof (type $t0))) + (func $check_read_proof (type $t0) (result i32) + (local $l0 i32) + call $ext_check_read_proof + set_local $l0 + get_local $l0 + return) + (table $__indirect_function_table 1 1 anyfunc) + (global $__data_end i32 (i32.const 1048610)) + (global $__heap_base i32 (i32.const 1048610)) + (global $__rustc_debug_gdb_scripts_section__ i32 (i32.const 1048576)) + (export "__indirect_function_table" (table 0)) + (export "__data_end" (global 0)) + (export "__heap_base" (global 1)) + (export "__rustc_debug_gdb_scripts_section__" (global 2)) + (export "check_read_proof" (func $check_read_proof)) +) "#).unwrap(); + + let result = execute_wasm( + &code, + &[], + ); + assert_eq!(result.unwrap(), ReturnValue::Value(TypedValue::I32(1))); +} diff --git a/core/wasm-proof/without_std.rs b/core/wasm-proof/without_std.rs new file mode 100644 index 0000000000000..d408b9843959f --- /dev/null +++ b/core/wasm-proof/without_std.rs @@ -0,0 +1,148 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +#[doc(hidden)] +pub use rstd; +pub use rstd::{mem, slice}; + +use core::{intrinsics, panic::PanicInfo}; +use rstd::{vec::Vec, cell::Cell, convert::TryInto}; +use primitives::offchain; +use codec::Decode; + +/// External (Host) APIs +pub mod ext { + use super::*; + + /// The state of an exchangeable function. + #[derive(Clone, Copy)] + enum ExchangeableFunctionState { + /// Original function is present + Original, + /// The function has been replaced. + Replaced, + } + + /// A function which implementation can be exchanged. + /// + /// Internally this works by swapping function pointers. + pub struct ExchangeableFunction(Cell<(T, ExchangeableFunctionState)>); + + impl ExchangeableFunction { + /// Create a new instance of `ExchangeableFunction`. + pub const fn new(impl_: T) -> Self { + Self(Cell::new((impl_, ExchangeableFunctionState::Original))) + } + } + + impl ExchangeableFunction { + /// Replace the implementation with `new_impl`. + /// + /// # Panics + /// + /// Panics when trying to replace an already replaced implementation. + /// + /// # Returns + /// + /// Returns the original implementation wrapped in [`RestoreImplementation`]. + pub fn replace_implementation(&'static self, new_impl: T) -> RestoreImplementation { + if let ExchangeableFunctionState::Replaced = self.0.get().1 { + panic!("Trying to replace an already replaced implementation!") + } + + let old = self.0.replace((new_impl, ExchangeableFunctionState::Replaced)); + + RestoreImplementation(self, Some(old.0)) + } + + /// Restore the original implementation. + fn restore_orig_implementation(&self, orig: T) { + self.0.set((orig, ExchangeableFunctionState::Original)); + } + + /// Returns the internal function pointer. + pub fn get(&self) -> T { + self.0.get().0 + } + } + + // WASM does not support threads, so this is safe; qed. + unsafe impl Sync for ExchangeableFunction {} + + /// Restores a function implementation on drop. + /// + /// Stores a static reference to the function object and the original implementation. + pub struct RestoreImplementation(&'static ExchangeableFunction, Option); + + impl Drop for RestoreImplementation { + fn drop(&mut self) { + self.0.restore_orig_implementation(self.1.take().expect("Value is only taken on drop; qed")); + } + } + + /// Declare extern functions + macro_rules! extern_functions { + ( + $( + $( #[$attr:meta] )* + fn $name:ident ( $( $arg:ident : $arg_ty:ty ),* $(,)? ) $( -> $ret:ty )?; + )* + ) => { + $( + $( #[$attr] )* + #[allow(non_upper_case_globals)] + pub static $name: ExchangeableFunction $ret )?> = + ExchangeableFunction::new(extern_functions_host_impl::$name); + )* + + /// The exchangeable extern functions host implementations. + pub(crate) mod extern_functions_host_impl { + $( + pub unsafe fn $name ( $( $arg : $arg_ty ),* ) $( -> $ret )? { + implementation::$name ( $( $arg ),* ) + } + )* + + mod implementation { + extern "C" { + $( + pub fn $name ( $( $arg : $arg_ty ),* ) $( -> $ret )?; + )* + } + } + } + }; + } + + /// Host functions, provided by the executor. + /// A WebAssembly runtime module would "import" these to access the execution environment + /// (most importantly, storage) or perform heavy hash calculations. + /// See also "ext_" functions in sr-sandbox and sr-std + extern_functions! { + fn ext_run_wasm(); +} + +} +pub use self::ext::*; + +impl OtherApi for () { + fn run_wasm(request: &RemoteReadRequest
, remote_proof: Vec>) { + // FIXME: ext_run_wasm not impled + unsafe { + ext_run_wasm.get()(); + } + } +} \ No newline at end of file diff --git a/node-template/runtime/Cargo.toml b/node-template/runtime/Cargo.toml index 76821b6dfd8a2..76bcaba3097a4 100644 --- a/node-template/runtime/Cargo.toml +++ b/node-template/runtime/Cargo.toml @@ -10,6 +10,7 @@ safe-mix = { version = "1.0.0", default-features = false } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } rstd = { package = "sr-std", path = "../../core/sr-std", default_features = false } runtime-io = { package = "sr-io", path = "../../core/sr-io", default_features = false } +wasm-proof = { package = "wasm-proof", path = "../../core/wasm-proof", default_features = false } version = { package = "sr-version", path = "../../core/sr-version", default_features = false } support = { package = "srml-support", path = "../../srml/support", default_features = false } primitives = { package = "substrate-primitives", path = "../../core/primitives", default_features = false } @@ -34,6 +35,7 @@ wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1. [features] default = ["std"] std = [ + "wasm-proof/std", "codec/std", "client/std", "rstd/std", diff --git a/node-template/runtime/src/template.rs b/node-template/runtime/src/template.rs index eb4398787d5a1..bb39059909268 100644 --- a/node-template/runtime/src/template.rs +++ b/node-template/runtime/src/template.rs @@ -10,6 +10,7 @@ use support::{decl_module, decl_storage, decl_event, dispatch::Result}; use system::ensure_signed; +use wasm_proof::run_wasm; /// The module's configuration trait. pub trait Trait: system::Trait {