diff --git a/substrate/core/executor/runtime-test/src/lib.rs b/substrate/core/executor/runtime-test/src/lib.rs index 35f3191ffd..61eca8dd4e 100644 --- a/substrate/core/executor/runtime-test/src/lib.rs +++ b/substrate/core/executor/runtime-test/src/lib.rs @@ -5,48 +5,23 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); -use rstd::{vec::Vec, slice, vec}; +#[cfg(not(feature = "std"))] +use rstd::{vec::Vec, vec}; +#[cfg(not(feature = "std"))] use runtime_io::{ set_storage, storage, clear_prefix, blake2_128, blake2_256, twox_128, twox_256, ed25519_verify, sr25519_verify, }; +#[cfg(not(feature = "std"))] use sr_primitives::{print, traits::{BlakeTwo256, Hash}}; +#[cfg(not(feature = "std"))] use primitives::{ed25519, sr25519}; -macro_rules! impl_stubs { - ( $( $new_name:ident => $invoke:expr, )* ) => { - $( - impl_stubs!(@METHOD $new_name => $invoke); - )* - }; - ( @METHOD $new_name:ident => $invoke:expr ) => { - #[no_mangle] - pub fn $new_name(input_data: *mut u8, input_len: usize) -> u64 { - let input: &[u8] = if input_len == 0 { - &[0u8; 0] - } else { - unsafe { - slice::from_raw_parts(input_data, input_len) - } - }; - - let output: Vec = $invoke(input); - let res = output.as_ptr() as u64 + ((output.len() as u64) << 32); - - // Leak the output vector to avoid it being freed. - // This is fine in a WASM context since the heap - // will be discarded after the call. - rstd::mem::forget(output); - res - } - }; -} - -impl_stubs!( - test_data_in => |input| { +primitives::wasm_export_functions! { + fn test_data_in(input: Vec) -> Vec { print("set_storage"); - set_storage(b"input", input); + set_storage(b"input", &input); print("storage"); let foo = storage(b"foo").unwrap(); @@ -56,25 +31,44 @@ impl_stubs!( print("finished!"); b"all ok!".to_vec() - }, - test_clear_prefix => |input| { - clear_prefix(input); + } + + fn test_clear_prefix(input: Vec) -> Vec { + clear_prefix(&input); b"all ok!".to_vec() - }, - test_empty_return => |_| Vec::new(), - test_exhaust_heap => |_| Vec::with_capacity(16777216), - test_panic => |_| panic!("test panic"), - test_conditional_panic => |input: &[u8]| { + } + + fn test_empty_return() {} + + fn test_exhaust_heap() -> Vec { Vec::with_capacity(16777216) } + + fn test_panic() { panic!("test panic") } + + fn test_conditional_panic(input: Vec) -> Vec { if input.len() > 0 { panic!("test panic") } - input.to_vec() - }, - test_blake2_256 => |input| blake2_256(input).to_vec(), - test_blake2_128 => |input| blake2_128(input).to_vec(), - test_twox_256 => |input| twox_256(input).to_vec(), - test_twox_128 => |input| twox_128(input).to_vec(), - test_ed25519_verify => |input: &[u8]| { + + input + } + + fn test_blake2_256(input: Vec) -> Vec { + blake2_256(&input).to_vec() + } + + fn test_blake2_128(input: Vec) -> Vec { + blake2_128(&input).to_vec() + } + + fn test_twox_256(input: Vec) -> Vec { + twox_256(&input).to_vec() + } + + fn test_twox_128(input: Vec) -> Vec { + twox_128(&input).to_vec() + } + + fn test_ed25519_verify(input: Vec) -> bool { let mut pubkey = [0; 32]; let mut sig = [0; 64]; @@ -82,9 +76,10 @@ impl_stubs!( sig.copy_from_slice(&input[32..96]); let msg = b"all ok!"; - [ed25519_verify(&ed25519::Signature(sig), &msg[..], &ed25519::Public(pubkey)) as u8].to_vec() - }, - test_sr25519_verify => |input: &[u8]| { + ed25519_verify(&ed25519::Signature(sig), &msg[..], &ed25519::Public(pubkey)) + } + + fn test_sr25519_verify(input: Vec) -> bool { let mut pubkey = [0; 32]; let mut sig = [0; 64]; @@ -92,9 +87,10 @@ impl_stubs!( sig.copy_from_slice(&input[32..96]); let msg = b"all ok!"; - [sr25519_verify(&sr25519::Signature(sig), &msg[..], &sr25519::Public(pubkey)) as u8].to_vec() - }, - test_ordered_trie_root => |_| { + sr25519_verify(&sr25519::Signature(sig), &msg[..], &sr25519::Public(pubkey)) + } + + fn test_ordered_trie_root() -> Vec { BlakeTwo256::ordered_trie_root( vec![ b"zero"[..].into(), @@ -102,24 +98,25 @@ impl_stubs!( b"two"[..].into(), ], ).as_ref().to_vec() - }, - test_sandbox => |code: &[u8]| { - let ok = execute_sandboxed(code, &[]).is_ok(); - [ok as u8].to_vec() - }, - test_sandbox_args => |code: &[u8]| { - let ok = execute_sandboxed( - code, + } + + fn test_sandbox(code: Vec) -> bool { + execute_sandboxed(&code, &[]).is_ok() + } + + fn test_sandbox_args(code: Vec) -> bool { + execute_sandboxed( + &code, &[ sandbox::TypedValue::I32(0x12345678), sandbox::TypedValue::I64(0x1234567887654321), - ] - ).is_ok(); - [ok as u8].to_vec() - }, - test_sandbox_return_val => |code: &[u8]| { + ], + ).is_ok() + } + + fn test_sandbox_return_val(code: Vec) -> bool { let ok = match execute_sandboxed( - code, + &code, &[ sandbox::TypedValue::I32(0x1336), ] @@ -127,41 +124,43 @@ impl_stubs!( Ok(sandbox::ReturnValue::Value(sandbox::TypedValue::I32(0x1337))) => true, _ => false, }; - [ok as u8].to_vec() - }, - test_sandbox_instantiate => |code: &[u8]| { + + ok + } + + fn test_sandbox_instantiate(code: Vec) -> u8 { let env_builder = sandbox::EnvironmentDefinitionBuilder::new(); - let code = match sandbox::Instance::new(code, &env_builder, &mut ()) { + let code = match sandbox::Instance::new(&code, &env_builder, &mut ()) { Ok(_) => 0, Err(sandbox::Error::Module) => 1, Err(sandbox::Error::Execution) => 2, Err(sandbox::Error::OutOfBounds) => 3, }; - [code].to_vec() - }, - test_offchain_local_storage => |_| { + + code + } + + fn test_offchain_local_storage() -> bool { let kind = primitives::offchain::StorageKind::PERSISTENT; assert_eq!(runtime_io::local_storage_get(kind, b"test"), None); runtime_io::local_storage_set(kind, b"test", b"asd"); assert_eq!(runtime_io::local_storage_get(kind, b"test"), Some(b"asd".to_vec())); let res = runtime_io::local_storage_compare_and_set(kind, b"test", Some(b"asd"), b""); - assert_eq!(res, true); assert_eq!(runtime_io::local_storage_get(kind, b"test"), Some(b"".to_vec())); + res + } - [0].to_vec() - }, - test_offchain_local_storage_with_none => |_| { + fn test_offchain_local_storage_with_none() { let kind = primitives::offchain::StorageKind::PERSISTENT; assert_eq!(runtime_io::local_storage_get(kind, b"test"), None); let res = runtime_io::local_storage_compare_and_set(kind, b"test", None, b"value"); assert_eq!(res, true); assert_eq!(runtime_io::local_storage_get(kind, b"test"), Some(b"value".to_vec())); + } - [0].to_vec() - }, - test_offchain_http => |_| { + fn test_offchain_http() -> bool { use primitives::offchain::HttpRequestStatus; let run = || -> Option<()> { let id = runtime_io::http_request_start("POST", "http://localhost:12345", &[]).ok()?; @@ -182,16 +181,23 @@ impl_stubs!( Some(()) }; - [if run().is_some() { 0 } else { 1 }].to_vec() - }, -); + run().is_some() + } + } -fn execute_sandboxed(code: &[u8], args: &[sandbox::TypedValue]) -> Result { +#[cfg(not(feature = "std"))] +fn execute_sandboxed( + code: &[u8], + args: &[sandbox::TypedValue], +) -> Result { struct State { counter: u32, } - fn env_assert(_e: &mut State, args: &[sandbox::TypedValue]) -> Result { + fn env_assert( + _e: &mut State, + args: &[sandbox::TypedValue], + ) -> Result { if args.len() != 1 { return Err(sandbox::HostError); } @@ -202,7 +208,10 @@ fn execute_sandboxed(code: &[u8], args: &[sandbox::TypedValue]) -> Result Result { + fn env_inc_counter( + e: &mut State, + args: &[sandbox::TypedValue], + ) -> Result { if args.len() != 1 { return Err(sandbox::HostError); } diff --git a/substrate/core/executor/src/sandbox.rs b/substrate/core/executor/src/sandbox.rs index 3a213ccdf0..c4122274a9 100644 --- a/substrate/core/executor/src/sandbox.rs +++ b/substrate/core/executor/src/sandbox.rs @@ -631,11 +631,11 @@ mod tests { call $assert ) ) - "#).unwrap(); + "#).unwrap().encode(); assert_eq!( call_wasm(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), - vec![1], + true.encode(), ); } @@ -673,7 +673,7 @@ mod tests { call $assert ) ) - "#).unwrap(); + "#).unwrap().encode(); let res = call_wasm(&mut ext, 8, &test_code[..], "test_exhaust_heap", &code); assert_eq!(res.is_err(), true); @@ -718,11 +718,11 @@ mod tests { call $assert ) ) - "#).unwrap(); + "#).unwrap().encode(); assert_eq!( call_wasm(&mut ext, 8, &test_code[..], "test_sandbox", &code).unwrap(), - vec![1], + true.encode(), ); } @@ -752,11 +752,11 @@ mod tests { ) ) ) - "#).unwrap(); + "#).unwrap().encode(); assert_eq!( call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_args", &code).unwrap(), - vec![1], + true.encode(), ); } @@ -774,11 +774,11 @@ mod tests { ) ) ) - "#).unwrap(); + "#).unwrap().encode(); assert_eq!( call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_return_val", &code).unwrap(), - vec![1], + true.encode(), ); } @@ -794,11 +794,11 @@ mod tests { (func (export "call") ) ) - "#).unwrap(); + "#).unwrap().encode(); assert_eq!( call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(), - vec![1], + 1u8.encode(), ); } @@ -808,11 +808,11 @@ mod tests { let test_code = WASM_BINARY; // Corrupted wasm file - let code = &[0, 0, 0, 0, 1, 0, 0, 0]; + let code = vec![0u8, 0, 0, 0, 1, 0, 0, 0].encode(); assert_eq!( - call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", code).unwrap(), - vec![1], + call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(), + 1u8.encode(), ); } @@ -831,11 +831,11 @@ mod tests { (start $start) ) - "#).unwrap(); + "#).unwrap().encode(); assert_eq!( call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(), - vec![0], + 0u8.encode(), ); } @@ -855,11 +855,11 @@ mod tests { (start $start) ) - "#).unwrap(); + "#).unwrap().encode(); assert_eq!( call_wasm(&mut ext, 8, &test_code[..], "test_sandbox_instantiate", &code).unwrap(), - vec![2], + 2u8.encode(), ); } } diff --git a/substrate/core/executor/src/wasmi_execution.rs b/substrate/core/executor/src/wasmi_execution.rs index 0719ff4d40..ba7738a993 100644 --- a/substrate/core/executor/src/wasmi_execution.rs +++ b/substrate/core/executor/src/wasmi_execution.rs @@ -662,6 +662,7 @@ mod tests { use runtime_test::WASM_BINARY; use substrate_offchain::testing; use trie::{TrieConfiguration, trie_types::Layout}; + use codec::{Encode, Decode}; type TestExternalities = CoreTestExternalities; @@ -694,10 +695,10 @@ mod tests { let output = call(&mut ext, 8, &test_code[..], "test_panic", &[]); assert!(output.is_err()); - let output = call(&mut ext, 8, &test_code[..], "test_conditional_panic", &[]); - assert_eq!(output.unwrap(), vec![0u8; 0]); + let output = call(&mut ext, 8, &test_code[..], "test_conditional_panic", &[0]); + assert_eq!(Decode::decode(&mut &output.unwrap()[..]), Ok(Vec::::new())); - let output = call(&mut ext, 8, &test_code[..], "test_conditional_panic", &[2]); + let output = call(&mut ext, 8, &test_code[..], "test_conditional_panic", &vec![2].encode()); assert!(output.is_err()); } @@ -707,9 +708,15 @@ mod tests { ext.set_storage(b"foo".to_vec(), b"bar".to_vec()); let test_code = WASM_BINARY; - let output = call(&mut ext, 8, &test_code[..], "test_data_in", b"Hello world").unwrap(); + let output = call( + &mut ext, + 8, + &test_code[..], + "test_data_in", + &b"Hello world".to_vec().encode(), + ).unwrap(); - assert_eq!(output, b"all ok!".to_vec()); + assert_eq!(output, b"all ok!".to_vec().encode()); let expected = TestExternalities::new((map![ b"input".to_vec() => b"Hello world".to_vec(), @@ -730,9 +737,15 @@ mod tests { let test_code = WASM_BINARY; // This will clear all entries which prefix is "ab". - let output = call(&mut ext, 8, &test_code[..], "test_clear_prefix", b"ab").unwrap(); + let output = call( + &mut ext, + 8, + &test_code[..], + "test_clear_prefix", + &b"ab".to_vec().encode(), + ).unwrap(); - assert_eq!(output, b"all ok!".to_vec()); + assert_eq!(output, b"all ok!".to_vec().encode()); let expected = TestExternalities::new((map![ b"aaa".to_vec() => b"1".to_vec(), @@ -747,12 +760,18 @@ mod tests { let mut ext = TestExternalities::default(); let test_code = WASM_BINARY; assert_eq!( - call(&mut ext, 8, &test_code[..], "test_blake2_256", &[]).unwrap(), - blake2_256(&b""[..]).encode() + call(&mut ext, 8, &test_code[..], "test_blake2_256", &[0]).unwrap(), + blake2_256(&b""[..]).to_vec().encode(), ); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_blake2_256", b"Hello world!").unwrap(), - blake2_256(&b"Hello world!"[..]).encode() + call( + &mut ext, + 8, + &test_code[..], + "test_blake2_256", + &b"Hello world!".to_vec().encode(), + ).unwrap(), + blake2_256(&b"Hello world!"[..]).to_vec().encode(), ); } @@ -761,12 +780,18 @@ mod tests { let mut ext = TestExternalities::default(); let test_code = WASM_BINARY; assert_eq!( - call(&mut ext, 8, &test_code[..], "test_blake2_128", &[]).unwrap(), - blake2_128(&b""[..]).encode() + call(&mut ext, 8, &test_code[..], "test_blake2_128", &[0]).unwrap(), + blake2_128(&b""[..]).to_vec().encode(), ); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_blake2_128", b"Hello world!").unwrap(), - blake2_128(&b"Hello world!"[..]).encode() + call( + &mut ext, + 8, + &test_code[..], + "test_blake2_128", + &b"Hello world!".to_vec().encode(), + ).unwrap(), + blake2_128(&b"Hello world!"[..]).to_vec().encode(), ); } @@ -775,12 +800,22 @@ mod tests { let mut ext = TestExternalities::default(); let test_code = WASM_BINARY; assert_eq!( - call(&mut ext, 8, &test_code[..], "test_twox_256", &[]).unwrap(), - hex!("99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a"), + call(&mut ext, 8, &test_code[..], "test_twox_256", &[0]).unwrap(), + hex!( + "99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a" + ).to_vec().encode(), ); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_twox_256", b"Hello world!").unwrap(), - hex!("b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74"), + call( + &mut ext, + 8, + &test_code[..], + "test_twox_256", + &b"Hello world!".to_vec().encode(), + ).unwrap(), + hex!( + "b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74" + ).to_vec().encode(), ); } @@ -789,12 +824,18 @@ mod tests { let mut ext = TestExternalities::default(); let test_code = WASM_BINARY; assert_eq!( - call(&mut ext, 8, &test_code[..], "test_twox_128", &[]).unwrap(), - hex!("99e9d85137db46ef4bbea33613baafd5") + call(&mut ext, 8, &test_code[..], "test_twox_128", &[0]).unwrap(), + hex!("99e9d85137db46ef4bbea33613baafd5").to_vec().encode(), ); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_twox_128", b"Hello world!").unwrap(), - hex!("b27dfd7f223f177f2a13647b533599af") + call( + &mut ext, + 8, + &test_code[..], + "test_twox_128", + &b"Hello world!".to_vec().encode(), + ).unwrap(), + hex!("b27dfd7f223f177f2a13647b533599af").to_vec().encode(), ); } @@ -809,8 +850,8 @@ mod tests { calldata.extend_from_slice(sig.as_ref()); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata).unwrap(), - vec![1] + call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata.encode()).unwrap(), + true.encode(), ); let other_sig = key.sign(b"all is not ok!"); @@ -819,8 +860,8 @@ mod tests { calldata.extend_from_slice(other_sig.as_ref()); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata).unwrap(), - vec![0] + call(&mut ext, 8, &test_code[..], "test_ed25519_verify", &calldata.encode()).unwrap(), + false.encode(), ); } @@ -835,8 +876,8 @@ mod tests { calldata.extend_from_slice(sig.as_ref()); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_sr25519_verify", &calldata).unwrap(), - vec![1] + call(&mut ext, 8, &test_code[..], "test_sr25519_verify", &calldata.encode()).unwrap(), + true.encode(), ); let other_sig = key.sign(b"all is not ok!"); @@ -845,8 +886,8 @@ mod tests { calldata.extend_from_slice(other_sig.as_ref()); assert_eq!( - call(&mut ext, 8, &test_code[..], "test_sr25519_verify", &calldata).unwrap(), - vec![0] + call(&mut ext, 8, &test_code[..], "test_sr25519_verify", &calldata.encode()).unwrap(), + false.encode(), ); } @@ -856,8 +897,8 @@ mod tests { let trie_input = vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()]; let test_code = WASM_BINARY; assert_eq!( - call(&mut ext, 8, &test_code[..], "test_ordered_trie_root", &[]).unwrap(), - Layout::::ordered_trie_root(trie_input.iter()).as_fixed_bytes().encode() + call(&mut ext, 8, &test_code[..], "test_ordered_trie_root", &[0]).unwrap(), + Layout::::ordered_trie_root(trie_input.iter()).as_bytes().encode(), ); } @@ -870,8 +911,8 @@ mod tests { ext.register_extension(OffchainExt::new(offchain)); let test_code = WASM_BINARY; assert_eq!( - call(&mut ext, 8, &test_code[..], "test_offchain_local_storage", &[]).unwrap(), - vec![0] + call(&mut ext, 8, &test_code[..], "test_offchain_local_storage", &[0]).unwrap(), + true.encode(), ); assert_eq!(state.read().persistent_storage.get(b"", b"test"), Some(vec![])); } @@ -897,8 +938,8 @@ mod tests { let test_code = WASM_BINARY; assert_eq!( - call(&mut ext, 8, &test_code[..], "test_offchain_http", &[]).unwrap(), - vec![0] + call(&mut ext, 8, &test_code[..], "test_offchain_http", &[0]).unwrap(), + true.encode(), ); } } diff --git a/substrate/core/primitives/src/lib.rs b/substrate/core/primitives/src/lib.rs index b1ce6e2f97..c2d23050f9 100644 --- a/substrate/core/primitives/src/lib.rs +++ b/substrate/core/primitives/src/lib.rs @@ -39,6 +39,7 @@ use std::borrow::Cow; use serde::{Serialize, Deserialize}; #[cfg(feature = "std")] pub use serde;// << for macro +#[doc(hidden)] pub use codec::{Encode, Decode};// << for macro #[cfg(feature = "std")] @@ -82,6 +83,9 @@ pub use self::hasher::blake2::Blake2Hasher; pub use primitives_storage as storage; +#[doc(hidden)] +pub use rstd; + /// Context for executing a call into the runtime. pub enum ExecutionContext { /// Context for general importing (including own blocks). diff --git a/substrate/core/primitives/src/testing.rs b/substrate/core/primitives/src/testing.rs index 7fdefd8fe0..92a09ff044 100644 --- a/substrate/core/primitives/src/testing.rs +++ b/substrate/core/primitives/src/testing.rs @@ -129,13 +129,127 @@ impl crate::traits::BareCryptoStore for KeyStore { } } +/// Macro for exporting functions from wasm in with the expected signature for using it with the +/// wasm executor. This is useful for tests where you need to call a function in wasm. +/// +/// The input parameters are expected to be SCALE encoded and will be automatically decoded for you. +/// The output value is also SCALE encoded when returned back to the host. +/// +/// The functions are feature-gated with `#[cfg(not(feature = "std"))]`, so they are only available +/// from within wasm. +/// +/// # Example +/// +/// ``` +/// # use substrate_primitives::wasm_export_functions; +/// +/// wasm_export_functions! { +/// fn test_in_wasm(value: bool, another_value: Vec) -> bool { +/// value && another_value.is_empty() +/// } +/// +/// fn without_return_value() { +/// // do something +/// } +/// } +/// ``` +#[macro_export] +macro_rules! wasm_export_functions { + ( + $( + fn $name:ident ( + $( $arg_name:ident: $arg_ty:ty ),* $(,)? + ) $( -> $ret_ty:ty )? { $( $fn_impl:tt )* } + )* + ) => { + $( + $crate::wasm_export_functions! { + @IMPL + fn $name ( + $( $arg_name: $arg_ty ),* + ) $( -> $ret_ty )? { $( $fn_impl )* } + } + )* + }; + (@IMPL + fn $name:ident ( + $( $arg_name:ident: $arg_ty:ty ),* + ) { $( $fn_impl:tt )* } + ) => { + #[no_mangle] + #[allow(unreachable_code)] + #[cfg(not(feature = "std"))] + pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { + let input: &[u8] = if input_len == 0 { + &[0u8; 0] + } else { + unsafe { + $crate::rstd::slice::from_raw_parts(input_data, input_len) + } + }; + + { + let ($( $arg_name ),*) : ($( $arg_ty ),*) = $crate::Decode::decode( + &mut &input[..], + ).expect("Input data is correctly encoded"); + + $( $fn_impl )* + } + + // We need to return *something* + let output = Vec::::new(); + let res = output.as_ptr() as u64 + ((output.len() as u64) << 32); + + // Leak the output vector to avoid it being freed. + // This is fine in a WASM context since the heap + // will be discarded after the call. + $crate::rstd::mem::forget(output); + res + } + }; + (@IMPL + fn $name:ident ( + $( $arg_name:ident: $arg_ty:ty ),* + ) $( -> $ret_ty:ty )? { $( $fn_impl:tt )* } + ) => { + #[no_mangle] + #[allow(unreachable_code)] + #[cfg(not(feature = "std"))] + pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { + let input: &[u8] = if input_len == 0 { + &[0u8; 0] + } else { + unsafe { + $crate::rstd::slice::from_raw_parts(input_data, input_len) + } + }; + + let output $( : $ret_ty )? = { + let ($( $arg_name ),*) : ($( $arg_ty ),*) = $crate::Decode::decode( + &mut &input[..], + ).expect("Input data is correctly encoded"); + + $( $fn_impl )* + }; + + let output = $crate::Encode::encode(&output); + let res = output.as_ptr() as u64 + ((output.len() as u64) << 32); + + // Leak the output vector to avoid it being freed. + // This is fine in a WASM context since the heap + // will be discarded after the call. + $crate::rstd::mem::forget(output); + res + } + }; +} + #[cfg(test)] mod tests { use super::*; use crate::sr25519; use crate::testing::{ED25519, SR25519}; - #[test] fn store_key_and_extract() { let store = KeyStore::new();