mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-22 13:45:40 +00:00
Remove deprecated host functions (#5038)
Sadly we need to keep one function `ext_blake2_256`. This function is manually defined in `sp-core`.
This commit is contained in:
@@ -1,941 +0,0 @@
|
|||||||
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Substrate is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Substrate is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Definition and implementation of the old and deprecated Substrate runtime interface for the host.
|
|
||||||
|
|
||||||
use codec::Encode;
|
|
||||||
use std::{convert::TryFrom, str};
|
|
||||||
use sp_core::{
|
|
||||||
blake2_128, blake2_256, twox_64, twox_128, twox_256, ed25519, sr25519, keccak_256, Blake2Hasher, Pair,
|
|
||||||
crypto::KeyTypeId, offchain,
|
|
||||||
};
|
|
||||||
use sp_trie::{TrieConfiguration, trie_types::Layout};
|
|
||||||
use sp_wasm_interface::{
|
|
||||||
Pointer, WordSize, WritePrimitive, ReadPrimitive, FunctionContext, Result as WResult,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature="wasm-extern-trace")]
|
|
||||||
macro_rules! debug_trace {
|
|
||||||
( $( $x:tt )* ) => ( trace!( $( $x )* ) )
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature="wasm-extern-trace"))]
|
|
||||||
macro_rules! debug_trace {
|
|
||||||
( $( $x:tt )* ) => ()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The old and deprecated Substrate externals. These are still required for backwards compatibility
|
|
||||||
/// reasons.
|
|
||||||
pub struct SubstrateExternals;
|
|
||||||
|
|
||||||
enum RecoverResult {
|
|
||||||
Invalid(u32),
|
|
||||||
Valid(secp256k1::PublicKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn secp256k1_recover(
|
|
||||||
context: &mut dyn FunctionContext,
|
|
||||||
msg_data: Pointer<u8>,
|
|
||||||
sig_data: Pointer<u8>,
|
|
||||||
) -> WResult<RecoverResult> {
|
|
||||||
let mut sig = [0u8; 65];
|
|
||||||
context.read_memory_into(sig_data, &mut sig[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get signature in ext_secp256k1_ecdsa_recover")?;
|
|
||||||
let rs = match secp256k1::Signature::parse_slice(&sig[0..64]) {
|
|
||||||
Ok(rs) => rs,
|
|
||||||
_ => return Ok(RecoverResult::Invalid(1)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let recovery_id = if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8;
|
|
||||||
let v = match secp256k1::RecoveryId::parse(recovery_id) {
|
|
||||||
Ok(v) => v,
|
|
||||||
_ => return Ok(RecoverResult::Invalid(2)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut msg = [0u8; 32];
|
|
||||||
context.read_memory_into(msg_data, &mut msg[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get message in ext_secp256k1_ecdsa_recover")?;
|
|
||||||
|
|
||||||
Ok(match secp256k1::recover(&secp256k1::Message::parse(&msg), &rs, &v) {
|
|
||||||
Ok(pubkey) => RecoverResult::Valid(pubkey),
|
|
||||||
Err(_) => RecoverResult::Invalid(3),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_wasm_host_interface! {
|
|
||||||
impl SubstrateExternals where context {
|
|
||||||
ext_malloc(size: WordSize) -> Pointer<u8> {
|
|
||||||
let r = context.allocate_memory(size)?;
|
|
||||||
debug_trace!(target: "sp-io", "malloc {} bytes at {:?}", size, r);
|
|
||||||
Ok(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_free(addr: Pointer<u8>) {
|
|
||||||
context.deallocate_memory(addr)?;
|
|
||||||
debug_trace!(target: "sp-io", "free {:?}", addr);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_sandbox_instantiate(
|
|
||||||
dispatch_thunk_idx: u32,
|
|
||||||
wasm_ptr: Pointer<u8>,
|
|
||||||
wasm_len: WordSize,
|
|
||||||
imports_ptr: Pointer<u8>,
|
|
||||||
imports_len: WordSize,
|
|
||||||
state: u32,
|
|
||||||
) -> u32 {
|
|
||||||
let wasm = context.read_memory(wasm_ptr, wasm_len)
|
|
||||||
.map_err(|_| "OOB while ext_sandbox_instantiate: wasm")?;
|
|
||||||
let raw_env_def = context.read_memory(imports_ptr, imports_len)
|
|
||||||
.map_err(|_| "OOB while ext_sandbox_instantiate: imports")?;
|
|
||||||
|
|
||||||
context.sandbox().instance_new(dispatch_thunk_idx, &wasm, &raw_env_def, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_sandbox_instance_teardown(instance_idx: u32) {
|
|
||||||
context.sandbox().instance_teardown(instance_idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_sandbox_invoke(
|
|
||||||
instance_idx: u32,
|
|
||||||
export_ptr: Pointer<u8>,
|
|
||||||
export_len: WordSize,
|
|
||||||
args_ptr: Pointer<u8>,
|
|
||||||
args_len: WordSize,
|
|
||||||
return_val_ptr: Pointer<u8>,
|
|
||||||
return_val_len: WordSize,
|
|
||||||
state: u32,
|
|
||||||
) -> u32 {
|
|
||||||
let export = context.read_memory(export_ptr, export_len)
|
|
||||||
.map_err(|_| "OOB while ext_sandbox_invoke: export")
|
|
||||||
.and_then(|b|
|
|
||||||
String::from_utf8(b)
|
|
||||||
.map_err(|_| "Export name should be a valid utf-8 sequence")
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Deserialize arguments and convert them into wasmi types.
|
|
||||||
let serialized_args = context.read_memory(args_ptr, args_len)
|
|
||||||
.map_err(|_| "OOB while ext_sandbox_invoke: args")?;
|
|
||||||
|
|
||||||
context.sandbox().invoke(
|
|
||||||
instance_idx,
|
|
||||||
&export,
|
|
||||||
&serialized_args,
|
|
||||||
return_val_ptr,
|
|
||||||
return_val_len,
|
|
||||||
state,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_sandbox_memory_new(initial: WordSize, maximum: WordSize) -> u32 {
|
|
||||||
context.sandbox().memory_new(initial, maximum)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_sandbox_memory_get(
|
|
||||||
memory_idx: u32,
|
|
||||||
offset: WordSize,
|
|
||||||
buf_ptr: Pointer<u8>,
|
|
||||||
buf_len: WordSize,
|
|
||||||
) -> u32 {
|
|
||||||
context.sandbox().memory_get(memory_idx, offset, buf_ptr, buf_len)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_sandbox_memory_set(
|
|
||||||
memory_idx: u32,
|
|
||||||
offset: WordSize,
|
|
||||||
val_ptr: Pointer<u8>,
|
|
||||||
val_len: WordSize,
|
|
||||||
) -> u32 {
|
|
||||||
context.sandbox().memory_set(memory_idx, offset, val_ptr, val_len)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_sandbox_memory_teardown(memory_idx: u32) {
|
|
||||||
context.sandbox().memory_teardown(memory_idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_print_utf8(utf8_data: Pointer<u8>, utf8_len: WordSize) {
|
|
||||||
if let Ok(utf8) = context.read_memory(utf8_data, utf8_len) {
|
|
||||||
sp_io::misc::print_utf8(&utf8);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_print_hex(data: Pointer<u8>, len: WordSize) {
|
|
||||||
if let Ok(hex) = context.read_memory(data, len) {
|
|
||||||
sp_io::misc::print_hex(&hex);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_print_num(number: u64) {
|
|
||||||
sp_io::misc::print_num(number);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_log(
|
|
||||||
level: u32,
|
|
||||||
target_data: Pointer<u8>,
|
|
||||||
target_len: WordSize,
|
|
||||||
message_data: Pointer<u8>,
|
|
||||||
message_len: WordSize,
|
|
||||||
) {
|
|
||||||
let target = context.read_memory(target_data, target_len)
|
|
||||||
.map_err(|_| "Invalid attempt to determine target in ext_log")?;
|
|
||||||
let message = context.read_memory(message_data, message_len)
|
|
||||||
.map_err(|_| "Invalid attempt to determine message in ext_log")?;
|
|
||||||
|
|
||||||
let target_str = std::str::from_utf8(&target)
|
|
||||||
.map_err(|_| "Target invalid utf8 in ext_log")?;
|
|
||||||
|
|
||||||
sp_io::logging::log(level.into(), &target_str, &message);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_set_storage(
|
|
||||||
key_data: Pointer<u8>,
|
|
||||||
key_len: WordSize,
|
|
||||||
value_data: Pointer<u8>,
|
|
||||||
value_len: WordSize,
|
|
||||||
) {
|
|
||||||
let key = context.read_memory(key_data, key_len)
|
|
||||||
.map_err(|_| "Invalid attempt to determine key in ext_set_storage")?;
|
|
||||||
let value = context.read_memory(value_data, value_len)
|
|
||||||
.map_err(|_| "Invalid attempt to determine value in ext_set_storage")?;
|
|
||||||
Ok(sp_io::storage::set(&key, &value))
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_clear_storage(key_data: Pointer<u8>, key_len: WordSize) {
|
|
||||||
let key = context.read_memory(key_data, key_len)
|
|
||||||
.map_err(|_| "Invalid attempt to determine key in ext_clear_storage")?;
|
|
||||||
Ok(sp_io::storage::clear(&key))
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_exists_storage(key_data: Pointer<u8>, key_len: WordSize) -> u32 {
|
|
||||||
let key = context.read_memory(key_data, key_len)
|
|
||||||
.map_err(|_| "Invalid attempt to determine key in ext_exists_storage")?;
|
|
||||||
Ok(if sp_io::storage::exists(&key) { 1 } else { 0 })
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_clear_prefix(prefix_data: Pointer<u8>, prefix_len: WordSize) {
|
|
||||||
let prefix = context.read_memory(prefix_data, prefix_len)
|
|
||||||
.map_err(|_| "Invalid attempt to determine prefix in ext_clear_prefix")?;
|
|
||||||
Ok(sp_io::storage::clear_prefix(&prefix))
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_get_allocated_storage(
|
|
||||||
key_data: Pointer<u8>,
|
|
||||||
key_len: WordSize,
|
|
||||||
written_out: Pointer<u32>,
|
|
||||||
) -> Pointer<u8> {
|
|
||||||
let key = context.read_memory(key_data, key_len)
|
|
||||||
.map_err(|_| "Invalid attempt to determine key in ext_get_allocated_storage")?;
|
|
||||||
|
|
||||||
if let Some(value) = sp_io::storage::get(&key) {
|
|
||||||
let offset = context.allocate_memory(value.len() as u32)?;
|
|
||||||
context.write_memory(offset, &value)
|
|
||||||
.map_err(|_| "Invalid attempt to set memory in ext_get_allocated_storage")?;
|
|
||||||
context.write_primitive(written_out, value.len() as u32)
|
|
||||||
.map_err(|_| "Invalid attempt to write written_out in ext_get_allocated_storage")?;
|
|
||||||
Ok(offset)
|
|
||||||
} else {
|
|
||||||
context.write_primitive(written_out, u32::max_value())
|
|
||||||
.map_err(|_| "Invalid attempt to write failed written_out in ext_get_allocated_storage")?;
|
|
||||||
Ok(Pointer::null())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_get_storage_into(
|
|
||||||
key_data: Pointer<u8>,
|
|
||||||
key_len: WordSize,
|
|
||||||
value_data: Pointer<u8>,
|
|
||||||
value_len: WordSize,
|
|
||||||
value_offset: WordSize,
|
|
||||||
) -> WordSize {
|
|
||||||
let key = context.read_memory(key_data, key_len)
|
|
||||||
.map_err(|_| "Invalid attempt to get key in ext_get_storage_into")?;
|
|
||||||
|
|
||||||
if let Some(value) = sp_io::storage::get(&key) {
|
|
||||||
let data = &value[value.len().min(value_offset as usize)..];
|
|
||||||
let written = std::cmp::min(value_len as usize, data.len());
|
|
||||||
context.write_memory(value_data, &data[..written])
|
|
||||||
.map_err(|_| "Invalid attempt to set value in ext_get_storage_into")?;
|
|
||||||
Ok(value.len() as u32)
|
|
||||||
} else {
|
|
||||||
Ok(u32::max_value())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_storage_root(result: Pointer<u8>) {
|
|
||||||
context.write_memory(result, sp_io::storage::root().as_ref())
|
|
||||||
.map_err(|_| "Invalid attempt to set memory in ext_storage_root".into())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_storage_changes_root(
|
|
||||||
parent_hash_data: Pointer<u8>,
|
|
||||||
_len: WordSize,
|
|
||||||
result: Pointer<u8>,
|
|
||||||
) -> u32 {
|
|
||||||
let mut parent_hash = [0u8; 32];
|
|
||||||
context.read_memory_into(parent_hash_data, &mut parent_hash[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get parent_hash in ext_storage_changes_root")?;
|
|
||||||
|
|
||||||
if let Some(r) = sp_io::storage::changes_root(&parent_hash) {
|
|
||||||
context.write_memory(result, &r[..])
|
|
||||||
.map_err(|_| "Invalid attempt to set memory in ext_storage_changes_root")?;
|
|
||||||
Ok(1)
|
|
||||||
} else {
|
|
||||||
Ok(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_blake2_256_enumerated_trie_root(
|
|
||||||
values_data: Pointer<u8>,
|
|
||||||
lens_data: Pointer<u32>,
|
|
||||||
lens_len: WordSize,
|
|
||||||
result: Pointer<u8>,
|
|
||||||
) {
|
|
||||||
let values = (0..lens_len)
|
|
||||||
.map(|i| context.read_primitive(lens_data.offset(i).ok_or("Pointer overflow")?))
|
|
||||||
.collect::<std::result::Result<Vec<u32>, _>>()?
|
|
||||||
.into_iter()
|
|
||||||
.scan(0u32, |acc, v| { let o = *acc; *acc += v; Some((o, v)) })
|
|
||||||
.map(|(offset, len)|
|
|
||||||
context.read_memory(values_data.offset(offset).ok_or("Pointer overflow")?, len)
|
|
||||||
.map_err(|_|
|
|
||||||
"Invalid attempt to get memory in ext_blake2_256_enumerated_trie_root"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.collect::<std::result::Result<Vec<_>, _>>()?;
|
|
||||||
let r = Layout::<Blake2Hasher>::ordered_trie_root(values.into_iter());
|
|
||||||
context.write_memory(result, &r[..])
|
|
||||||
.map_err(|_| "Invalid attempt to set memory in ext_blake2_256_enumerated_trie_root")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_chain_id() -> u64 {
|
|
||||||
Ok(sp_io::misc::chain_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_twox_64(data: Pointer<u8>, len: WordSize, out: Pointer<u8>) {
|
|
||||||
let result: [u8; 8] = if len == 0 {
|
|
||||||
let hashed = twox_64(&[0u8; 0]);
|
|
||||||
hashed
|
|
||||||
} else {
|
|
||||||
let key = context.read_memory(data, len)
|
|
||||||
.map_err(|_| "Invalid attempt to get key in ext_twox_64")?;
|
|
||||||
let hashed_key = twox_64(&key);
|
|
||||||
hashed_key
|
|
||||||
};
|
|
||||||
|
|
||||||
context.write_memory(out, &result)
|
|
||||||
.map_err(|_| "Invalid attempt to set result in ext_twox_64")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_twox_128(data: Pointer<u8>, len: WordSize, out: Pointer<u8>) {
|
|
||||||
let result: [u8; 16] = if len == 0 {
|
|
||||||
let hashed = twox_128(&[0u8; 0]);
|
|
||||||
hashed
|
|
||||||
} else {
|
|
||||||
let key = context.read_memory(data, len)
|
|
||||||
.map_err(|_| "Invalid attempt to get key in ext_twox_128")?;
|
|
||||||
let hashed_key = twox_128(&key);
|
|
||||||
hashed_key
|
|
||||||
};
|
|
||||||
|
|
||||||
context.write_memory(out, &result)
|
|
||||||
.map_err(|_| "Invalid attempt to set result in ext_twox_128")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_twox_256(data: Pointer<u8>, len: WordSize, out: Pointer<u8>) {
|
|
||||||
let result: [u8; 32] = if len == 0 {
|
|
||||||
twox_256(&[0u8; 0])
|
|
||||||
} else {
|
|
||||||
let mem = context.read_memory(data, len)
|
|
||||||
.map_err(|_| "Invalid attempt to get data in ext_twox_256")?;
|
|
||||||
twox_256(&mem)
|
|
||||||
};
|
|
||||||
context.write_memory(out, &result)
|
|
||||||
.map_err(|_| "Invalid attempt to set result in ext_twox_256")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_blake2_128(data: Pointer<u8>, len: WordSize, out: Pointer<u8>) {
|
|
||||||
let result: [u8; 16] = if len == 0 {
|
|
||||||
let hashed = blake2_128(&[0u8; 0]);
|
|
||||||
hashed
|
|
||||||
} else {
|
|
||||||
let key = context.read_memory(data, len)
|
|
||||||
.map_err(|_| "Invalid attempt to get key in ext_blake2_128")?;
|
|
||||||
let hashed_key = blake2_128(&key);
|
|
||||||
hashed_key
|
|
||||||
};
|
|
||||||
|
|
||||||
context.write_memory(out, &result)
|
|
||||||
.map_err(|_| "Invalid attempt to set result in ext_blake2_128")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_blake2_256(data: Pointer<u8>, len: WordSize, out: Pointer<u8>) {
|
|
||||||
let result: [u8; 32] = if len == 0 {
|
|
||||||
blake2_256(&[0u8; 0])
|
|
||||||
} else {
|
|
||||||
let mem = context.read_memory(data, len)
|
|
||||||
.map_err(|_| "Invalid attempt to get data in ext_blake2_256")?;
|
|
||||||
blake2_256(&mem)
|
|
||||||
};
|
|
||||||
context.write_memory(out, &result)
|
|
||||||
.map_err(|_| "Invalid attempt to set result in ext_blake2_256")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_keccak_256(data: Pointer<u8>, len: WordSize, out: Pointer<u8>) {
|
|
||||||
let result: [u8; 32] = if len == 0 {
|
|
||||||
keccak_256(&[0u8; 0])
|
|
||||||
} else {
|
|
||||||
let mem = context.read_memory(data, len)
|
|
||||||
.map_err(|_| "Invalid attempt to get data in ext_keccak_256")?;
|
|
||||||
keccak_256(&mem)
|
|
||||||
};
|
|
||||||
context.write_memory(out, &result)
|
|
||||||
.map_err(|_| "Invalid attempt to set result in ext_keccak_256")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_ed25519_public_keys(id_data: Pointer<u8>, result_len: Pointer<u32>) -> Pointer<u8> {
|
|
||||||
let mut id = [0u8; 4];
|
|
||||||
context.read_memory_into(id_data, &mut id[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get id in ext_ed25519_public_keys")?;
|
|
||||||
let key_type = KeyTypeId(id);
|
|
||||||
|
|
||||||
let keys = sp_io::crypto::ed25519_public_keys(key_type).encode();
|
|
||||||
|
|
||||||
let len = keys.len() as u32;
|
|
||||||
let offset = context.allocate_memory(len)?;
|
|
||||||
|
|
||||||
context.write_memory(offset, keys.as_ref())
|
|
||||||
.map_err(|_| "Invalid attempt to set memory in ext_ed25519_public_keys")?;
|
|
||||||
context.write_primitive(result_len, len)
|
|
||||||
.map_err(|_| "Invalid attempt to write result_len in ext_ed25519_public_keys")?;
|
|
||||||
|
|
||||||
Ok(offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_ed25519_verify(
|
|
||||||
msg_data: Pointer<u8>,
|
|
||||||
msg_len: WordSize,
|
|
||||||
sig_data: Pointer<u8>,
|
|
||||||
pubkey_data: Pointer<u8>,
|
|
||||||
) -> u32 {
|
|
||||||
let mut sig = [0u8; 64];
|
|
||||||
context.read_memory_into(sig_data, &mut sig[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get signature in ext_ed25519_verify")?;
|
|
||||||
let mut pubkey = [0u8; 32];
|
|
||||||
context.read_memory_into(pubkey_data, &mut pubkey[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get pubkey in ext_ed25519_verify")?;
|
|
||||||
let msg = context.read_memory(msg_data, msg_len)
|
|
||||||
.map_err(|_| "Invalid attempt to get message in ext_ed25519_verify")?;
|
|
||||||
|
|
||||||
Ok(if ed25519::Pair::verify_weak(&sig, &msg, &pubkey) {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_ed25519_generate(
|
|
||||||
id_data: Pointer<u8>,
|
|
||||||
seed: Pointer<u8>,
|
|
||||||
seed_len: WordSize,
|
|
||||||
out: Pointer<u8>,
|
|
||||||
) {
|
|
||||||
let mut id = [0u8; 4];
|
|
||||||
context.read_memory_into(id_data, &mut id[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get id in ext_ed25519_generate")?;
|
|
||||||
let key_type = KeyTypeId(id);
|
|
||||||
|
|
||||||
let seed = if seed_len == 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(
|
|
||||||
context.read_memory(seed, seed_len)
|
|
||||||
.map_err(|_| "Invalid attempt to get seed in ext_ed25519_generate")?
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let pubkey = sp_io::crypto::ed25519_generate(key_type, seed);
|
|
||||||
|
|
||||||
context.write_memory(out, pubkey.as_ref())
|
|
||||||
.map_err(|_| "Invalid attempt to set out in ext_ed25519_generate".into())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_ed25519_sign(
|
|
||||||
id_data: Pointer<u8>,
|
|
||||||
pubkey_data: Pointer<u8>,
|
|
||||||
msg_data: Pointer<u8>,
|
|
||||||
msg_len: WordSize,
|
|
||||||
out: Pointer<u8>,
|
|
||||||
) -> u32 {
|
|
||||||
let mut id = [0u8; 4];
|
|
||||||
context.read_memory_into(id_data, &mut id[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get id in ext_ed25519_sign")?;
|
|
||||||
let key_type = KeyTypeId(id);
|
|
||||||
|
|
||||||
let mut pubkey = [0u8; 32];
|
|
||||||
context.read_memory_into(pubkey_data, &mut pubkey[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get pubkey in ext_ed25519_sign")?;
|
|
||||||
|
|
||||||
let msg = context.read_memory(msg_data, msg_len)
|
|
||||||
.map_err(|_| "Invalid attempt to get message in ext_ed25519_sign")?;
|
|
||||||
|
|
||||||
let pub_key = ed25519::Public::try_from(pubkey.as_ref())
|
|
||||||
.map_err(|_| "Invalid `ed25519` public key")?;
|
|
||||||
|
|
||||||
let signature = sp_io::crypto::ed25519_sign(key_type, &pub_key, &msg);
|
|
||||||
|
|
||||||
match signature {
|
|
||||||
Some(signature) => {
|
|
||||||
context.write_memory(out, signature.as_ref())
|
|
||||||
.map_err(|_| "Invalid attempt to set out in ext_ed25519_sign")?;
|
|
||||||
Ok(0)
|
|
||||||
},
|
|
||||||
None => Ok(1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_sr25519_public_keys(id_data: Pointer<u8>, result_len: Pointer<u32>) -> Pointer<u8> {
|
|
||||||
let mut id = [0u8; 4];
|
|
||||||
context.read_memory_into(id_data, &mut id[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get id in ext_sr25519_public_keys")?;
|
|
||||||
let key_type = KeyTypeId(id);
|
|
||||||
|
|
||||||
let keys = sp_io::crypto::sr25519_public_keys(key_type).encode();
|
|
||||||
|
|
||||||
let len = keys.len() as u32;
|
|
||||||
let offset = context.allocate_memory(len)?;
|
|
||||||
|
|
||||||
context.write_memory(offset, keys.as_ref())
|
|
||||||
.map_err(|_| "Invalid attempt to set memory in ext_sr25519_public_keys")?;
|
|
||||||
context.write_primitive(result_len, len)
|
|
||||||
.map_err(|_| "Invalid attempt to write result_len in ext_sr25519_public_keys")?;
|
|
||||||
|
|
||||||
Ok(offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_sr25519_verify(
|
|
||||||
msg_data: Pointer<u8>,
|
|
||||||
msg_len: WordSize,
|
|
||||||
sig_data: Pointer<u8>,
|
|
||||||
pubkey_data: Pointer<u8>,
|
|
||||||
) -> u32 {
|
|
||||||
let mut sig = [0u8; 64];
|
|
||||||
context.read_memory_into(sig_data, &mut sig[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get signature in ext_sr25519_verify")?;
|
|
||||||
let mut pubkey = [0u8; 32];
|
|
||||||
context.read_memory_into(pubkey_data, &mut pubkey[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get pubkey in ext_sr25519_verify")?;
|
|
||||||
let msg = context.read_memory(msg_data, msg_len)
|
|
||||||
.map_err(|_| "Invalid attempt to get message in ext_sr25519_verify")?;
|
|
||||||
|
|
||||||
Ok(if sr25519::Pair::verify_weak(&sig, &msg, &pubkey) {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_sr25519_generate(
|
|
||||||
id_data: Pointer<u8>,
|
|
||||||
seed: Pointer<u8>,
|
|
||||||
seed_len: WordSize,
|
|
||||||
out: Pointer<u8>,
|
|
||||||
) {
|
|
||||||
let mut id = [0u8; 4];
|
|
||||||
context.read_memory_into(id_data, &mut id[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get id in ext_sr25519_generate")?;
|
|
||||||
let key_type = KeyTypeId(id);
|
|
||||||
let seed = if seed_len == 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(
|
|
||||||
context.read_memory(seed, seed_len)
|
|
||||||
.map_err(|_| "Invalid attempt to get seed in ext_sr25519_generate")?
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let pubkey = sp_io::crypto::sr25519_generate(key_type, seed);
|
|
||||||
|
|
||||||
context.write_memory(out, pubkey.as_ref())
|
|
||||||
.map_err(|_| "Invalid attempt to set out in ext_sr25519_generate".into())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_sr25519_sign(
|
|
||||||
id_data: Pointer<u8>,
|
|
||||||
pubkey_data: Pointer<u8>,
|
|
||||||
msg_data: Pointer<u8>,
|
|
||||||
msg_len: WordSize,
|
|
||||||
out: Pointer<u8>,
|
|
||||||
) -> u32 {
|
|
||||||
let mut id = [0u8; 4];
|
|
||||||
context.read_memory_into(id_data, &mut id[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get id in ext_sr25519_sign")?;
|
|
||||||
let key_type = KeyTypeId(id);
|
|
||||||
|
|
||||||
let mut pubkey = [0u8; 32];
|
|
||||||
context.read_memory_into(pubkey_data, &mut pubkey[..])
|
|
||||||
.map_err(|_| "Invalid attempt to get pubkey in ext_sr25519_sign")?;
|
|
||||||
|
|
||||||
let msg = context.read_memory(msg_data, msg_len)
|
|
||||||
.map_err(|_| "Invalid attempt to get message in ext_sr25519_sign")?;
|
|
||||||
|
|
||||||
let pub_key = sr25519::Public::try_from(pubkey.as_ref())
|
|
||||||
.map_err(|_| "Invalid `sr25519` public key")?;
|
|
||||||
|
|
||||||
let signature = sp_io::crypto::sr25519_sign(key_type, &pub_key, &msg);
|
|
||||||
|
|
||||||
match signature {
|
|
||||||
Some(signature) => {
|
|
||||||
context.write_memory(out, signature.as_ref())
|
|
||||||
.map_err(|_| "Invalid attempt to set out in ext_sr25519_sign")?;
|
|
||||||
Ok(0)
|
|
||||||
},
|
|
||||||
None => Ok(1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_secp256k1_ecdsa_recover(
|
|
||||||
msg_data: Pointer<u8>,
|
|
||||||
sig_data: Pointer<u8>,
|
|
||||||
pubkey_data: Pointer<u8>,
|
|
||||||
) -> u32 {
|
|
||||||
match secp256k1_recover(context, msg_data, sig_data)? {
|
|
||||||
RecoverResult::Invalid(c) => Ok(c),
|
|
||||||
RecoverResult::Valid(pubkey) => {
|
|
||||||
context.write_memory(pubkey_data, &pubkey.serialize()[1..65])
|
|
||||||
.map_err(|_| "Invalid attempt to set pubkey in ext_secp256k1_ecdsa_recover")?;
|
|
||||||
Ok(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_secp256k1_ecdsa_recover_compressed(
|
|
||||||
msg_data: Pointer<u8>,
|
|
||||||
sig_data: Pointer<u8>,
|
|
||||||
pubkey_data: Pointer<u8>,
|
|
||||||
) -> u32 {
|
|
||||||
match secp256k1_recover(context, msg_data, sig_data)? {
|
|
||||||
RecoverResult::Invalid(c) => Ok(c),
|
|
||||||
RecoverResult::Valid(pubkey) => {
|
|
||||||
context.write_memory(pubkey_data, &pubkey.serialize_compressed()[..])
|
|
||||||
.map_err(|_| "Invalid attempt to set pubkey in ext_secp256k1_ecdsa_recover")?;
|
|
||||||
Ok(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_is_validator() -> u32 {
|
|
||||||
if sp_io::offchain::is_validator() { Ok(1) } else { Ok(0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_submit_transaction(msg_data: Pointer<u8>, len: WordSize) -> u32 {
|
|
||||||
let extrinsic = context.read_memory(msg_data, len)
|
|
||||||
.map_err(|_| "OOB while ext_submit_transaction: wasm")?;
|
|
||||||
|
|
||||||
let res = sp_io::offchain::submit_transaction(extrinsic);
|
|
||||||
|
|
||||||
Ok(if res.is_ok() { 0 } else { 1 })
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_network_state(written_out: Pointer<u32>) -> Pointer<u8> {
|
|
||||||
let res = sp_io::offchain::network_state();
|
|
||||||
|
|
||||||
let encoded = res.encode();
|
|
||||||
let len = encoded.len() as u32;
|
|
||||||
let offset = context.allocate_memory(len)?;
|
|
||||||
context.write_memory(offset, &encoded)
|
|
||||||
.map_err(|_| "Invalid attempt to set memory in ext_network_state")?;
|
|
||||||
|
|
||||||
context.write_primitive(written_out, len)
|
|
||||||
.map_err(|_| "Invalid attempt to write written_out in ext_network_state")?;
|
|
||||||
|
|
||||||
Ok(offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_timestamp() -> u64 {
|
|
||||||
Ok(sp_io::offchain::timestamp().unix_millis())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_sleep_until(deadline: u64) {
|
|
||||||
sp_io::offchain::sleep_until(offchain::Timestamp::from_unix_millis(deadline));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_random_seed(seed_data: Pointer<u8>) {
|
|
||||||
// NOTE the runtime as assumptions about seed size.
|
|
||||||
let seed = sp_io::offchain::random_seed();
|
|
||||||
|
|
||||||
context.write_memory(seed_data, &seed)
|
|
||||||
.map_err(|_| "Invalid attempt to set value in ext_random_seed")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_local_storage_set(
|
|
||||||
kind: u32,
|
|
||||||
key: Pointer<u8>,
|
|
||||||
key_len: WordSize,
|
|
||||||
value: Pointer<u8>,
|
|
||||||
value_len: WordSize,
|
|
||||||
) {
|
|
||||||
let kind = offchain::StorageKind::try_from(kind)
|
|
||||||
.map_err(|_| "storage kind OOB while ext_local_storage_set: wasm")?;
|
|
||||||
let key = context.read_memory(key, key_len)
|
|
||||||
.map_err(|_| "OOB while ext_local_storage_set: wasm")?;
|
|
||||||
let value = context.read_memory(value, value_len)
|
|
||||||
.map_err(|_| "OOB while ext_local_storage_set: wasm")?;
|
|
||||||
|
|
||||||
sp_io::offchain::local_storage_set(kind, &key, &value);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_local_storage_get(
|
|
||||||
kind: u32,
|
|
||||||
key: Pointer<u8>,
|
|
||||||
key_len: WordSize,
|
|
||||||
value_len: Pointer<u32>,
|
|
||||||
) -> Pointer<u8> {
|
|
||||||
let kind = offchain::StorageKind::try_from(kind)
|
|
||||||
.map_err(|_| "storage kind OOB while ext_local_storage_get: wasm")?;
|
|
||||||
let key = context.read_memory(key, key_len)
|
|
||||||
.map_err(|_| "OOB while ext_local_storage_get: wasm")?;
|
|
||||||
|
|
||||||
let maybe_value = sp_io::offchain::local_storage_get(kind, &key);
|
|
||||||
|
|
||||||
let (offset, len) = if let Some(value) = maybe_value {
|
|
||||||
let offset = context.allocate_memory(value.len() as u32)?;
|
|
||||||
context.write_memory(offset, &value)
|
|
||||||
.map_err(|_| "Invalid attempt to set memory in ext_local_storage_get")?;
|
|
||||||
(offset, value.len() as u32)
|
|
||||||
} else {
|
|
||||||
(Pointer::null(), u32::max_value())
|
|
||||||
};
|
|
||||||
|
|
||||||
context.write_primitive(value_len, len)
|
|
||||||
.map_err(|_| "Invalid attempt to write value_len in ext_local_storage_get")?;
|
|
||||||
|
|
||||||
Ok(offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_local_storage_compare_and_set(
|
|
||||||
kind: u32,
|
|
||||||
key: Pointer<u8>,
|
|
||||||
key_len: WordSize,
|
|
||||||
old_value: Pointer<u8>,
|
|
||||||
old_value_len: WordSize,
|
|
||||||
new_value: Pointer<u8>,
|
|
||||||
new_value_len: WordSize,
|
|
||||||
) -> u32 {
|
|
||||||
let kind = offchain::StorageKind::try_from(kind)
|
|
||||||
.map_err(|_| "storage kind OOB while ext_local_storage_compare_and_set: wasm")?;
|
|
||||||
let key = context.read_memory(key, key_len)
|
|
||||||
.map_err(|_| "OOB while ext_local_storage_compare_and_set: wasm")?;
|
|
||||||
let new_value = context.read_memory(new_value, new_value_len)
|
|
||||||
.map_err(|_| "OOB while ext_local_storage_compare_and_set: wasm")?;
|
|
||||||
|
|
||||||
let old_value = if old_value_len == u32::max_value() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(
|
|
||||||
context.read_memory(old_value, old_value_len)
|
|
||||||
.map_err(|_| "OOB while ext_local_storage_compare_and_set: wasm")?
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let res = sp_io::offchain::local_storage_compare_and_set(
|
|
||||||
kind,
|
|
||||||
&key,
|
|
||||||
old_value,
|
|
||||||
&new_value,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(if res { 0 } else { 1 })
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_http_request_start(
|
|
||||||
method: Pointer<u8>,
|
|
||||||
method_len: WordSize,
|
|
||||||
url: Pointer<u8>,
|
|
||||||
url_len: WordSize,
|
|
||||||
meta: Pointer<u8>,
|
|
||||||
meta_len: WordSize,
|
|
||||||
) -> u32 {
|
|
||||||
let method = context.read_memory(method, method_len)
|
|
||||||
.map_err(|_| "OOB while ext_http_request_start: wasm")?;
|
|
||||||
let url = context.read_memory(url, url_len)
|
|
||||||
.map_err(|_| "OOB while ext_http_request_start: wasm")?;
|
|
||||||
let meta = context.read_memory(meta, meta_len)
|
|
||||||
.map_err(|_| "OOB while ext_http_request_start: wasm")?;
|
|
||||||
|
|
||||||
let method_str = str::from_utf8(&method)
|
|
||||||
.map_err(|_| "invalid str while ext_http_request_start: wasm")?;
|
|
||||||
let url_str = str::from_utf8(&url)
|
|
||||||
.map_err(|_| "invalid str while ext_http_request_start: wasm")?;
|
|
||||||
|
|
||||||
let id = sp_io::offchain::http_request_start(method_str, url_str, &meta);
|
|
||||||
|
|
||||||
if let Ok(id) = id {
|
|
||||||
Ok(id.into())
|
|
||||||
} else {
|
|
||||||
Ok(u32::max_value())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_http_request_add_header(
|
|
||||||
request_id: u32,
|
|
||||||
name: Pointer<u8>,
|
|
||||||
name_len: WordSize,
|
|
||||||
value: Pointer<u8>,
|
|
||||||
value_len: WordSize,
|
|
||||||
) -> u32 {
|
|
||||||
let name = context.read_memory(name, name_len)
|
|
||||||
.map_err(|_| "OOB while ext_http_request_add_header: wasm")?;
|
|
||||||
let value = context.read_memory(value, value_len)
|
|
||||||
.map_err(|_| "OOB while ext_http_request_add_header: wasm")?;
|
|
||||||
|
|
||||||
let name_str = str::from_utf8(&name)
|
|
||||||
.map_err(|_| "Invalid str while ext_http_request_add_header: wasm")?;
|
|
||||||
let value_str = str::from_utf8(&value)
|
|
||||||
.map_err(|_| "Invalid str while ext_http_request_add_header: wasm")?;
|
|
||||||
|
|
||||||
let res = sp_io::offchain::http_request_add_header(
|
|
||||||
offchain::HttpRequestId(request_id as u16),
|
|
||||||
name_str,
|
|
||||||
value_str,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(if res.is_ok() { 0 } else { 1 })
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_http_request_write_body(
|
|
||||||
request_id: u32,
|
|
||||||
chunk: Pointer<u8>,
|
|
||||||
chunk_len: WordSize,
|
|
||||||
deadline: u64,
|
|
||||||
) -> u32 {
|
|
||||||
let chunk = context.read_memory(chunk, chunk_len)
|
|
||||||
.map_err(|_| "OOB while ext_http_request_write_body: wasm")?;
|
|
||||||
|
|
||||||
let res = sp_io::offchain::http_request_write_body(
|
|
||||||
offchain::HttpRequestId(request_id as u16),
|
|
||||||
&chunk,
|
|
||||||
deadline_to_timestamp(deadline),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(match res {
|
|
||||||
Ok(()) => 0,
|
|
||||||
Err(e) => e.into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_http_response_wait(
|
|
||||||
ids: Pointer<u32>,
|
|
||||||
ids_len: WordSize,
|
|
||||||
statuses: Pointer<u32>,
|
|
||||||
deadline: u64,
|
|
||||||
) {
|
|
||||||
let ids = (0..ids_len)
|
|
||||||
.map(|i|
|
|
||||||
context.read_primitive(ids.offset(i).ok_or("Point overflow")?)
|
|
||||||
.map(|id: u32| offchain::HttpRequestId(id as u16))
|
|
||||||
.map_err(|_| "OOB while ext_http_response_wait: wasm")
|
|
||||||
)
|
|
||||||
.collect::<std::result::Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
let res = sp_io::offchain::http_response_wait(&ids, deadline_to_timestamp(deadline))
|
|
||||||
.into_iter()
|
|
||||||
.map(|status| u32::from(status))
|
|
||||||
.enumerate()
|
|
||||||
// make sure to take up to `ids_len` to avoid exceeding the mem.
|
|
||||||
.take(ids_len as usize);
|
|
||||||
|
|
||||||
for (i, status) in res {
|
|
||||||
context.write_primitive(statuses.offset(i as u32).ok_or("Point overflow")?, status)
|
|
||||||
.map_err(|_| "Invalid attempt to set memory in ext_http_response_wait")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_http_response_headers(
|
|
||||||
request_id: u32,
|
|
||||||
written_out: Pointer<u32>,
|
|
||||||
) -> Pointer<u8> {
|
|
||||||
use codec::Encode;
|
|
||||||
|
|
||||||
let headers = sp_io::offchain::http_response_headers(
|
|
||||||
offchain::HttpRequestId(request_id as u16),
|
|
||||||
);
|
|
||||||
|
|
||||||
let encoded = headers.encode();
|
|
||||||
let len = encoded.len() as u32;
|
|
||||||
let offset = context.allocate_memory(len)?;
|
|
||||||
|
|
||||||
context.write_memory(offset, &encoded)
|
|
||||||
.map_err(|_| "Invalid attempt to set memory in ext_http_response_headers")?;
|
|
||||||
context.write_primitive(written_out, len)
|
|
||||||
.map_err(|_| "Invalid attempt to write written_out in ext_http_response_headers")?;
|
|
||||||
|
|
||||||
Ok(offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
ext_http_response_read_body(
|
|
||||||
request_id: u32,
|
|
||||||
buffer: Pointer<u8>,
|
|
||||||
buffer_len: WordSize,
|
|
||||||
deadline: u64,
|
|
||||||
) -> WordSize {
|
|
||||||
let mut internal_buffer = Vec::with_capacity(buffer_len as usize);
|
|
||||||
internal_buffer.resize(buffer_len as usize, 0);
|
|
||||||
|
|
||||||
let res = sp_io::offchain::http_response_read_body(
|
|
||||||
offchain::HttpRequestId(request_id as u16),
|
|
||||||
&mut internal_buffer,
|
|
||||||
deadline_to_timestamp(deadline),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(match res {
|
|
||||||
Ok(read) => {
|
|
||||||
context.write_memory(buffer, &internal_buffer[..read as usize])
|
|
||||||
.map_err(|_| "Invalid attempt to set memory in ext_http_response_read_body")?;
|
|
||||||
|
|
||||||
read as u32
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
u32::max_value() - u32::from(err) + 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deadline_to_timestamp(deadline: u64) -> Option<offchain::Timestamp> {
|
|
||||||
if deadline == 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(offchain::Timestamp::from_unix_millis(deadline))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,11 +29,8 @@
|
|||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![recursion_limit="128"]
|
#![recursion_limit="128"]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod wasm_utils;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod native_executor;
|
mod native_executor;
|
||||||
pub mod deprecated_host_interface;
|
|
||||||
mod wasm_runtime;
|
mod wasm_runtime;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod integration_tests;
|
mod integration_tests;
|
||||||
|
|||||||
@@ -101,10 +101,6 @@ impl<D: NativeExecutionDispatch> NativeExecutor<D> {
|
|||||||
/// Defaults to `DEFAULT_HEAP_PAGES` if `None` is provided.
|
/// Defaults to `DEFAULT_HEAP_PAGES` if `None` is provided.
|
||||||
pub fn new(fallback_method: WasmExecutionMethod, default_heap_pages: Option<u64>) -> Self {
|
pub fn new(fallback_method: WasmExecutionMethod, default_heap_pages: Option<u64>) -> Self {
|
||||||
let mut host_functions = sp_io::SubstrateHostFunctions::host_functions();
|
let mut host_functions = sp_io::SubstrateHostFunctions::host_functions();
|
||||||
// Add the old and deprecated host functions as well, so that we support old wasm runtimes.
|
|
||||||
host_functions.extend(
|
|
||||||
crate::deprecated_host_interface::SubstrateExternals::host_functions(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add the custom host functions provided by the user.
|
// Add the custom host functions provided by the user.
|
||||||
host_functions.extend(D::ExtendHostFunctions::host_functions());
|
host_functions.extend(D::ExtendHostFunctions::host_functions());
|
||||||
|
|||||||
@@ -1,173 +0,0 @@
|
|||||||
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Substrate is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// Substrate is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Utilities for defining the wasm host environment.
|
|
||||||
|
|
||||||
/// Converts arguments into respective WASM types.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! convert_args {
|
|
||||||
() => ([]);
|
|
||||||
( $( $t:ty ),* ) => ( [ $( <$t as $crate::sp_wasm_interface::IntoValue>::VALUE_TYPE, )* ] );
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a WASM signature for given list of parameters.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! gen_signature {
|
|
||||||
( ( $( $params: ty ),* ) ) => (
|
|
||||||
$crate::sp_wasm_interface::Signature {
|
|
||||||
args: std::borrow::Cow::Borrowed(&convert_args!( $( $params ),* )[..]),
|
|
||||||
return_value: None,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
( ( $( $params: ty ),* ) -> $returns:ty ) => (
|
|
||||||
$crate::sp_wasm_interface::Signature {
|
|
||||||
args: std::borrow::Cow::Borrowed(&convert_args!( $( $params ),* )[..]),
|
|
||||||
return_value: Some(<$returns as $crate::sp_wasm_interface::IntoValue>::VALUE_TYPE),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! gen_functions {
|
|
||||||
(@INTERNAL
|
|
||||||
{ $( $generated:tt )* }
|
|
||||||
$context:ident,
|
|
||||||
) => (
|
|
||||||
vec![ $( $generated )* ]
|
|
||||||
);
|
|
||||||
(@INTERNAL
|
|
||||||
{ $( $generated:tt )* }
|
|
||||||
$context:ident,
|
|
||||||
$name:ident ( $( $names:ident: $params:ty ),* ) $( -> $returns:ty )? { $( $body:tt )* }
|
|
||||||
$( $tail:tt )*
|
|
||||||
) => (
|
|
||||||
gen_functions! {
|
|
||||||
@INTERNAL
|
|
||||||
{
|
|
||||||
$( $generated )*
|
|
||||||
{
|
|
||||||
struct $name;
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
impl $crate::sp_wasm_interface::Function for $name {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
stringify!($name)
|
|
||||||
}
|
|
||||||
fn signature(&self) -> $crate::sp_wasm_interface::Signature {
|
|
||||||
gen_signature!( ( $( $params ),* ) $( -> $returns )? )
|
|
||||||
}
|
|
||||||
fn execute(
|
|
||||||
&self,
|
|
||||||
context: &mut dyn $crate::sp_wasm_interface::FunctionContext,
|
|
||||||
args: &mut dyn Iterator<Item=$crate::sp_wasm_interface::Value>,
|
|
||||||
) -> ::std::result::Result<Option<$crate::sp_wasm_interface::Value>, String> {
|
|
||||||
let mut $context = context;
|
|
||||||
marshall! {
|
|
||||||
args,
|
|
||||||
( $( $names : $params ),* ) $( -> $returns )? => { $( $body )* }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&$name as &dyn $crate::sp_wasm_interface::Function
|
|
||||||
},
|
|
||||||
}
|
|
||||||
$context,
|
|
||||||
$( $tail )*
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
( $context:ident, $( $tail:tt )* ) => (
|
|
||||||
gen_functions!(@INTERNAL {} $context, $($tail)*);
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts the list of arguments coming from WASM into their native types.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! unmarshall_args {
|
|
||||||
( $body:tt, $args_iter:ident, $( $names:ident : $params:ty ),*) => ({
|
|
||||||
$(
|
|
||||||
let $names : $params =
|
|
||||||
$args_iter.next()
|
|
||||||
.and_then(|val| <$params as $crate::sp_wasm_interface::TryFromValue>::try_from_value(val))
|
|
||||||
.expect(
|
|
||||||
"`$args_iter` comes from an argument of Externals::execute_function;
|
|
||||||
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
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Since we can't specify the type of closure directly at binding site:
|
|
||||||
///
|
|
||||||
/// ```nocompile
|
|
||||||
/// 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, String>
|
|
||||||
{
|
|
||||||
f
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pass the list of parameters by converting them to respective WASM types.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! marshall {
|
|
||||||
( $args_iter:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({
|
|
||||||
let body = $crate::wasm_utils::constrain_closure::<$returns, _>(|| {
|
|
||||||
unmarshall_args!($body, $args_iter, $( $names : $params ),*)
|
|
||||||
});
|
|
||||||
let r = body()?;
|
|
||||||
return Ok(Some($crate::sp_wasm_interface::IntoValue::into_value(r)))
|
|
||||||
});
|
|
||||||
( $args_iter:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({
|
|
||||||
let body = $crate::wasm_utils::constrain_closure::<(), _>(|| {
|
|
||||||
unmarshall_args!($body, $args_iter, $( $names : $params ),*)
|
|
||||||
});
|
|
||||||
body()?;
|
|
||||||
return Ok(None)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implements the wasm host interface for the given type.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! impl_wasm_host_interface {
|
|
||||||
(
|
|
||||||
impl $interface_name:ident where $context:ident {
|
|
||||||
$(
|
|
||||||
$name:ident($( $names:ident : $params:ty ),* $(,)? ) $( -> $returns:ty )?
|
|
||||||
{ $( $body:tt )* }
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
) => (
|
|
||||||
impl $crate::sp_wasm_interface::HostFunctions for $interface_name {
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
fn host_functions() -> Vec<&'static dyn $crate::sp_wasm_interface::Function> {
|
|
||||||
gen_functions!(
|
|
||||||
$context,
|
|
||||||
$( $name( $( $names: $params ),* ) $( -> $returns )? { $( $body )* } )*
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -915,6 +915,56 @@ pub fn oom(_: core::alloc::Layout) -> ! {
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub type TestExternalities = sp_state_machine::TestExternalities<sp_core::Blake2Hasher, u64>;
|
pub type TestExternalities = sp_state_machine::TestExternalities<sp_core::Blake2Hasher, u64>;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
mod ext_blake2_256 {
|
||||||
|
use sp_wasm_interface::{Signature, Function, HostFunctions, ValueType, Value, FunctionContext};
|
||||||
|
|
||||||
|
/// There is a custom `extern function` in `sp_core::hasher` for `ext_blake2_256` hasher. This
|
||||||
|
/// custom extern was missed to remove and requires us to support this now. This type is a custom
|
||||||
|
/// implementation for the wasm function in native.
|
||||||
|
pub struct ExtBlake2_256;
|
||||||
|
|
||||||
|
impl HostFunctions for ExtBlake2_256 {
|
||||||
|
fn host_functions() -> Vec<&'static dyn Function> {
|
||||||
|
vec![&ExtBlake2_256]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Function for ExtBlake2_256 {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"ext_blake2_256"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::new_with_args(&[ValueType::I32, ValueType::I32, ValueType::I32][..])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute(
|
||||||
|
&self,
|
||||||
|
context: &mut dyn FunctionContext,
|
||||||
|
args: &mut dyn Iterator<Item = Value>,
|
||||||
|
) -> sp_wasm_interface::Result<Option<Value>> {
|
||||||
|
let data = args.next().and_then(|v| v.as_i32())
|
||||||
|
.ok_or_else(|| "`data` not present or not an `i32`")? as u32;
|
||||||
|
let len = args.next().and_then(|v| v.as_i32())
|
||||||
|
.ok_or_else(|| "`len` not present or not an `i32`")? as u32;
|
||||||
|
let out = args.next().and_then(|v| v.as_i32())
|
||||||
|
.ok_or_else(|| "`out` not present or not an `i32`")? as u32;
|
||||||
|
|
||||||
|
let result: [u8; 32] = if len == 0 {
|
||||||
|
sp_core::hashing::blake2_256(&[0u8; 0])
|
||||||
|
} else {
|
||||||
|
let mem = context.read_memory(data.into(), len)
|
||||||
|
.map_err(|_| "Invalid attempt to get data in ext_blake2_256")?;
|
||||||
|
sp_core::hashing::blake2_256(&mem)
|
||||||
|
};
|
||||||
|
context.write_memory(out.into(), &result)
|
||||||
|
.map_err(|_| "Invalid attempt to set result in ext_blake2_256")?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The host functions Substrate provides for the Wasm runtime environment.
|
/// The host functions Substrate provides for the Wasm runtime environment.
|
||||||
///
|
///
|
||||||
/// All these host functions will be callable from inside the Wasm environment.
|
/// All these host functions will be callable from inside the Wasm environment.
|
||||||
@@ -929,6 +979,7 @@ pub type SubstrateHostFunctions = (
|
|||||||
logging::HostFunctions,
|
logging::HostFunctions,
|
||||||
sandbox::HostFunctions,
|
sandbox::HostFunctions,
|
||||||
crate::trie::HostFunctions,
|
crate::trie::HostFunctions,
|
||||||
|
ext_blake2_256::ExtBlake2_256,
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -105,23 +105,6 @@ pub trait TestApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Two random external functions from the old runtime interface.
|
|
||||||
/// This ensures that we still inherently export these functions from the host and that we are still
|
|
||||||
/// compatible with old wasm runtimes.
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
extern "C" {
|
|
||||||
pub fn ext_clear_storage(key_data: *const u8, key_len: u32);
|
|
||||||
pub fn ext_keccak_256(data: *const u8, len: u32, out: *mut u8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make sure the old runtime interface needs to be imported.
|
|
||||||
#[no_mangle]
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
pub fn force_old_runtime_interface_import() {
|
|
||||||
unsafe { ext_clear_storage(sp_std::ptr::null(), 0); }
|
|
||||||
unsafe { ext_keccak_256(sp_std::ptr::null(), 0, sp_std::ptr::null_mut()); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function is not used, but we require it for the compiler to include `sp-io`.
|
/// This function is not used, but we require it for the compiler to include `sp-io`.
|
||||||
/// `sp-io` is required for its panic and oom handler.
|
/// `sp-io` is required for its panic and oom handler.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -248,4 +231,14 @@ wasm_export_functions! {
|
|||||||
}
|
}
|
||||||
assert_eq!(0, len);
|
assert_eq!(0, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_ext_blake2_256() {
|
||||||
|
use sp_core::Hasher;
|
||||||
|
|
||||||
|
let data = "hey, hash me please!";
|
||||||
|
let hash = sp_core::Blake2Hasher::hash(data.as_bytes());
|
||||||
|
|
||||||
|
let expected = sp_io::hashing::blake2_256(data.as_bytes());
|
||||||
|
assert_eq!(&expected, hash.as_ref());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ fn call_wasm_method<HF: HostFunctionsT>(method: &str) -> TestExternalities {
|
|||||||
(
|
(
|
||||||
HF,
|
HF,
|
||||||
sp_io::SubstrateHostFunctions,
|
sp_io::SubstrateHostFunctions,
|
||||||
sc_executor::deprecated_host_interface::SubstrateExternals
|
|
||||||
)
|
)
|
||||||
>(
|
>(
|
||||||
method,
|
method,
|
||||||
@@ -128,3 +127,8 @@ fn test_encoded_return_value_memory_is_freed() {
|
|||||||
fn test_array_return_value_memory_is_freed() {
|
fn test_array_return_value_memory_is_freed() {
|
||||||
call_wasm_method::<HostFunctions>("test_array_return_value_memory_is_freed");
|
call_wasm_method::<HostFunctions>("test_array_return_value_memory_is_freed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ext_blake2_256() {
|
||||||
|
call_wasm_method::<HostFunctions>("test_ext_blake2_256");
|
||||||
|
}
|
||||||
|
|||||||
@@ -170,6 +170,12 @@ impl<T: PointerType> Pointer<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: PointerType> From<u32> for Pointer<T> {
|
||||||
|
fn from(ptr: u32) -> Self {
|
||||||
|
Pointer::new(ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: PointerType> From<Pointer<T>> for u32 {
|
impl<T: PointerType> From<Pointer<T>> for u32 {
|
||||||
fn from(ptr: Pointer<T>) -> Self {
|
fn from(ptr: Pointer<T>) -> Self {
|
||||||
ptr.ptr
|
ptr.ptr
|
||||||
|
|||||||
Reference in New Issue
Block a user