// Copyright 2017-2019 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 . //! Definition and implementation of the old and deprecated Substrate runtime interface for the host. use codec::Encode; use std::{convert::TryFrom, str}; use primitives::{ blake2_128, blake2_256, twox_64, twox_128, twox_256, ed25519, sr25519, keccak_256, Blake2Hasher, Pair, crypto::KeyTypeId, offchain, }; use trie::{TrieConfiguration, trie_types::Layout}; use 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, sig_data: Pointer, ) -> WResult { 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 { let r = context.allocate_memory(size)?; debug_trace!(target: "sp-io", "malloc {} bytes at {:?}", size, r); Ok(r) } ext_free(addr: Pointer) { context.deallocate_memory(addr)?; debug_trace!(target: "sp-io", "free {:?}", addr); Ok(()) } ext_sandbox_instantiate( dispatch_thunk_idx: u32, wasm_ptr: Pointer, wasm_len: WordSize, imports_ptr: Pointer, 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, export_len: WordSize, args_ptr: Pointer, args_len: WordSize, return_val_ptr: Pointer, 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, 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, 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, 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, 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, target_len: WordSize, message_data: Pointer, 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, key_len: WordSize, value_data: Pointer, 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_set_child_storage( storage_key_data: Pointer, storage_key_len: WordSize, key_data: Pointer, key_len: WordSize, value_data: Pointer, value_len: WordSize, ) { let storage_key = context.read_memory(storage_key_data, storage_key_len) .map_err(|_| "Invalid attempt to determine storage_key in ext_set_child_storage")?; let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_set_child_storage")?; let value = context.read_memory(value_data, value_len) .map_err(|_| "Invalid attempt to determine value in ext_set_child_storage")?; Ok(sp_io::storage::child_set(&storage_key, &key, &value)) } ext_clear_child_storage( storage_key_data: Pointer, storage_key_len: WordSize, key_data: Pointer, key_len: WordSize, ) { let storage_key = context.read_memory(storage_key_data, storage_key_len) .map_err(|_| "Invalid attempt to determine storage_key in ext_clear_child_storage")?; let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_clear_child_storage")?; Ok(sp_io::storage::child_clear(&storage_key, &key)) } ext_clear_storage(key_data: Pointer, 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, 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_exists_child_storage( storage_key_data: Pointer, storage_key_len: WordSize, key_data: Pointer, key_len: WordSize, ) -> u32 { let storage_key = context.read_memory(storage_key_data, storage_key_len) .map_err(|_| "Invalid attempt to determine storage_key in ext_exists_child_storage")?; let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_exists_child_storage")?; Ok(if sp_io::storage::child_exists(&storage_key, &key) { 1 } else { 0 }) } ext_clear_prefix(prefix_data: Pointer, 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_clear_child_prefix( storage_key_data: Pointer, storage_key_len: WordSize, prefix_data: Pointer, prefix_len: WordSize, ) { let storage_key = context.read_memory(storage_key_data, storage_key_len) .map_err(|_| "Invalid attempt to determine storage_key in ext_clear_child_prefix")?; let prefix = context.read_memory(prefix_data, prefix_len) .map_err(|_| "Invalid attempt to determine prefix in ext_clear_child_prefix")?; Ok(sp_io::storage::child_clear_prefix(&storage_key, &prefix)) } ext_kill_child_storage(storage_key_data: Pointer, storage_key_len: WordSize) { let storage_key = context.read_memory(storage_key_data, storage_key_len) .map_err(|_| "Invalid attempt to determine storage_key in ext_kill_child_storage")?; Ok(sp_io::storage::child_storage_kill(&storage_key)) } ext_get_allocated_storage( key_data: Pointer, key_len: WordSize, written_out: Pointer, ) -> Pointer { 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_allocated_child_storage( storage_key_data: Pointer, storage_key_len: WordSize, key_data: Pointer, key_len: WordSize, written_out: Pointer, ) -> Pointer { let storage_key = context.read_memory(storage_key_data, storage_key_len) .map_err(|_| "Invalid attempt to determine storage_key in ext_get_allocated_child_storage")?; let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to determine key in ext_get_allocated_child_storage")?; if let Some(value) = sp_io::storage::child_get(&storage_key, &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_child_storage")?; context.write_primitive(written_out, value.len() as u32) .map_err(|_| "Invalid attempt to write written_out in ext_get_allocated_child_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_child_storage")?; Ok(Pointer::null()) } } ext_get_storage_into( key_data: Pointer, key_len: WordSize, value_data: Pointer, 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_get_child_storage_into( storage_key_data: Pointer, storage_key_len: WordSize, key_data: Pointer, key_len: WordSize, value_data: Pointer, value_len: WordSize, value_offset: WordSize, ) -> WordSize { let storage_key = context.read_memory(storage_key_data, storage_key_len) .map_err(|_| "Invalid attempt to determine storage_key in ext_get_child_storage_into")?; let key = context.read_memory(key_data, key_len) .map_err(|_| "Invalid attempt to get key in ext_get_child_storage_into")?; if let Some(value) = sp_io::storage::child_get(&storage_key, &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 get value in ext_get_child_storage_into")?; Ok(value.len() as u32) } else { Ok(u32::max_value()) } } ext_storage_root(result: Pointer) { context.write_memory(result, sp_io::storage::root().as_ref()) .map_err(|_| "Invalid attempt to set memory in ext_storage_root".into()) } ext_child_storage_root( storage_key_data: Pointer, storage_key_len: WordSize, written_out: Pointer, ) -> Pointer { let storage_key = context.read_memory(storage_key_data, storage_key_len) .map_err(|_| "Invalid attempt to determine storage_key in ext_child_storage_root")?; let value = sp_io::storage::child_root(&storage_key); let offset = context.allocate_memory(value.len() as u32)?; context.write_memory(offset, &value) .map_err(|_| "Invalid attempt to set memory in ext_child_storage_root")?; context.write_primitive(written_out, value.len() as u32) .map_err(|_| "Invalid attempt to write written_out in ext_child_storage_root")?; Ok(offset) } ext_storage_changes_root( parent_hash_data: Pointer, _len: WordSize, result: Pointer, ) -> 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, lens_data: Pointer, lens_len: WordSize, result: Pointer, ) { let values = (0..lens_len) .map(|i| context.read_primitive(lens_data.offset(i).ok_or("Pointer overflow")?)) .collect::, _>>()? .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::, _>>()?; let r = Layout::::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, len: WordSize, out: Pointer) { 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, len: WordSize, out: Pointer) { 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, len: WordSize, out: Pointer) { 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, len: WordSize, out: Pointer) { 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, len: WordSize, out: Pointer) { 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, len: WordSize, out: Pointer) { 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, result_len: Pointer) -> Pointer { 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, msg_len: WordSize, sig_data: Pointer, pubkey_data: Pointer, ) -> 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, seed: Pointer, seed_len: WordSize, out: Pointer, ) { 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, pubkey_data: Pointer, msg_data: Pointer, msg_len: WordSize, out: Pointer, ) -> 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, result_len: Pointer) -> Pointer { 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, msg_len: WordSize, sig_data: Pointer, pubkey_data: Pointer, ) -> 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, seed: Pointer, seed_len: WordSize, out: Pointer, ) { 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, pubkey_data: Pointer, msg_data: Pointer, msg_len: WordSize, out: Pointer, ) -> 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, sig_data: Pointer, pubkey_data: Pointer, ) -> 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, sig_data: Pointer, pubkey_data: Pointer, ) -> 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, 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) -> Pointer { 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) { // 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, key_len: WordSize, value: Pointer, 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, key_len: WordSize, value_len: Pointer, ) -> Pointer { 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, key_len: WordSize, old_value: Pointer, old_value_len: WordSize, new_value: Pointer, 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, method_len: WordSize, url: Pointer, url_len: WordSize, meta: Pointer, 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, name_len: WordSize, value: Pointer, 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, 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, ids_len: WordSize, statuses: Pointer, 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::, _>>()?; 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, ) -> Pointer { 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, 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 { if deadline == 0 { None } else { Some(offchain::Timestamp::from_unix_millis(deadline)) } }