referrerpolicy=no-referrer-when-downgrade

sp_runtime_interface_test_wasm/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Tests for the runtime interface traits and proc macros.
19
20#![cfg_attr(not(feature = "std"), no_std)]
21
22extern crate alloc;
23
24use sp_runtime_interface::{
25	pass_by::{
26		AllocateAndReturnByCodec, AllocateAndReturnFatPointer, AllocateAndReturnPointer, PassAs,
27		PassFatPointerAndDecode, PassFatPointerAndDecodeSlice, PassFatPointerAndRead,
28		PassFatPointerAndReadWrite, PassPointerAndRead, PassPointerAndReadCopy,
29		PassPointerAndWrite, ReturnAs,
30	},
31	runtime_interface,
32};
33
34#[cfg(not(feature = "std"))]
35use core::mem;
36
37use alloc::{vec, vec::Vec};
38use sp_core::{sr25519::Public, wasm_export_functions};
39
40// Include the WASM binary
41#[cfg(feature = "std")]
42include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
43
44/// Wasm binary unwrapped. If built with `SKIP_WASM_BUILD`, the function panics.
45#[cfg(feature = "std")]
46pub fn wasm_binary_unwrap() -> &'static [u8] {
47	WASM_BINARY.expect(
48		"Development wasm binary is not available. Testing is only \
49						supported with the flag disabled.",
50	)
51}
52
53/// Used in the `test_array_as_mutable_reference` test.
54const TEST_ARRAY: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
55
56#[runtime_interface]
57pub trait TestApi {
58	/// Returns the input data as result.
59	fn return_input(data: PassFatPointerAndRead<Vec<u8>>) -> AllocateAndReturnFatPointer<Vec<u8>> {
60		data
61	}
62
63	/// Returns 16kb data.
64	///
65	/// # Note
66	///
67	/// We return a `Vec<u32>` because this will use the code path that uses SCALE
68	/// to pass the data between native/wasm. (`Vec<u8>` is passed without encoding the
69	/// data)
70	fn return_16kb() -> AllocateAndReturnByCodec<Vec<u32>> {
71		vec![0; 4 * 1024]
72	}
73
74	fn return_option_vec() -> AllocateAndReturnByCodec<Option<Vec<u8>>> {
75		let mut vec = Vec::new();
76		vec.resize(16 * 1024, 0xAA);
77		Some(vec)
78	}
79
80	fn return_option_bytes() -> AllocateAndReturnByCodec<Option<bytes::Bytes>> {
81		let mut vec = Vec::new();
82		vec.resize(16 * 1024, 0xAA);
83		Some(vec.into())
84	}
85
86	/// Set the storage at key with value.
87	fn set_storage(
88		&mut self,
89		key: PassFatPointerAndRead<&[u8]>,
90		data: PassFatPointerAndRead<&[u8]>,
91	) {
92		self.place_storage(key.to_vec(), Some(data.to_vec()));
93	}
94
95	/// Copy `hello` into the given mutable reference
96	fn return_value_into_mutable_reference(&self, data: PassFatPointerAndReadWrite<&mut [u8]>) {
97		let res = "hello";
98		data[..res.len()].copy_from_slice(res.as_bytes());
99	}
100
101	/// Returns the input data wrapped in an `Option` as result.
102	fn return_option_input(
103		data: PassFatPointerAndRead<Vec<u8>>,
104	) -> AllocateAndReturnByCodec<Option<Vec<u8>>> {
105		Some(data)
106	}
107
108	/// Get an array as input and returns a subset of this array.
109	fn get_and_return_array(
110		data: PassPointerAndReadCopy<[u8; 34], 34>,
111	) -> AllocateAndReturnPointer<[u8; 16], 16> {
112		let mut res = [0u8; 16];
113		res.copy_from_slice(&data[..16]);
114		res
115	}
116
117	/// Take and fill mutable array.
118	fn array_as_mutable_reference(data: PassPointerAndWrite<&mut [u8; 16], 16>) {
119		data.copy_from_slice(&TEST_ARRAY);
120	}
121
122	/// Returns the given public key as result.
123	fn return_input_public_key(
124		key: PassPointerAndReadCopy<Public, 32>,
125	) -> AllocateAndReturnPointer<Public, 32> {
126		key
127	}
128
129	/// A function that is called with invalid utf8 data from the runtime.
130	///
131	/// This also checks that we accept `_` (wild card) argument names.
132	fn invalid_utf8_data(_: PassFatPointerAndRead<&str>) {}
133
134	/// Overwrite the native implementation in wasm. The native implementation always returns
135	/// `false` and the replacement function will return always `true`.
136	fn overwrite_native_function_implementation() -> bool {
137		false
138	}
139
140	fn test_versioning(&self, data: u32) -> bool {
141		data == 42 || data == 50
142	}
143
144	#[version(2)]
145	fn test_versioning(&self, data: u32) -> bool {
146		data == 42
147	}
148
149	fn test_versioning_register_only(&self, data: u32) -> bool {
150		data == 80
151	}
152
153	#[version(2, register_only)]
154	fn test_versioning_register_only(&self, data: u32) -> bool {
155		data == 42
156	}
157
158	/// Returns the input values as tuple.
159	fn return_input_as_tuple(
160		a: PassFatPointerAndRead<Vec<u8>>,
161		b: u32,
162		c: PassFatPointerAndDecode<Option<Vec<u32>>>,
163		d: u8,
164	) -> AllocateAndReturnByCodec<(Vec<u8>, u32, Option<Vec<u32>>, u8)> {
165		(a, b, c, d)
166	}
167
168	// Host functions for testing every marshaling strategy:
169
170	fn pass_pointer_and_read_copy(value: PassPointerAndReadCopy<[u8; 3], 3>) {
171		assert_eq!(value, [1, 2, 3]);
172	}
173
174	fn pass_pointer_and_read(value: PassPointerAndRead<&[u8; 3], 3>) {
175		assert_eq!(value, &[1, 2, 3]);
176	}
177
178	fn pass_fat_pointer_and_read(value: PassFatPointerAndRead<&[u8]>) {
179		assert_eq!(value, [1, 2, 3]);
180	}
181
182	fn pass_fat_pointer_and_read_write(value: PassFatPointerAndReadWrite<&mut [u8]>) {
183		assert_eq!(value, [1, 2, 3]);
184		value.copy_from_slice(&[4, 5, 6]);
185	}
186
187	fn pass_pointer_and_write(value: PassPointerAndWrite<&mut [u8; 3], 3>) {
188		assert_eq!(*value, [0, 0, 0]);
189		*value = [1, 2, 3];
190	}
191
192	fn pass_by_codec(value: PassFatPointerAndDecode<Vec<u16>>) {
193		assert_eq!(value, [1, 2, 3]);
194	}
195
196	fn pass_slice_ref_by_codec(value: PassFatPointerAndDecodeSlice<&[u16]>) {
197		assert_eq!(value, [1, 2, 3]);
198	}
199
200	fn pass_as(value: PassAs<Opaque, u32>) {
201		assert_eq!(value.0, 123);
202	}
203
204	fn return_as() -> ReturnAs<Opaque, u32> {
205		Opaque(123)
206	}
207
208	fn allocate_and_return_pointer() -> AllocateAndReturnPointer<[u8; 3], 3> {
209		[1, 2, 3]
210	}
211
212	fn allocate_and_return_fat_pointer() -> AllocateAndReturnFatPointer<Vec<u8>> {
213		vec![1, 2, 3]
214	}
215
216	fn allocate_and_return_by_codec() -> AllocateAndReturnByCodec<Vec<u16>> {
217		vec![1, 2, 3]
218	}
219}
220
221#[derive(Copy, Clone, PartialEq, Eq, Debug)]
222pub struct Opaque(u32);
223
224impl From<Opaque> for u32 {
225	fn from(value: Opaque) -> Self {
226		value.0
227	}
228}
229
230impl TryFrom<u32> for Opaque {
231	type Error = ();
232	fn try_from(value: u32) -> Result<Self, Self::Error> {
233		Ok(Opaque(value))
234	}
235}
236
237/// This function is not used, but we require it for the compiler to include `sp-io`.
238/// `sp-io` is required for its panic and oom handler.
239#[no_mangle]
240pub fn import_sp_io() {
241	sp_io::misc::print_utf8(&[]);
242}
243
244wasm_export_functions! {
245	fn test_return_data() {
246		let input = vec![1, 2, 3, 4, 5, 6];
247		let res = test_api::return_input(input.clone());
248
249		assert_eq!(input, res);
250	}
251
252	fn test_return_option_data() {
253		let input = vec![1, 2, 3, 4, 5, 6];
254		let res = test_api::return_option_input(input.clone());
255
256		assert_eq!(Some(input), res);
257	}
258
259	fn test_set_storage() {
260		let key = "hello";
261		let value = "world";
262
263		test_api::set_storage(key.as_bytes(), value.as_bytes());
264	}
265
266	fn test_return_value_into_mutable_reference() {
267		let mut data = vec![1, 2, 3, 4, 5, 6];
268
269		test_api::return_value_into_mutable_reference(&mut data);
270
271		let expected = "hello";
272		assert_eq!(expected.as_bytes(), &data[..expected.len()]);
273	}
274
275	fn test_get_and_return_array() {
276		let mut input = unsafe { mem::MaybeUninit::<[u8; 34]>::zeroed().assume_init() };
277		input.copy_from_slice(&[
278			24, 3, 23, 20, 2, 16, 32, 1, 12, 26, 27, 8, 29, 31, 6, 5, 4, 19, 10, 28, 34, 21, 18, 33, 9,
279			13, 22, 25, 15, 11, 30, 7, 14, 17,
280		]);
281
282		let res = test_api::get_and_return_array(input);
283
284		assert_eq!(&res, &input[..16]);
285	}
286
287	fn test_array_as_mutable_reference() {
288		let mut array = [0u8; 16];
289		test_api::array_as_mutable_reference(&mut array);
290
291		assert_eq!(array, TEST_ARRAY);
292	}
293
294	fn test_return_input_public_key() {
295		let key = Public::try_from(
296			&[
297				1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
298				25, 26, 27, 28, 29, 30, 31, 32,
299			][..],
300		).unwrap();
301		let ret_key = test_api::return_input_public_key(key.clone());
302
303		let key_data: &[u8] = key.as_ref();
304		let ret_key_data: &[u8] = ret_key.as_ref();
305		assert_eq!(key_data, ret_key_data);
306	}
307
308	fn test_invalid_utf8_data_should_return_an_error() {
309		let data = vec![0, 159, 146, 150];
310		// I'm an evil hacker, trying to hack!
311		let data_str = unsafe { alloc::str::from_utf8_unchecked(&data) };
312
313		test_api::invalid_utf8_data(data_str);
314	}
315
316	fn test_overwrite_native_function_implementation() {
317		fn new_implementation() -> bool {
318			true
319		}
320
321		// Check native implementation
322		assert!(!test_api::overwrite_native_function_implementation());
323
324		let _guard = test_api::host_overwrite_native_function_implementation
325			.replace_implementation(new_implementation);
326
327		assert!(test_api::overwrite_native_function_implementation());
328	}
329
330	fn test_vec_return_value_memory_is_freed() {
331		let mut len = 0;
332		for _ in 0..1024 {
333			len += test_api::return_16kb().len();
334		}
335		assert_eq!(1024 * 1024 * 4, len);
336	}
337
338	fn test_encoded_return_value_memory_is_freed() {
339		let mut len = 0;
340		for _ in 0..1024 {
341			len += test_api::return_option_input(vec![0; 16 * 1024]).map(|v| v.len()).unwrap();
342		}
343		assert_eq!(1024 * 1024 * 16, len);
344	}
345
346	fn test_array_return_value_memory_is_freed() {
347		let mut len = 0;
348		for _ in 0..1024 * 1024 {
349			len += test_api::get_and_return_array([0; 34])[1];
350		}
351		assert_eq!(0, len);
352	}
353
354	fn test_versioning_works() {
355		// we fix new api to accept only 42 as a proper input
356		// as opposed to sp-runtime-interface-test-wasm-deprecated::test_api::verify_input
357		// which accepted 42 and 50.
358		assert!(test_api::test_versioning(42));
359
360		assert!(!test_api::test_versioning(50));
361		assert!(!test_api::test_versioning(102));
362	}
363
364	fn test_versioning_register_only_works() {
365		// Ensure that we will import the version of the runtime interface function that
366		// isn't tagged with `register_only`.
367		assert!(!test_api::test_versioning_register_only(42));
368		assert!(test_api::test_versioning_register_only(80));
369	}
370
371	fn test_return_input_as_tuple() {
372		let a = vec![1, 3, 4, 5];
373		let b = 10000;
374		let c = Some(vec![2, 3]);
375		let d = 5;
376
377		let res = test_api::return_input_as_tuple(a.clone(), b, c.clone(), d);
378
379		assert_eq!(a, res.0);
380		assert_eq!(b, res.1);
381		assert_eq!(c, res.2);
382		assert_eq!(d, res.3);
383	}
384
385	fn test_return_option_vec() {
386		test_api::return_option_vec();
387	}
388
389	fn test_return_option_bytes() {
390		test_api::return_option_bytes();
391	}
392
393	fn test_marshalling_strategies() {
394		test_api::pass_pointer_and_read_copy([1_u8, 2, 3]);
395		test_api::pass_pointer_and_read(&[1_u8, 2, 3]);
396		test_api::pass_fat_pointer_and_read(&[1_u8, 2, 3][..]);
397		{
398			let mut slice = [1_u8, 2, 3];
399			test_api::pass_fat_pointer_and_read_write(&mut slice);
400			assert_eq!(slice, [4_u8, 5, 6]);
401		}
402		{
403			let mut slice = [9_u8, 9, 9];
404			test_api::pass_pointer_and_write(&mut slice);
405			assert_eq!(slice, [1_u8, 2, 3]);
406		}
407		test_api::pass_by_codec(vec![1_u16, 2, 3]);
408		test_api::pass_slice_ref_by_codec(&[1_u16, 2, 3][..]);
409		test_api::pass_as(Opaque(123));
410		assert_eq!(test_api::return_as(), Opaque(123));
411		assert_eq!(test_api::allocate_and_return_pointer(), [1_u8, 2, 3]);
412		assert_eq!(test_api::allocate_and_return_fat_pointer(), vec![1_u8, 2, 3]);
413		assert_eq!(test_api::allocate_and_return_by_codec(), vec![1_u16, 2, 3]);
414	}
415}