mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 17:41:08 +00:00
Migrate from parity-wasmi to wasmi. (#103)
This commit is contained in:
committed by
Gav Wood
parent
bd066e27a6
commit
b0d8eeb526
Generated
+21
-4
@@ -903,6 +903,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"
|
||||
@@ -1021,12 +1026,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parity-wasm"
|
||||
version = "0.15.4"
|
||||
version = "0.27.4"
|
||||
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.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1703,7 +1708,6 @@ dependencies = [
|
||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-wasm 0.15.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (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)",
|
||||
@@ -1713,6 +1717,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)",
|
||||
"wasmi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2320,6 +2325,16 @@ name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wasmi"
|
||||
version = "0.1.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)",
|
||||
"memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-wasm 0.27.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
@@ -2474,6 +2489,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
|
||||
"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
|
||||
"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 mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
|
||||
"checksum mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e00e17be181010a91dbfefb01660b17311059dc8c7f48b9017677721e732bd"
|
||||
@@ -2487,7 +2503,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 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.15.4 (registry+https://github.com/rust-lang/crates.io-index)" = "235801e9531998c4bb307f4ea6833c9f40a4cf132895219ac8c2cd25a9b310f7"
|
||||
"checksum parity-wasm 0.27.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba1ceaec13865445bcf05117867e4c6456d91c3617cdff2f3ef77b92b18cd12"
|
||||
"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"
|
||||
@@ -2575,6 +2591,7 @@ 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 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"
|
||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
|
||||
@@ -13,7 +13,7 @@ substrate-state-machine = { path = "../state-machine" }
|
||||
ed25519 = { path = "../ed25519" }
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
parity-wasm = "0.15.0"
|
||||
wasmi = "0.1.0"
|
||||
byteorder = "1.1"
|
||||
rustc-hex = "1.0.0"
|
||||
triehash = "0.1.0"
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
//! Rust executor possible errors.
|
||||
|
||||
use serializer;
|
||||
use wasmi;
|
||||
|
||||
error_chain! {
|
||||
foreign_links {
|
||||
InvalidData(serializer::Error) #[doc = "Unserializable Data"];
|
||||
Trap(wasmi::Trap) #[doc = "Trap occured during execution"];
|
||||
Wasmi(wasmi::Error) #[doc = "Wasmi loading/instantiating error"];
|
||||
}
|
||||
|
||||
errors {
|
||||
|
||||
@@ -35,7 +35,7 @@ extern crate substrate_state_machine as state_machine;
|
||||
extern crate ed25519;
|
||||
|
||||
extern crate serde;
|
||||
extern crate parity_wasm;
|
||||
extern crate wasmi;
|
||||
extern crate byteorder;
|
||||
extern crate rustc_hex;
|
||||
extern crate triehash;
|
||||
@@ -47,12 +47,6 @@ extern crate error_chain;
|
||||
#[cfg(test)]
|
||||
extern crate assert_matches;
|
||||
|
||||
// TODO: move into own crate
|
||||
macro_rules! map {
|
||||
($( $name:expr => $value:expr ),*) => (
|
||||
vec![ $( ( $name, $value ) ),* ].into_iter().collect()
|
||||
)
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
mod wasm_utils;
|
||||
|
||||
@@ -16,16 +16,14 @@
|
||||
|
||||
//! Rust implementation of Substrate contracts.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use parity_wasm::{deserialize_buffer, ModuleInstanceInterface, ProgramInstance};
|
||||
use parity_wasm::interpreter::{ItemIndex, DummyUserError};
|
||||
use parity_wasm::RuntimeValue::{I32, I64};
|
||||
use wasmi::{Module, ModuleInstance, MemoryInstance, MemoryRef, ImportsBuilder};
|
||||
use wasmi::RuntimeValue::{I32, I64};
|
||||
use wasmi::memory_units::{Pages, Bytes};
|
||||
use state_machine::{Externalities, CodeExecutor};
|
||||
use error::{Error, ErrorKind, Result};
|
||||
use wasm_utils::{MemoryInstance, UserDefinedElements,
|
||||
AddModuleWithoutFullDependentInstance};
|
||||
use wasm_utils::{DummyUserError};
|
||||
use primitives::{blake2_256, twox_128, twox_256};
|
||||
use primitives::hexdisplay::HexDisplay;
|
||||
use triehash::ordered_trie_root;
|
||||
@@ -35,22 +33,21 @@ struct Heap {
|
||||
}
|
||||
|
||||
impl Heap {
|
||||
fn new(memory: &MemoryInstance) -> Result<Self> {
|
||||
const HEAP_SIZE_IN_PAGES: u32 = 8;
|
||||
const PAGE_SIZE_IN_BYTES: u32 = 65536;
|
||||
/// Construct new `Heap` struct.
|
||||
///
|
||||
/// Returns `Err` if the heap couldn't allocate required
|
||||
/// number of pages.
|
||||
///
|
||||
/// This could mean that wasm binary specifies memory
|
||||
/// limit and we are trying to allocate beyond that limit.
|
||||
fn new(memory: &MemoryRef) -> Result<Self> {
|
||||
const HEAP_SIZE_IN_PAGES: usize = 8;
|
||||
|
||||
let prev_page_count = memory.grow(HEAP_SIZE_IN_PAGES).map_err(
|
||||
|_: ::parity_wasm::interpreter::Error<DummyUserError>| Error::from(ErrorKind::Runtime),
|
||||
)?;
|
||||
if prev_page_count == 0xFFFFFFFF {
|
||||
// Wasm vm refuses to mount the specified amount of new pages. This
|
||||
// could mean that wasm binary specifies memory limit and we are trying
|
||||
// to allocate beyond that limit.
|
||||
return Err(ErrorKind::Runtime.into());
|
||||
}
|
||||
let allocated_area_start = prev_page_count * PAGE_SIZE_IN_BYTES;
|
||||
let prev_page_count = memory
|
||||
.grow(Pages(HEAP_SIZE_IN_PAGES))
|
||||
.map_err(|_| Error::from(ErrorKind::Runtime))?;
|
||||
Ok(Heap {
|
||||
end: allocated_area_start as u32,
|
||||
end: Bytes::from(prev_page_count).0 as u32,
|
||||
})
|
||||
}
|
||||
fn allocate(&mut self, size: u32) -> u32 {
|
||||
@@ -64,16 +61,16 @@ impl Heap {
|
||||
|
||||
struct FunctionExecutor<'e, E: Externalities + 'e> {
|
||||
heap: Heap,
|
||||
memory: Arc<MemoryInstance>,
|
||||
memory: MemoryRef,
|
||||
ext: &'e mut E,
|
||||
hash_lookup: HashMap<Vec<u8>, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl<'e, E: Externalities> FunctionExecutor<'e, E> {
|
||||
fn new(m: &Arc<MemoryInstance>, e: &'e mut E) -> Result<Self> {
|
||||
fn new(m: MemoryRef, e: &'e mut E) -> Result<Self> {
|
||||
Ok(FunctionExecutor {
|
||||
heap: Heap::new(&*m)?,
|
||||
memory: Arc::clone(m),
|
||||
heap: Heap::new(&m)?,
|
||||
memory: m,
|
||||
ext: e,
|
||||
hash_lookup: HashMap::new(),
|
||||
})
|
||||
@@ -129,50 +126,54 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
println!("{}", message);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
ext_print_hex(data: *const u8, len: u32) => {
|
||||
if let Ok(hex) = this.memory.get(data, len as usize) {
|
||||
println!("{}", HexDisplay::from(&hex));
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
ext_print_num(number: u64) => {
|
||||
println!("{}", number);
|
||||
Ok(())
|
||||
},
|
||||
ext_memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 => {
|
||||
let sl1 = this.memory.get(s1, n as usize).map_err(|_| DummyUserError)?;
|
||||
let sl2 = this.memory.get(s2, n as usize).map_err(|_| DummyUserError)?;
|
||||
match sl1.cmp(&sl2) {
|
||||
Ok(match sl1.cmp(&sl2) {
|
||||
Ordering::Greater => 1,
|
||||
Ordering::Less => -1,
|
||||
Ordering::Equal => 0,
|
||||
}
|
||||
})
|
||||
},
|
||||
ext_memcpy(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => {
|
||||
this.memory.copy_nonoverlapping(src as usize, dest as usize, count as usize)
|
||||
.map_err(|_| DummyUserError)?;
|
||||
trace!(target: "runtime-io", "memcpy {} from {}, {} bytes", dest, src, count);
|
||||
dest
|
||||
Ok(dest)
|
||||
},
|
||||
ext_memmove(dest: *mut u8, src: *const u8, count: usize) -> *mut u8 => {
|
||||
this.memory.copy(src as usize, dest as usize, count as usize)
|
||||
.map_err(|_| DummyUserError)?;
|
||||
trace!(target: "runtime-io", "memmove {} from {}, {} bytes", dest, src, count);
|
||||
dest
|
||||
Ok(dest)
|
||||
},
|
||||
ext_memset(dest: *mut u8, val: u32, count: usize) -> *mut u8 => {
|
||||
this.memory.clear(dest as usize, val as u8, count as usize)
|
||||
.map_err(|_| DummyUserError)?;
|
||||
trace!(target: "runtime-io", "memset {} with {}, {} bytes", dest, val, count);
|
||||
dest
|
||||
Ok(dest)
|
||||
},
|
||||
ext_malloc(size: usize) -> *mut u8 => {
|
||||
let r = this.heap.allocate(size);
|
||||
trace!(target: "runtime-io", "malloc {} bytes at {}", size, r);
|
||||
r
|
||||
Ok(r)
|
||||
},
|
||||
ext_free(addr: *mut u8) => {
|
||||
this.heap.deallocate(addr);
|
||||
trace!(target: "runtime-io", "free {}", addr)
|
||||
trace!(target: "runtime-io", "free {}", addr);
|
||||
Ok(())
|
||||
},
|
||||
ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32) => {
|
||||
let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?;
|
||||
@@ -183,6 +184,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
info!(target: "wasm-trace", "*** Setting storage: {} -> {} [k={}]", ascii_format(&key), HexDisplay::from(&value), HexDisplay::from(&key));
|
||||
}
|
||||
this.ext.set_storage(key, value);
|
||||
Ok(())
|
||||
},
|
||||
ext_clear_storage(key_data: *const u8, key_len: u32) => {
|
||||
let key = this.memory.get(key_data, key_len as usize).map_err(|_| DummyUserError)?;
|
||||
@@ -192,6 +194,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
info!(target: "wasm-trace", "*** Clearing storage: {} [k={}]", ascii_format(&key), HexDisplay::from(&key));
|
||||
}
|
||||
this.ext.clear_storage(&key);
|
||||
Ok(())
|
||||
},
|
||||
// return 0 and place u32::max_value() into written_out if no value exists for the key.
|
||||
ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => {
|
||||
@@ -208,10 +211,10 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
let offset = this.heap.allocate(value.len() as u32) as u32;
|
||||
this.memory.set(offset, &value).map_err(|_| DummyUserError)?;
|
||||
this.memory.write_primitive(written_out, value.len() as u32)?;
|
||||
offset
|
||||
Ok(offset)
|
||||
} else {
|
||||
this.memory.write_primitive(written_out, u32::max_value())?;
|
||||
0
|
||||
Ok(0)
|
||||
}
|
||||
},
|
||||
// return u32::max_value() if no value exists for the key.
|
||||
@@ -227,14 +230,15 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
let value = &value[value_offset as usize..];
|
||||
let written = ::std::cmp::min(value_len as usize, value.len());
|
||||
this.memory.set(value_data, &value[..written]).map_err(|_| DummyUserError)?;
|
||||
written as u32
|
||||
Ok(written as u32)
|
||||
} else {
|
||||
u32::max_value()
|
||||
Ok(u32::max_value())
|
||||
}
|
||||
},
|
||||
ext_storage_root(result: *mut u8) => {
|
||||
let r = this.ext.storage_root();
|
||||
this.memory.set(result, &r[..]).map_err(|_| DummyUserError)?;
|
||||
Ok(())
|
||||
},
|
||||
ext_enumerated_trie_root(values_data: *const u8, lens_data: *const u32, lens_len: u32, result: *mut u8) => {
|
||||
let values = (0..lens_len)
|
||||
@@ -249,9 +253,10 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
.collect::<::std::result::Result<Vec<_>, DummyUserError>>()?;
|
||||
let r = ordered_trie_root(values.into_iter());
|
||||
this.memory.set(result, &r[..]).map_err(|_| DummyUserError)?;
|
||||
Ok(())
|
||||
},
|
||||
ext_chain_id() -> u64 => {
|
||||
this.ext.chain_id()
|
||||
Ok(this.ext.chain_id())
|
||||
},
|
||||
ext_twox_128(data: *const u8, len: u32, out: *mut u8) => {
|
||||
let result = if len == 0 {
|
||||
@@ -272,6 +277,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
};
|
||||
|
||||
this.memory.set(out, &result).map_err(|_| DummyUserError)?;
|
||||
Ok(())
|
||||
},
|
||||
ext_twox_256(data: *const u8, len: u32, out: *mut u8) => {
|
||||
let result = if len == 0 {
|
||||
@@ -280,6 +286,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
twox_256(&this.memory.get(data, len as usize).map_err(|_| DummyUserError)?)
|
||||
};
|
||||
this.memory.set(out, &result).map_err(|_| DummyUserError)?;
|
||||
Ok(())
|
||||
},
|
||||
ext_blake2_256(data: *const u8, len: u32, out: *mut u8) => {
|
||||
let result = if len == 0 {
|
||||
@@ -288,6 +295,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
blake2_256(&this.memory.get(data, len as usize).map_err(|_| DummyUserError)?)
|
||||
};
|
||||
this.memory.set(out, &result).map_err(|_| DummyUserError)?;
|
||||
Ok(())
|
||||
},
|
||||
ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32 => {
|
||||
let mut sig = [0u8; 64];
|
||||
@@ -296,12 +304,12 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
this.memory.get_into(pubkey_data, &mut pubkey[..]).map_err(|_| DummyUserError)?;
|
||||
let msg = this.memory.get(msg_data, msg_len as usize).map_err(|_| DummyUserError)?;
|
||||
|
||||
if ::ed25519::verify(&sig, &msg, &pubkey) {
|
||||
Ok(if ::ed25519::verify(&sig, &msg, &pubkey) {
|
||||
0
|
||||
} else {
|
||||
5
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
=> <'e, E: Externalities + 'e>
|
||||
);
|
||||
|
||||
@@ -324,27 +332,40 @@ impl CodeExecutor for WasmExecutor {
|
||||
// TODO: handle all expects as errors to be returned.
|
||||
println!("Wasm-Calling {}({})", method, HexDisplay::from(&data));
|
||||
|
||||
let program = ProgramInstance::new().expect("this really shouldn't be able to fail; qed");
|
||||
let module = Module::from_buffer(code).expect("all modules compiled with rustc are valid wasm code; qed");
|
||||
|
||||
let module = deserialize_buffer(code.to_vec()).expect("all modules compiled with rustc are valid wasm code; qed");
|
||||
let module = program.add_module_by_sigs("test", module, map!["env" => FunctionExecutor::<E>::SIGNATURES]).expect("runtime signatures always provided; qed");
|
||||
// start module instantiation. Don't run 'start' function yet.
|
||||
let intermediate_instance = ModuleInstance::new(
|
||||
&module,
|
||||
&ImportsBuilder::new()
|
||||
.with_resolver("env", FunctionExecutor::<E>::resolver())
|
||||
)?;
|
||||
|
||||
let memory = module.memory(ItemIndex::Internal(0)).expect("all modules compiled with rustc include memory segments; qed");
|
||||
let mut fec = FunctionExecutor::new(&memory, ext)?;
|
||||
// extract a reference to a linear memory and initialize FunctionExecutor.
|
||||
let memory = intermediate_instance
|
||||
.not_started_instance()
|
||||
.export_by_name("memory")
|
||||
.expect("all modules compiled with rustc should have an export named 'memory'; qed")
|
||||
.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)?;
|
||||
|
||||
// finish instantiation by running 'start' function (if any).
|
||||
let instance = intermediate_instance.run_start(&mut fec)?;
|
||||
|
||||
let size = data.len() as u32;
|
||||
let offset = fec.heap.allocate(size);
|
||||
memory.set(offset, &data).expect("heap always gives a sensible offset to write");
|
||||
|
||||
let returned = program
|
||||
.params_with_external("env", &mut fec)
|
||||
.map(|p| p
|
||||
.add_argument(I32(offset as i32))
|
||||
.add_argument(I32(size as i32)))
|
||||
.and_then(|p| module.execute_export(method, p))
|
||||
.map_err(|_| -> Error {
|
||||
ErrorKind::Runtime.into()
|
||||
})?;
|
||||
let returned = instance.invoke_export(
|
||||
method,
|
||||
&[
|
||||
I32(offset as i32),
|
||||
I32(size as i32)
|
||||
],
|
||||
&mut fec
|
||||
)?;
|
||||
|
||||
if let Some(I64(r)) = returned {
|
||||
let offset = r as u32;
|
||||
@@ -365,6 +386,13 @@ mod tests {
|
||||
use codec::Slicable;
|
||||
use state_machine::TestExternalities;
|
||||
|
||||
// TODO: move into own crate.
|
||||
macro_rules! map {
|
||||
($( $name:expr => $value:expr ),*) => (
|
||||
vec![ $( ( $name, $value ) ),* ].into_iter().collect()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returning_should_work() {
|
||||
let mut ext = TestExternalities::default();
|
||||
|
||||
@@ -16,18 +16,18 @@
|
||||
|
||||
//! Rust implementation of Substrate contracts.
|
||||
|
||||
use std::sync::{Arc};
|
||||
use std::collections::HashMap;
|
||||
pub use std::result;
|
||||
pub use parity_wasm::builder;
|
||||
pub use parity_wasm::elements::{ValueType, Module};
|
||||
pub use parity_wasm::interpreter::{RuntimeValue, UserFunctionDescriptor, UserFunctionExecutor,
|
||||
UserDefinedElements, env_native_module, DummyUserError, ExecutionParams, UserError};
|
||||
use parity_wasm::interpreter;
|
||||
use wasmi::{ValueType, RuntimeValue, HostError};
|
||||
use std::fmt;
|
||||
|
||||
pub type Error = interpreter::Error<DummyUserError>;
|
||||
pub type MemoryInstance = interpreter::MemoryInstance<DummyUserError>;
|
||||
pub type CallerContext<'a> = interpreter::CallerContext<'a, DummyUserError>;
|
||||
#[derive(Debug)]
|
||||
pub struct DummyUserError;
|
||||
impl fmt::Display for DummyUserError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "DummyUserError")
|
||||
}
|
||||
}
|
||||
impl HostError for DummyUserError {
|
||||
}
|
||||
|
||||
pub trait ConvertibleToWasm { const VALUE_TYPE: ValueType; type NativeType; fn to_runtime_value(self) -> RuntimeValue; }
|
||||
impl ConvertibleToWasm for i32 { type NativeType = i32; const VALUE_TYPE: ValueType = ValueType::I32; fn to_runtime_value(self) -> RuntimeValue { RuntimeValue::I32(self) } }
|
||||
@@ -48,153 +48,151 @@ macro_rules! convert_args {
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! convert_fn {
|
||||
( $name:ident ( $( $params:ty ),* ) ) => ( $crate::wasm_utils::UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), None) );
|
||||
( $name:ident ( $( $params:ty ),* ) -> $returns:ty ) => ( $crate::wasm_utils::UserFunctionDescriptor::Static(stringify!($name), &convert_args!($($params),*), Some({ use $crate::wasm_utils::ConvertibleToWasm; <$returns>::VALUE_TYPE }) ) );
|
||||
macro_rules! gen_signature {
|
||||
( ( $( $params: ty ),* ) ) => (
|
||||
{
|
||||
$crate::wasmi::Signature::new(&convert_args!($($params),*)[..], None)
|
||||
}
|
||||
);
|
||||
|
||||
( ( $( $params: ty ),* ) -> $returns: ty ) => (
|
||||
{
|
||||
$crate::wasmi::Signature::new(&convert_args!($($params),*)[..], Some({
|
||||
use $crate::wasm_utils::ConvertibleToWasm; <$returns>::VALUE_TYPE
|
||||
}))
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! resolve_fn {
|
||||
(@iter $index:expr, $sig_var:ident, $name_var:ident) => ();
|
||||
(@iter $index:expr, $sig_var:ident, $name_var:ident $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* => $($tail:tt)* ) => (
|
||||
if $name_var == stringify!($name) {
|
||||
let signature = gen_signature!( ( $( $params ),* ) $( -> $returns )* );
|
||||
if $sig_var != &signature {
|
||||
return Err($crate::wasmi::Error::Instantiation(
|
||||
format!("Export {} has different signature {:?}", $name_var, $sig_var),
|
||||
));
|
||||
}
|
||||
return Ok($crate::wasmi::FuncInstance::alloc_host(signature, $index));
|
||||
}
|
||||
resolve_fn!(@iter $index + 1, $sig_var, $name_var $($tail)*)
|
||||
);
|
||||
|
||||
($sig_var:ident, $name_var:ident, $($tail:tt)* ) => (
|
||||
resolve_fn!(@iter 0, $sig_var, $name_var $($tail)*);
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! reverse_params {
|
||||
// Entry point, use brackets to recursively reverse above.
|
||||
($body:tt, $self:ident, $context:ident, $( $names:ident : $params:ty ),*) => (
|
||||
reverse_params!($body $self $context [ $( $names : $params ),* ]);
|
||||
);
|
||||
($body:tt $self:ident $context:ident [] $( $names:ident : $params:ty ),*) => ({
|
||||
macro_rules! unmarshall_args {
|
||||
( $body:tt, $objectname:ident, $args_iter:ident, $( $names:ident : $params:ty ),*) => ({
|
||||
$(
|
||||
let $names : <$params as $crate::wasm_utils::ConvertibleToWasm>::NativeType = match $context.value_stack.pop_as() {
|
||||
Ok(value) => value,
|
||||
Err(error) => return Err(error.into()),
|
||||
};
|
||||
let $names : <$params as $crate::wasm_utils::ConvertibleToWasm>::NativeType =
|
||||
$args_iter.next()
|
||||
.and_then(|rt_val| rt_val.try_into())
|
||||
.expect(
|
||||
"`$args_iter` comes from an argument of Externals::invoke_index;
|
||||
args to an external call always matches the signature of the external;
|
||||
external signatures are built with count and types and in order defined by `$params`;
|
||||
here, we iterating on `$params`;
|
||||
qed;
|
||||
"
|
||||
);
|
||||
)*
|
||||
$body
|
||||
});
|
||||
($body:tt $self:ident $context:ident [ $name:ident : $param:ty $(, $names:ident : $params:ty )* ] $( $reversed_names:ident : $reversed_params:ty ),*) => (
|
||||
reverse_params!($body $self $context [ $( $names : $params ),* ] $name : $param $( , $reversed_names : $reversed_params )*);
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
/// Since we can't specify the type of closure directly at binding site:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// let f: FnOnce() -> Result<<u32 as ConvertibleToWasm>::NativeType, _> = || { /* ... */ };
|
||||
/// ```
|
||||
///
|
||||
/// we use this function to constrain the type of the closure.
|
||||
#[inline(always)]
|
||||
pub fn constrain_closure<R, F>(f: F) -> F
|
||||
where
|
||||
F: FnOnce() -> Result<R, ::wasmi::Trap>
|
||||
{
|
||||
f
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! marshall {
|
||||
( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({
|
||||
reverse_params!($body, $self, $context, $( $names : $params ),*);
|
||||
Ok(None)
|
||||
( $args_iter:ident, $objectname:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({
|
||||
let body = $crate::wasm_utils::constrain_closure::<
|
||||
<$returns as $crate::wasm_utils::ConvertibleToWasm>::NativeType, _
|
||||
>(|| {
|
||||
unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*)
|
||||
});
|
||||
let r = body()?;
|
||||
return Ok(Some({ use $crate::wasm_utils::ConvertibleToWasm; r.to_runtime_value() }))
|
||||
});
|
||||
( $context:ident, $self:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({
|
||||
let r : <$returns as $crate::wasm_utils::ConvertibleToWasm>::NativeType = reverse_params!($body, $self, $context, $( $names : $params ),*);
|
||||
Ok(Some({ use $crate::wasm_utils::ConvertibleToWasm; r.to_runtime_value() }))
|
||||
( $args_iter:ident, $objectname:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({
|
||||
let body = $crate::wasm_utils::constrain_closure::<(), _>(|| {
|
||||
unmarshall_args!($body, $objectname, $args_iter, $( $names : $params ),*)
|
||||
});
|
||||
body()?;
|
||||
return Ok(None)
|
||||
})
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! dispatch {
|
||||
( $objectname:ident, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* ) => (
|
||||
fn execute(&mut self, name: &str, context: $crate::wasm_utils::CallerContext)
|
||||
-> $crate::wasm_utils::result::Result<Option<$crate::wasm_utils::RuntimeValue>, $crate::wasm_utils::Error> {
|
||||
let $objectname = self;
|
||||
match name {
|
||||
$(
|
||||
stringify!($name) => marshall!(context, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body),
|
||||
)*
|
||||
_ => panic!()
|
||||
}
|
||||
macro_rules! dispatch_fn {
|
||||
( @iter $index:expr, $index_ident:ident, $objectname:ident, $args_iter:ident) => {
|
||||
// `$index` comes from an argument of Externals::invoke_index;
|
||||
// externals are always invoked with index given by resolve_fn! at resolve time;
|
||||
// For each next function resolve_fn! gives new index, starting from 0;
|
||||
// Both dispatch_fn! and resolve_fn! are called with the same list of functions;
|
||||
// qed;
|
||||
panic!("fn with index {} is undefined", $index);
|
||||
};
|
||||
|
||||
( @iter $index:expr, $index_ident:ident, $objectname:ident, $args_iter:ident, $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt $($tail:tt)*) => (
|
||||
if $index_ident == $index {
|
||||
{ marshall!($args_iter, $objectname, ( $( $names : $params ),* ) $( -> $returns )* => $body) }
|
||||
}
|
||||
dispatch_fn!( @iter $index + 1, $index_ident, $objectname, $args_iter $($tail)*)
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! signatures {
|
||||
( $( $name:ident ( $( $params:ty ),* ) $( -> $returns:ty )* ),* ) => (
|
||||
const SIGNATURES: &'static [$crate::wasm_utils::UserFunctionDescriptor] = &[
|
||||
$(
|
||||
convert_fn!( $name ( $( $params ),* ) $( -> $returns )* ),
|
||||
)*
|
||||
];
|
||||
( $index_ident:ident, $objectname:ident, $args_iter:ident, $($tail:tt)* ) => (
|
||||
dispatch_fn!( @iter 0, $index_ident, $objectname, $args_iter, $($tail)*);
|
||||
);
|
||||
}
|
||||
|
||||
pub trait IntoUserDefinedElements {
|
||||
fn into_user_defined_elements(&mut self) -> UserDefinedElements<DummyUserError>;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_function_executor {
|
||||
( $objectname:ident : $structname:ty, $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt ),* => $($pre:tt)+ ) => (
|
||||
impl $($pre)+ $crate::wasm_utils::UserFunctionExecutor<$crate::wasm_utils::DummyUserError> for $structname {
|
||||
dispatch!($objectname, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*);
|
||||
}
|
||||
impl $($pre)+ $structname {
|
||||
signatures!($( $name( $( $params ),* ) $( -> $returns )* ),*);
|
||||
}
|
||||
impl $($pre)+ $crate::wasm_utils::IntoUserDefinedElements for $structname {
|
||||
fn into_user_defined_elements(&mut self) -> UserDefinedElements<$crate::wasm_utils::DummyUserError> {
|
||||
$crate::wasm_utils::UserDefinedElements {
|
||||
executor: Some(self),
|
||||
globals: HashMap::new(), // TODO: provide
|
||||
functions: ::std::borrow::Cow::from(Self::SIGNATURES),
|
||||
( $objectname:ident : $structname:ty,
|
||||
$( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt , )*
|
||||
=> $($pre:tt)+ ) => (
|
||||
impl $( $pre ) + $structname {
|
||||
#[allow(unused)]
|
||||
fn resolver() -> &'static $crate::wasmi::ModuleImportResolver {
|
||||
struct Resolver;
|
||||
impl $crate::wasmi::ModuleImportResolver for Resolver {
|
||||
fn resolve_func(&self, name: &str, signature: &$crate::wasmi::Signature) -> ::std::result::Result<$crate::wasmi::FuncRef, $crate::wasmi::Error> {
|
||||
resolve_fn!(signature, name, $( $name( $( $params ),* ) $( -> $returns )* => )*);
|
||||
|
||||
Err($crate::wasmi::Error::Instantiation(
|
||||
format!("Export {} not found", name),
|
||||
))
|
||||
}
|
||||
}
|
||||
&Resolver
|
||||
}
|
||||
}
|
||||
|
||||
impl $( $pre ) + $crate::wasmi::Externals for $structname {
|
||||
fn invoke_index(
|
||||
&mut self,
|
||||
index: usize,
|
||||
args: $crate::wasmi::RuntimeArgs,
|
||||
) -> ::std::result::Result<Option<$crate::wasmi::RuntimeValue>, $crate::wasmi::Trap> {
|
||||
let $objectname = self;
|
||||
let mut args = args.as_ref().iter();
|
||||
dispatch_fn!(index, $objectname, args, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct DummyUserFunctionExecutor;
|
||||
impl<E: UserError> interpreter::UserFunctionExecutor<E> for DummyUserFunctionExecutor {
|
||||
fn execute(&mut self, _name: &str, _context: interpreter::CallerContext<E>) ->
|
||||
result::Result<Option<interpreter::RuntimeValue>, interpreter::Error<E>>
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AddModuleWithoutFullDependentInstance {
|
||||
fn add_module_by_sigs(
|
||||
&self,
|
||||
name: &str,
|
||||
module: Module,
|
||||
functions: HashMap<&str, &'static [UserFunctionDescriptor]>,
|
||||
) -> result::Result<Arc<interpreter::ModuleInstance<DummyUserError>>, interpreter::Error<DummyUserError>>;
|
||||
|
||||
fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> result::Result<ExecutionParams<'a, DummyUserError>, Error>;
|
||||
}
|
||||
|
||||
impl AddModuleWithoutFullDependentInstance for interpreter::ProgramInstance<DummyUserError> {
|
||||
fn add_module_by_sigs(
|
||||
&self,
|
||||
name: &str,
|
||||
module: Module,
|
||||
functions: HashMap<&str, &'static [UserFunctionDescriptor]>
|
||||
) -> result::Result<Arc<interpreter::ModuleInstance<DummyUserError>>, interpreter::Error<DummyUserError>> {
|
||||
let mut dufe = vec![DummyUserFunctionExecutor; functions.len()];
|
||||
let dufe_refs = dufe.iter_mut().collect::<Vec<_>>();
|
||||
let fake_module_map = functions.into_iter()
|
||||
.zip(dufe_refs.into_iter())
|
||||
.map(|((dep_mod_name, functions), dufe)| -> result::Result<_, interpreter::Error<DummyUserError>> {
|
||||
let fake_module = Arc::new(
|
||||
interpreter::env_native_module(
|
||||
self.module(dep_mod_name).ok_or(DummyUserError)?, UserDefinedElements {
|
||||
executor: Some(dufe),
|
||||
globals: HashMap::new(),
|
||||
functions: ::std::borrow::Cow::from(functions),
|
||||
}
|
||||
)?
|
||||
);
|
||||
let fake_module: Arc<interpreter::ModuleInstanceInterface<_>> = fake_module;
|
||||
Ok((dep_mod_name.into(), fake_module))
|
||||
})
|
||||
.collect::<result::Result<HashMap<_, _>, interpreter::Error<DummyUserError>>>()?;
|
||||
self.add_module(name, module, Some(&fake_module_map))
|
||||
}
|
||||
|
||||
fn params_with_external<'a, 'b: 'a>(&'b self, externals_name: &str, externals: &'a mut IntoUserDefinedElements) -> result::Result<ExecutionParams<'a, DummyUserError>, Error> {
|
||||
Ok(interpreter::ExecutionParams::with_external(
|
||||
externals_name.into(),
|
||||
Arc::new(
|
||||
interpreter::env_native_module(
|
||||
self.module(externals_name).ok_or(DummyUserError)?,
|
||||
externals.into_user_defined_elements()
|
||||
)?
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user