From e3ae647c42980ba2c188db0f19353fcfa517c09b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 14 Oct 2019 20:20:07 +0200 Subject: [PATCH] Provide macro for exporting functions from wasm (#3801) The macro generates the functions with the signature we expect for wasm functions. This macro is useful for tests where we need to call into wasm. Parameter passing is done by SCALE encoding the input and output parameters. --- .../core/executor/runtime-test/src/lib.rs | 185 +++++++++--------- substrate/core/executor/src/sandbox.rs | 36 ++-- .../core/executor/src/wasmi_execution.rs | 115 +++++++---- substrate/core/primitives/src/lib.rs | 4 + substrate/core/primitives/src/testing.rs | 116 ++++++++++- 5 files changed, 312 insertions(+), 144 deletions(-) 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();