mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 19:17:58 +00:00
923cb8eea1
* Build every wasm crate in its own project with wasm-builder Building all wasm crates in one workspace was a nice idea, however it just introduced problems: 1. We needed to prune old members, but this didn't worked for old git deps. 2. We locked the whole wasm workspace while building one crate. This could lead to infinitely locking the workspace on a crash. Now we just build every crate in its own project, this means we will build the dependencies multiple times. While building the dependencies multiple times, we still decrease the build time by around 30 seconds for Polkadot and Substrate because of the new parallelism ;) * Remove the requirement on wasm-builder-runner This removes the requirement on wasm-builder-runner by using the new `build_dep` feature of cargo. We use nightly anyway and that enables us to use this feature. This solves the problem of not mixing build/proc-macro deps with normal deps. By doing this we get rid off this complicated project structure and can depend directly on `wasm-builder`. This also removes all the code from wasm-builder-runner and mentions that it is deprecated. * Copy the `Cargo.lock` to the correct folder * Remove wasm-builder-runner * Update docs * Fix deterministic check Modified-by: Bastian Köcher <git@kchr.de> * Try to make the ui test happy * Switch to `SKIP_WASM_BUILD` * Rename `SKIP_WASM_BINARY` to the correct name... * Update utils/wasm-builder/src/builder.rs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Update utils/wasm-builder/src/builder.rs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
409 lines
11 KiB
Rust
409 lines
11 KiB
Rust
#![cfg_attr(not(feature = "std"), no_std)]
|
|
|
|
// Make the WASM binary available.
|
|
#[cfg(feature = "std")]
|
|
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
|
|
|
|
/// Wasm binary unwrapped. If built with `SKIP_WASM_BUILD`, the function panics.
|
|
#[cfg(feature = "std")]
|
|
pub fn wasm_binary_unwrap() -> &'static [u8] {
|
|
WASM_BINARY.expect("Development wasm binary is not available. Testing is only \
|
|
supported with the flag disabled.")
|
|
}
|
|
|
|
#[cfg(not(feature = "std"))]
|
|
use sp_std::{vec::Vec, vec};
|
|
|
|
#[cfg(not(feature = "std"))]
|
|
use sp_io::{
|
|
storage, hashing::{blake2_128, blake2_256, sha2_256, twox_128, twox_256},
|
|
crypto::{ed25519_verify, sr25519_verify}, wasm_tracing,
|
|
};
|
|
#[cfg(not(feature = "std"))]
|
|
use sp_runtime::{print, traits::{BlakeTwo256, Hash}};
|
|
#[cfg(not(feature = "std"))]
|
|
use sp_core::{ed25519, sr25519};
|
|
#[cfg(not(feature = "std"))]
|
|
use sp_sandbox::Value;
|
|
|
|
extern "C" {
|
|
#[allow(dead_code)]
|
|
fn missing_external();
|
|
|
|
#[allow(dead_code)]
|
|
fn yet_another_missing_external();
|
|
}
|
|
|
|
#[cfg(not(feature = "std"))]
|
|
/// Mutable static variables should be always observed to have
|
|
/// the initialized value at the start of a runtime call.
|
|
static mut MUTABLE_STATIC: u64 = 32;
|
|
|
|
sp_core::wasm_export_functions! {
|
|
fn test_calling_missing_external() {
|
|
unsafe { missing_external() }
|
|
}
|
|
|
|
fn test_calling_yet_another_missing_external() {
|
|
unsafe { yet_another_missing_external() }
|
|
}
|
|
|
|
fn test_data_in(input: Vec<u8>) -> Vec<u8> {
|
|
print("set_storage");
|
|
storage::set(b"input", &input);
|
|
|
|
print("storage");
|
|
let foo = storage::get(b"foo").unwrap();
|
|
|
|
print("set_storage");
|
|
storage::set(b"baz", &foo);
|
|
|
|
print("finished!");
|
|
b"all ok!".to_vec()
|
|
}
|
|
|
|
fn test_clear_prefix(input: Vec<u8>) -> Vec<u8> {
|
|
storage::clear_prefix(&input);
|
|
b"all ok!".to_vec()
|
|
}
|
|
|
|
fn test_empty_return() {}
|
|
|
|
fn test_exhaust_heap() -> Vec<u8> { Vec::with_capacity(16777216) }
|
|
|
|
fn test_panic() { panic!("test panic") }
|
|
|
|
fn test_conditional_panic(input: Vec<u8>) -> Vec<u8> {
|
|
if input.len() > 0 {
|
|
panic!("test panic")
|
|
}
|
|
|
|
input
|
|
}
|
|
|
|
fn test_blake2_256(input: Vec<u8>) -> Vec<u8> {
|
|
blake2_256(&input).to_vec()
|
|
}
|
|
|
|
fn test_blake2_128(input: Vec<u8>) -> Vec<u8> {
|
|
blake2_128(&input).to_vec()
|
|
}
|
|
|
|
fn test_sha2_256(input: Vec<u8>) -> Vec<u8> {
|
|
sha2_256(&input).to_vec()
|
|
}
|
|
|
|
fn test_twox_256(input: Vec<u8>) -> Vec<u8> {
|
|
twox_256(&input).to_vec()
|
|
}
|
|
|
|
fn test_twox_128(input: Vec<u8>) -> Vec<u8> {
|
|
twox_128(&input).to_vec()
|
|
}
|
|
|
|
fn test_ed25519_verify(input: Vec<u8>) -> bool {
|
|
let mut pubkey = [0; 32];
|
|
let mut sig = [0; 64];
|
|
|
|
pubkey.copy_from_slice(&input[0..32]);
|
|
sig.copy_from_slice(&input[32..96]);
|
|
|
|
let msg = b"all ok!";
|
|
ed25519_verify(&ed25519::Signature(sig), &msg[..], &ed25519::Public(pubkey))
|
|
}
|
|
|
|
fn test_sr25519_verify(input: Vec<u8>) -> bool {
|
|
let mut pubkey = [0; 32];
|
|
let mut sig = [0; 64];
|
|
|
|
pubkey.copy_from_slice(&input[0..32]);
|
|
sig.copy_from_slice(&input[32..96]);
|
|
|
|
let msg = b"all ok!";
|
|
sr25519_verify(&sr25519::Signature(sig), &msg[..], &sr25519::Public(pubkey))
|
|
}
|
|
|
|
fn test_ordered_trie_root() -> Vec<u8> {
|
|
BlakeTwo256::ordered_trie_root(
|
|
vec![
|
|
b"zero"[..].into(),
|
|
b"one"[..].into(),
|
|
b"two"[..].into(),
|
|
],
|
|
).as_ref().to_vec()
|
|
}
|
|
|
|
fn test_sandbox(code: Vec<u8>) -> bool {
|
|
execute_sandboxed(&code, &[]).is_ok()
|
|
}
|
|
|
|
fn test_sandbox_args(code: Vec<u8>) -> bool {
|
|
execute_sandboxed(
|
|
&code,
|
|
&[
|
|
Value::I32(0x12345678),
|
|
Value::I64(0x1234567887654321),
|
|
],
|
|
).is_ok()
|
|
}
|
|
|
|
fn test_sandbox_return_val(code: Vec<u8>) -> bool {
|
|
let ok = match execute_sandboxed(
|
|
&code,
|
|
&[
|
|
Value::I32(0x1336),
|
|
]
|
|
) {
|
|
Ok(sp_sandbox::ReturnValue::Value(Value::I32(0x1337))) => true,
|
|
_ => false,
|
|
};
|
|
|
|
ok
|
|
}
|
|
|
|
fn test_sandbox_instantiate(code: Vec<u8>) -> u8 {
|
|
let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new();
|
|
let code = match sp_sandbox::Instance::new(&code, &env_builder, &mut ()) {
|
|
Ok(_) => 0,
|
|
Err(sp_sandbox::Error::Module) => 1,
|
|
Err(sp_sandbox::Error::Execution) => 2,
|
|
Err(sp_sandbox::Error::OutOfBounds) => 3,
|
|
};
|
|
|
|
code
|
|
}
|
|
|
|
|
|
fn test_sandbox_get_global_val(code: Vec<u8>) -> i64 {
|
|
let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new();
|
|
let instance = if let Ok(i) = sp_sandbox::Instance::new(&code, &env_builder, &mut ()) {
|
|
i
|
|
} else {
|
|
return 20;
|
|
};
|
|
|
|
match instance.get_global_val("test_global") {
|
|
Some(sp_sandbox::Value::I64(val)) => val,
|
|
None => 30,
|
|
val => 40,
|
|
}
|
|
}
|
|
|
|
|
|
fn test_offchain_index_set() {
|
|
sp_io::offchain_index::set(b"k", b"v");
|
|
}
|
|
|
|
|
|
fn test_offchain_local_storage() -> bool {
|
|
let kind = sp_core::offchain::StorageKind::PERSISTENT;
|
|
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None);
|
|
sp_io::offchain::local_storage_set(kind, b"test", b"asd");
|
|
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"asd".to_vec()));
|
|
|
|
let res = sp_io::offchain::local_storage_compare_and_set(
|
|
kind,
|
|
b"test",
|
|
Some(b"asd".to_vec()),
|
|
b"",
|
|
);
|
|
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"".to_vec()));
|
|
res
|
|
}
|
|
|
|
fn test_offchain_local_storage_with_none() {
|
|
let kind = sp_core::offchain::StorageKind::PERSISTENT;
|
|
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None);
|
|
|
|
let res = sp_io::offchain::local_storage_compare_and_set(kind, b"test", None, b"value");
|
|
assert_eq!(res, true);
|
|
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), Some(b"value".to_vec()));
|
|
}
|
|
|
|
fn test_offchain_http() -> bool {
|
|
use sp_core::offchain::HttpRequestStatus;
|
|
let run = || -> Option<()> {
|
|
let id = sp_io::offchain::http_request_start(
|
|
"POST",
|
|
"http://localhost:12345",
|
|
&[],
|
|
).ok()?;
|
|
sp_io::offchain::http_request_add_header(id, "X-Auth", "test").ok()?;
|
|
sp_io::offchain::http_request_write_body(id, &[1, 2, 3, 4], None).ok()?;
|
|
sp_io::offchain::http_request_write_body(id, &[], None).ok()?;
|
|
let status = sp_io::offchain::http_response_wait(&[id], None);
|
|
assert!(status == vec![HttpRequestStatus::Finished(200)], "Expected Finished(200) status.");
|
|
let headers = sp_io::offchain::http_response_headers(id);
|
|
assert_eq!(headers, vec![(b"X-Auth".to_vec(), b"hello".to_vec())]);
|
|
let mut buffer = vec![0; 64];
|
|
let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?;
|
|
assert_eq!(read, 3);
|
|
assert_eq!(&buffer[0..read as usize], &[1, 2, 3]);
|
|
let read = sp_io::offchain::http_response_read_body(id, &mut buffer, None).ok()?;
|
|
assert_eq!(read, 0);
|
|
|
|
Some(())
|
|
};
|
|
|
|
run().is_some()
|
|
}
|
|
|
|
// Just some test to make sure that `sp-allocator` compiles on `no_std`.
|
|
fn test_sp_allocator_compiles() {
|
|
sp_allocator::FreeingBumpHeapAllocator::new(0);
|
|
}
|
|
|
|
fn test_enter_span() -> u64 {
|
|
wasm_tracing::enter_span(Default::default())
|
|
}
|
|
|
|
fn test_exit_span(span_id: u64) {
|
|
wasm_tracing::exit(span_id)
|
|
}
|
|
|
|
fn returns_mutable_static() -> u64 {
|
|
unsafe {
|
|
MUTABLE_STATIC += 1;
|
|
MUTABLE_STATIC
|
|
}
|
|
}
|
|
|
|
fn allocates_huge_stack_array(trap: bool) -> Vec<u8> {
|
|
// Allocate a stack frame that is approx. 75% of the stack (assuming it is 1MB).
|
|
// This will just decrease (stacks in wasm32-u-u grow downwards) the stack
|
|
// pointer. This won't trap on the current compilers.
|
|
let mut data = [0u8; 1024 * 768];
|
|
|
|
// Then make sure we actually write something to it.
|
|
//
|
|
// If:
|
|
// 1. the stack area is placed at the beginning of the linear memory space, and
|
|
// 2. the stack pointer points to out-of-bounds area, and
|
|
// 3. a write is performed around the current stack pointer.
|
|
//
|
|
// then a trap should happen.
|
|
//
|
|
for (i, v) in data.iter_mut().enumerate() {
|
|
*v = i as u8; // deliberate truncation
|
|
}
|
|
|
|
if trap {
|
|
// There is a small chance of this to be pulled up in theory. In practice
|
|
// the probability of that is rather low.
|
|
panic!()
|
|
}
|
|
|
|
data.to_vec()
|
|
}
|
|
|
|
// Check that the heap at `heap_base + offset` don't contains the test message.
|
|
// After the check succeeds the test message is written into the heap.
|
|
//
|
|
// It is expected that the given pointer is not allocated.
|
|
fn check_and_set_in_heap(heap_base: u32, offset: u32) {
|
|
let test_message = b"Hello invalid heap memory";
|
|
let ptr = unsafe { (heap_base + offset) as *mut u8 };
|
|
|
|
let message_slice = unsafe { sp_std::slice::from_raw_parts_mut(ptr, test_message.len()) };
|
|
|
|
assert_ne!(test_message, message_slice);
|
|
message_slice.copy_from_slice(test_message);
|
|
}
|
|
|
|
fn test_spawn() {
|
|
let data = vec![1u8, 2u8];
|
|
let data_new = sp_tasks::spawn(tasks::incrementer, data).join();
|
|
|
|
assert_eq!(data_new, vec![2u8, 3u8]);
|
|
}
|
|
|
|
fn test_nested_spawn() {
|
|
let data = vec![7u8, 13u8];
|
|
let data_new = sp_tasks::spawn(tasks::parallel_incrementer, data).join();
|
|
|
|
assert_eq!(data_new, vec![10u8, 16u8]);
|
|
}
|
|
|
|
fn test_panic_in_spawned() {
|
|
sp_tasks::spawn(tasks::panicker, vec![]).join();
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "std"))]
|
|
mod tasks {
|
|
use sp_std::prelude::*;
|
|
|
|
pub fn incrementer(data: Vec<u8>) -> Vec<u8> {
|
|
data.into_iter().map(|v| v + 1).collect()
|
|
}
|
|
|
|
pub fn panicker(_: Vec<u8>) -> Vec<u8> {
|
|
panic!()
|
|
}
|
|
|
|
pub fn parallel_incrementer(data: Vec<u8>) -> Vec<u8> {
|
|
let first = data.into_iter().map(|v| v + 2).collect::<Vec<_>>();
|
|
let second = sp_tasks::spawn(incrementer, first).join();
|
|
second
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "std"))]
|
|
fn execute_sandboxed(
|
|
code: &[u8],
|
|
args: &[Value],
|
|
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> {
|
|
struct State {
|
|
counter: u32,
|
|
}
|
|
|
|
fn env_assert(
|
|
_e: &mut State,
|
|
args: &[Value],
|
|
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> {
|
|
if args.len() != 1 {
|
|
return Err(sp_sandbox::HostError);
|
|
}
|
|
let condition = args[0].as_i32().ok_or_else(|| sp_sandbox::HostError)?;
|
|
if condition != 0 {
|
|
Ok(sp_sandbox::ReturnValue::Unit)
|
|
} else {
|
|
Err(sp_sandbox::HostError)
|
|
}
|
|
}
|
|
fn env_inc_counter(
|
|
e: &mut State,
|
|
args: &[Value],
|
|
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> {
|
|
if args.len() != 1 {
|
|
return Err(sp_sandbox::HostError);
|
|
}
|
|
let inc_by = args[0].as_i32().ok_or_else(|| sp_sandbox::HostError)?;
|
|
e.counter += inc_by as u32;
|
|
Ok(sp_sandbox::ReturnValue::Value(Value::I32(e.counter as i32)))
|
|
}
|
|
|
|
let mut state = State { counter: 0 };
|
|
|
|
let env_builder = {
|
|
let mut env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new();
|
|
env_builder.add_host_func("env", "assert", env_assert);
|
|
env_builder.add_host_func("env", "inc_counter", env_inc_counter);
|
|
let memory = match sp_sandbox::Memory::new(1, Some(16)) {
|
|
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);
|
|
env_builder
|
|
};
|
|
|
|
let mut instance = sp_sandbox::Instance::new(code, &env_builder, &mut state)?;
|
|
let result = instance.invoke("call", args, &mut state);
|
|
|
|
result.map_err(|_| sp_sandbox::HostError)
|
|
}
|