#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(feature = "std"), feature(alloc_error_handler))]
#![cfg_attr(feature = "std",
doc = "Substrate runtime standard library as compiled when linked with Rust's standard library.")]
#![cfg_attr(not(feature = "std"),
doc = "Substrate's runtime standard library as compiled without Rust's standard library.")]
use sp_std::vec::Vec;
#[cfg(feature = "std")]
use sp_std::ops::Deref;
#[cfg(feature = "std")]
use tracing;
#[cfg(feature = "std")]
use sp_core::{
crypto::Pair,
traits::{KeystoreExt, CallInWasmExt, TaskExecutorExt},
offchain::{OffchainExt, TransactionPoolExt},
hexdisplay::HexDisplay,
storage::ChildInfo,
};
use sp_core::{
OpaquePeerId, crypto::KeyTypeId, ed25519, sr25519, ecdsa, H256, LogLevel,
offchain::{
Timestamp, HttpRequestId, HttpRequestStatus, HttpError, StorageKind, OpaqueNetworkState,
},
};
#[cfg(feature = "std")]
use sp_trie::{TrieConfiguration, trie_types::Layout};
use sp_runtime_interface::{runtime_interface, Pointer};
use sp_runtime_interface::pass_by::PassBy;
use codec::{Encode, Decode};
#[cfg(feature = "std")]
use sp_externalities::{ExternalitiesExt, Externalities};
#[cfg(feature = "std")]
mod batch_verifier;
#[cfg(feature = "std")]
use batch_verifier::BatchVerifier;
#[derive(Encode, Decode)]
pub enum EcdsaVerifyError {
BadRS,
BadV,
BadSignature,
}
#[runtime_interface]
pub trait Storage {
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
self.storage(key).map(|s| s.to_vec())
}
fn read(&self, key: &[u8], value_out: &mut [u8], value_offset: u32) -> Option<u32> {
self.storage(key).map(|value| {
let value_offset = value_offset as usize;
let data = &value[value_offset.min(value.len())..];
let written = std::cmp::min(data.len(), value_out.len());
value_out[..written].copy_from_slice(&data[..written]);
data.len() as u32
})
}
fn set(&mut self, key: &[u8], value: &[u8]) {
self.set_storage(key.to_vec(), value.to_vec());
}
fn clear(&mut self, key: &[u8]) {
self.clear_storage(key)
}
fn exists(&self, key: &[u8]) -> bool {
self.exists_storage(key)
}
fn clear_prefix(&mut self, prefix: &[u8]) {
Externalities::clear_prefix(*self, prefix)
}
fn append(&mut self, key: &[u8], value: Vec<u8>) {
self.storage_append(key.to_vec(), value);
}
fn root(&mut self) -> Vec<u8> {
self.storage_root()
}
fn changes_root(&mut self, parent_hash: &[u8]) -> Option<Vec<u8>> {
self.storage_changes_root(parent_hash)
.expect("Invalid `parent_hash` given to `changes_root`.")
}
fn next_key(&mut self, key: &[u8]) -> Option<Vec<u8>> {
self.next_storage_key(&key)
}
fn start_transaction(&mut self) {
self.storage_start_transaction();
}
fn rollback_transaction(&mut self) {
self.storage_rollback_transaction()
.expect("No open transaction that can be rolled back.");
}
fn commit_transaction(&mut self) {
self.storage_commit_transaction()
.expect("No open transaction that can be committed.");
}
}
#[runtime_interface]
pub trait DefaultChildStorage {
fn get(
&self,
storage_key: &[u8],
key: &[u8],
) -> Option<Vec<u8>> {
let child_info = ChildInfo::new_default(storage_key);
self.child_storage(&child_info, key).map(|s| s.to_vec())
}
fn read(
&self,
storage_key: &[u8],
key: &[u8],
value_out: &mut [u8],
value_offset: u32,
) -> Option<u32> {
let child_info = ChildInfo::new_default(storage_key);
self.child_storage(&child_info, key)
.map(|value| {
let value_offset = value_offset as usize;
let data = &value[value_offset.min(value.len())..];
let written = std::cmp::min(data.len(), value_out.len());
value_out[..written].copy_from_slice(&data[..written]);
data.len() as u32
})
}
fn set(
&mut self,
storage_key: &[u8],
key: &[u8],
value: &[u8],
) {
let child_info = ChildInfo::new_default(storage_key);
self.set_child_storage(&child_info, key.to_vec(), value.to_vec());
}
fn clear(
&mut self,
storage_key: &[u8],
key: &[u8],
) {
let child_info = ChildInfo::new_default(storage_key);
self.clear_child_storage(&child_info, key);
}
fn storage_kill(
&mut self,
storage_key: &[u8],
) {
let child_info = ChildInfo::new_default(storage_key);
self.kill_child_storage(&child_info);
}
fn exists(
&self,
storage_key: &[u8],
key: &[u8],
) -> bool {
let child_info = ChildInfo::new_default(storage_key);
self.exists_child_storage(&child_info, key)
}
fn clear_prefix(
&mut self,
storage_key: &[u8],
prefix: &[u8],
) {
let child_info = ChildInfo::new_default(storage_key);
self.clear_child_prefix(&child_info, prefix);
}
fn root(
&mut self,
storage_key: &[u8],
) -> Vec<u8> {
let child_info = ChildInfo::new_default(storage_key);
self.child_storage_root(&child_info)
}
fn next_key(
&mut self,
storage_key: &[u8],
key: &[u8],
) -> Option<Vec<u8>> {
let child_info = ChildInfo::new_default(storage_key);
self.next_child_storage_key(&child_info, key)
}
}
#[runtime_interface]
pub trait Trie {
fn blake2_256_root(input: Vec<(Vec<u8>, Vec<u8>)>) -> H256 {
Layout::<sp_core::Blake2Hasher>::trie_root(input)
}
fn blake2_256_ordered_root(input: Vec<Vec<u8>>) -> H256 {
Layout::<sp_core::Blake2Hasher>::ordered_trie_root(input)
}
fn keccak_256_root(input: Vec<(Vec<u8>, Vec<u8>)>) -> H256 {
Layout::<sp_core::KeccakHasher>::trie_root(input)
}
fn keccak_256_ordered_root(input: Vec<Vec<u8>>) -> H256 {
Layout::<sp_core::KeccakHasher>::ordered_trie_root(input)
}
}
#[runtime_interface]
pub trait Misc {
fn chain_id(&self) -> u64 {
sp_externalities::Externalities::chain_id(*self)
}
fn print_num(val: u64) {
log::debug!(target: "runtime", "{}", val);
}
fn print_utf8(utf8: &[u8]) {
if let Ok(data) = std::str::from_utf8(utf8) {
log::debug!(target: "runtime", "{}", data)
}
}
fn print_hex(data: &[u8]) {
log::debug!(target: "runtime", "{}", HexDisplay::from(&data));
}
fn runtime_version(&mut self, wasm: &[u8]) -> Option<Vec<u8>> {
let mut ext = sp_state_machine::BasicExternalities::default();
self.extension::<CallInWasmExt>()
.expect("No `CallInWasmExt` associated for the current context!")
.call_in_wasm(
wasm,
None,
"Core_version",
&[],
&mut ext,
sp_core::traits::MissingHostFunctions::Allow,
)
.ok()
}
}
#[runtime_interface]
pub trait Crypto {
fn ed25519_public_keys(&mut self, id: KeyTypeId) -> Vec<ed25519::Public> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.read()
.ed25519_public_keys(id)
}
fn ed25519_generate(&mut self, id: KeyTypeId, seed: Option<Vec<u8>>) -> ed25519::Public {
let seed = seed.as_ref().map(|s| std::str::from_utf8(&s).expect("Seed is valid utf8!"));
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.write()
.ed25519_generate_new(id, seed)
.expect("`ed25519_generate` failed")
}
fn ed25519_sign(
&mut self,
id: KeyTypeId,
pub_key: &ed25519::Public,
msg: &[u8],
) -> Option<ed25519::Signature> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.read()
.sign_with(id, &pub_key.into(), msg)
.map(|sig| ed25519::Signature::from_slice(sig.as_slice()))
.ok()
}
fn ed25519_verify(
sig: &ed25519::Signature,
msg: &[u8],
pub_key: &ed25519::Public,
) -> bool {
ed25519::Pair::verify(sig, msg, pub_key)
}
fn ed25519_batch_verify(
&mut self,
sig: &ed25519::Signature,
msg: &[u8],
pub_key: &ed25519::Public,
) -> bool {
self.extension::<VerificationExt>().map(
|extension| extension.push_ed25519(sig.clone(), pub_key.clone(), msg.to_vec())
).unwrap_or_else(|| ed25519_verify(sig, msg, pub_key))
}
#[version(2)]
fn sr25519_verify(
sig: &sr25519::Signature,
msg: &[u8],
pub_key: &sr25519::Public,
) -> bool {
sr25519::Pair::verify(sig, msg, pub_key)
}
fn sr25519_batch_verify(
&mut self,
sig: &sr25519::Signature,
msg: &[u8],
pub_key: &sr25519::Public,
) -> bool {
self.extension::<VerificationExt>().map(
|extension| extension.push_sr25519(sig.clone(), pub_key.clone(), msg.to_vec())
).unwrap_or_else(|| sr25519_verify(sig, msg, pub_key))
}
fn start_batch_verify(&mut self) {
let scheduler = self.extension::<TaskExecutorExt>()
.expect("No task executor associated with the current context!")
.0
.clone();
self.register_extension(VerificationExt(BatchVerifier::new(scheduler)))
.expect("Failed to register required extension: `VerificationExt`");
}
fn finish_batch_verify(&mut self) -> bool {
let result = self.extension::<VerificationExt>()
.expect("`finish_batch_verify` should only be called after `start_batch_verify`")
.verify_and_clear();
self.deregister_extension::<VerificationExt>()
.expect("No verification extension in current context!");
result
}
fn sr25519_public_keys(&mut self, id: KeyTypeId) -> Vec<sr25519::Public> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.read()
.sr25519_public_keys(id)
}
fn sr25519_generate(&mut self, id: KeyTypeId, seed: Option<Vec<u8>>) -> sr25519::Public {
let seed = seed.as_ref().map(|s| std::str::from_utf8(&s).expect("Seed is valid utf8!"));
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.write()
.sr25519_generate_new(id, seed)
.expect("`sr25519_generate` failed")
}
fn sr25519_sign(
&mut self,
id: KeyTypeId,
pub_key: &sr25519::Public,
msg: &[u8],
) -> Option<sr25519::Signature> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.read()
.sign_with(id, &pub_key.into(), msg)
.map(|sig| sr25519::Signature::from_slice(sig.as_slice()))
.ok()
}
fn sr25519_verify(sig: &sr25519::Signature, msg: &[u8], pubkey: &sr25519::Public) -> bool {
sr25519::Pair::verify_deprecated(sig, msg, pubkey)
}
fn ecdsa_public_keys(&mut self, id: KeyTypeId) -> Vec<ecdsa::Public> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.read()
.ecdsa_public_keys(id)
}
fn ecdsa_generate(&mut self, id: KeyTypeId, seed: Option<Vec<u8>>) -> ecdsa::Public {
let seed = seed.as_ref().map(|s| std::str::from_utf8(&s).expect("Seed is valid utf8!"));
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.write()
.ecdsa_generate_new(id, seed)
.expect("`ecdsa_generate` failed")
}
fn ecdsa_sign(
&mut self,
id: KeyTypeId,
pub_key: &ecdsa::Public,
msg: &[u8],
) -> Option<ecdsa::Signature> {
self.extension::<KeystoreExt>()
.expect("No `keystore` associated for the current context!")
.read()
.sign_with(id, &pub_key.into(), msg)
.map(|sig| ecdsa::Signature::from_slice(sig.as_slice()))
.ok()
}
fn ecdsa_verify(
sig: &ecdsa::Signature,
msg: &[u8],
pub_key: &ecdsa::Public,
) -> bool {
ecdsa::Pair::verify(sig, msg, pub_key)
}
fn ecdsa_batch_verify(
&mut self,
sig: &ecdsa::Signature,
msg: &[u8],
pub_key: &ecdsa::Public,
) -> bool {
self.extension::<VerificationExt>().map(
|extension| extension.push_ecdsa(sig.clone(), pub_key.clone(), msg.to_vec())
).unwrap_or_else(|| ecdsa_verify(sig, msg, pub_key))
}
fn secp256k1_ecdsa_recover(
sig: &[u8; 65],
msg: &[u8; 32],
) -> Result<[u8; 64], EcdsaVerifyError> {
let rs = secp256k1::Signature::parse_slice(&sig[0..64])
.map_err(|_| EcdsaVerifyError::BadRS)?;
let v = secp256k1::RecoveryId::parse(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8)
.map_err(|_| EcdsaVerifyError::BadV)?;
let pubkey = secp256k1::recover(&secp256k1::Message::parse(msg), &rs, &v)
.map_err(|_| EcdsaVerifyError::BadSignature)?;
let mut res = [0u8; 64];
res.copy_from_slice(&pubkey.serialize()[1..65]);
Ok(res)
}
fn secp256k1_ecdsa_recover_compressed(
sig: &[u8; 65],
msg: &[u8; 32],
) -> Result<[u8; 33], EcdsaVerifyError> {
let rs = secp256k1::Signature::parse_slice(&sig[0..64])
.map_err(|_| EcdsaVerifyError::BadRS)?;
let v = secp256k1::RecoveryId::parse(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8)
.map_err(|_| EcdsaVerifyError::BadV)?;
let pubkey = secp256k1::recover(&secp256k1::Message::parse(msg), &rs, &v)
.map_err(|_| EcdsaVerifyError::BadSignature)?;
Ok(pubkey.serialize_compressed())
}
}
#[runtime_interface]
pub trait Hashing {
fn keccak_256(data: &[u8]) -> [u8; 32] {
sp_core::hashing::keccak_256(data)
}
fn sha2_256(data: &[u8]) -> [u8; 32] {
sp_core::hashing::sha2_256(data)
}
fn blake2_128(data: &[u8]) -> [u8; 16] {
sp_core::hashing::blake2_128(data)
}
fn blake2_256(data: &[u8]) -> [u8; 32] {
sp_core::hashing::blake2_256(data)
}
fn twox_256(data: &[u8]) -> [u8; 32] {
sp_core::hashing::twox_256(data)
}
fn twox_128(data: &[u8]) -> [u8; 16] {
sp_core::hashing::twox_128(data)
}
fn twox_64(data: &[u8]) -> [u8; 8] {
sp_core::hashing::twox_64(data)
}
}
#[runtime_interface]
pub trait OffchainIndex {
fn set(&mut self, key: &[u8], value: &[u8]) {
self.set_offchain_storage(key, Some(value));
}
fn clear(&mut self, key: &[u8]) {
self.set_offchain_storage(key, None);
}
}
#[cfg(feature = "std")]
sp_externalities::decl_extension! {
pub struct VerificationExt(BatchVerifier);
}
#[runtime_interface]
pub trait Offchain {
fn is_validator(&mut self) -> bool {
self.extension::<OffchainExt>()
.expect("is_validator can be called only in the offchain worker context")
.is_validator()
}
fn submit_transaction(&mut self, data: Vec<u8>) -> Result<(), ()> {
self.extension::<TransactionPoolExt>()
.expect("submit_transaction can be called only in the offchain call context with
TransactionPool capabilities enabled")
.submit_transaction(data)
}
fn network_state(&mut self) -> Result<OpaqueNetworkState, ()> {
self.extension::<OffchainExt>()
.expect("network_state can be called only in the offchain worker context")
.network_state()
}
fn timestamp(&mut self) -> Timestamp {
self.extension::<OffchainExt>()
.expect("timestamp can be called only in the offchain worker context")
.timestamp()
}
fn sleep_until(&mut self, deadline: Timestamp) {
self.extension::<OffchainExt>()
.expect("sleep_until can be called only in the offchain worker context")
.sleep_until(deadline)
}
fn random_seed(&mut self) -> [u8; 32] {
self.extension::<OffchainExt>()
.expect("random_seed can be called only in the offchain worker context")
.random_seed()
}
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
self.extension::<OffchainExt>()
.expect("local_storage_set can be called only in the offchain worker context")
.local_storage_set(kind, key, value)
}
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
self.extension::<OffchainExt>()
.expect("local_storage_clear can be called only in the offchain worker context")
.local_storage_clear(kind, key)
}
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<Vec<u8>>,
new_value: &[u8],
) -> bool {
self.extension::<OffchainExt>()
.expect("local_storage_compare_and_set can be called only in the offchain worker context")
.local_storage_compare_and_set(kind, key, old_value.as_ref().map(|v| v.deref()), new_value)
}
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
self.extension::<OffchainExt>()
.expect("local_storage_get can be called only in the offchain worker context")
.local_storage_get(kind, key)
}
fn http_request_start(
&mut self,
method: &str,
uri: &str,
meta: &[u8],
) -> Result<HttpRequestId, ()> {
self.extension::<OffchainExt>()
.expect("http_request_start can be called only in the offchain worker context")
.http_request_start(method, uri, meta)
}
fn http_request_add_header(
&mut self,
request_id: HttpRequestId,
name: &str,
value: &str,
) -> Result<(), ()> {
self.extension::<OffchainExt>()
.expect("http_request_add_header can be called only in the offchain worker context")
.http_request_add_header(request_id, name, value)
}
fn http_request_write_body(
&mut self,
request_id: HttpRequestId,
chunk: &[u8],
deadline: Option<Timestamp>,
) -> Result<(), HttpError> {
self.extension::<OffchainExt>()
.expect("http_request_write_body can be called only in the offchain worker context")
.http_request_write_body(request_id, chunk, deadline)
}
fn http_response_wait(
&mut self,
ids: &[HttpRequestId],
deadline: Option<Timestamp>,
) -> Vec<HttpRequestStatus> {
self.extension::<OffchainExt>()
.expect("http_response_wait can be called only in the offchain worker context")
.http_response_wait(ids, deadline)
}
fn http_response_headers(&mut self, request_id: HttpRequestId) -> Vec<(Vec<u8>, Vec<u8>)> {
self.extension::<OffchainExt>()
.expect("http_response_headers can be called only in the offchain worker context")
.http_response_headers(request_id)
}
fn http_response_read_body(
&mut self,
request_id: HttpRequestId,
buffer: &mut [u8],
deadline: Option<Timestamp>,
) -> Result<u32, HttpError> {
self.extension::<OffchainExt>()
.expect("http_response_read_body can be called only in the offchain worker context")
.http_response_read_body(request_id, buffer, deadline)
.map(|r| r as u32)
}
fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool) {
self.extension::<OffchainExt>()
.expect("set_authorized_nodes can be called only in the offchain worker context")
.set_authorized_nodes(nodes, authorized_only)
}
}
#[runtime_interface(wasm_only)]
trait Allocator {
fn malloc(&mut self, size: u32) -> Pointer<u8> {
self.allocate_memory(size).expect("Failed to allocate memory")
}
fn free(&mut self, ptr: Pointer<u8>) {
self.deallocate_memory(ptr).expect("Failed to deallocate memory")
}
}
#[runtime_interface]
pub trait Logging {
fn log(level: LogLevel, target: &str, message: &[u8]) {
if let Ok(message) = std::str::from_utf8(message) {
log::log!(
target: target,
log::Level::from(level),
"{}",
message,
)
}
}
}
#[derive(Encode, Decode)]
pub struct Crossing<T: Encode + Decode>(T);
impl<T: Encode + Decode> PassBy for Crossing<T> {
type PassBy = sp_runtime_interface::pass_by::Codec<Self>;
}
impl<T: Encode + Decode> Crossing<T> {
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> core::default::Default for Crossing<T>
where T: core::default::Default + Encode + Decode
{
fn default() -> Self {
Self(Default::default())
}
}
#[runtime_interface(wasm_only, no_tracing)]
pub trait WasmTracing {
fn enabled(&mut self, metadata: Crossing<sp_tracing::WasmMetadata>) -> bool {
let metadata: &tracing_core::metadata::Metadata<'static> = (&metadata.into_inner()).into();
tracing::dispatcher::get_default(|d| {
d.enabled(metadata)
})
}
fn enter_span(&mut self, span: Crossing<sp_tracing::WasmEntryAttributes>) -> u64 {
let span: tracing::Span = span.into_inner().into();
match span.id() {
Some(id) => tracing::dispatcher::get_default(|d| {
let final_id = d.clone_span(&id);
d.enter(&final_id);
final_id.into_u64()
}),
_ => {
0
}
}
}
fn event(&mut self, event: Crossing<sp_tracing::WasmEntryAttributes>) {
event.into_inner().emit();
}
fn exit(&mut self, span: u64) {
tracing::dispatcher::get_default(|d| {
let id = tracing_core::span::Id::from_u64(span);
d.exit(&id);
});
}
}
#[cfg(all(not(feature="std"), feature="with-tracing"))]
mod tracing_setup {
use core::sync::atomic::{AtomicBool, Ordering};
use tracing_core::{
dispatcher::{Dispatch, set_global_default},
span::{Id, Record, Attributes},
Metadata, Event,
};
use super::{wasm_tracing, Crossing};
const TRACING_SET : AtomicBool = AtomicBool::new(false);
struct PassingTracingSubsciber;
impl tracing_core::Subscriber for PassingTracingSubsciber {
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
wasm_tracing::enabled(Crossing(metadata.into()))
}
fn new_span(&self, attrs: &Attributes<'_>) -> Id {
Id::from_u64(wasm_tracing::enter_span(Crossing(attrs.into())))
}
fn enter(&self, span: &Id) {
}
fn record(&self, span: &Id, values: &Record<'_>) {
unimplemented!{}
}
fn record_follows_from(&self, span: &Id, follows: &Id) {
unimplemented!{ }
}
fn event(&self, event: &Event<'_>) {
wasm_tracing::event(Crossing(event.into()))
}
fn exit(&self, span: &Id) {
wasm_tracing::exit(span.into_u64())
}
}
pub fn init_tracing() {
if TRACING_SET.load(Ordering::Relaxed) == false {
set_global_default(Dispatch::new(PassingTracingSubsciber {}))
.expect("We only ever call this once");
TRACING_SET.store(true, Ordering::Relaxed);
}
}
}
#[cfg(not(all(not(feature="std"), feature="with-tracing")))]
mod tracing_setup {
pub fn init_tracing() { }
}
pub use tracing_setup::init_tracing;
#[runtime_interface(wasm_only)]
pub trait Sandbox {
fn instantiate(
&mut self,
dispatch_thunk: u32,
wasm_code: &[u8],
env_def: &[u8],
state_ptr: Pointer<u8>,
) -> u32 {
self.sandbox()
.instance_new(dispatch_thunk, wasm_code, env_def, state_ptr.into())
.expect("Failed to instantiate a new sandbox")
}
fn invoke(
&mut self,
instance_idx: u32,
function: &str,
args: &[u8],
return_val_ptr: Pointer<u8>,
return_val_len: u32,
state_ptr: Pointer<u8>,
) -> u32 {
self.sandbox().invoke(
instance_idx,
&function,
&args,
return_val_ptr,
return_val_len,
state_ptr.into(),
).expect("Failed to invoke function with sandbox")
}
fn memory_new(&mut self, initial: u32, maximum: u32) -> u32 {
self.sandbox()
.memory_new(initial, maximum)
.expect("Failed to create new memory with sandbox")
}
fn memory_get(
&mut self,
memory_idx: u32,
offset: u32,
buf_ptr: Pointer<u8>,
buf_len: u32,
) -> u32 {
self.sandbox()
.memory_get(memory_idx, offset, buf_ptr, buf_len)
.expect("Failed to get memory with sandbox")
}
fn memory_set(
&mut self,
memory_idx: u32,
offset: u32,
val_ptr: Pointer<u8>,
val_len: u32,
) -> u32 {
self.sandbox()
.memory_set(memory_idx, offset, val_ptr, val_len)
.expect("Failed to set memory with sandbox")
}
fn memory_teardown(&mut self, memory_idx: u32) {
self.sandbox().memory_teardown(memory_idx).expect("Failed to teardown memory with sandbox")
}
fn instance_teardown(&mut self, instance_idx: u32) {
self.sandbox().instance_teardown(instance_idx).expect("Failed to teardown sandbox instance")
}
fn get_global_val(&mut self, instance_idx: u32, name: &str) -> Option<sp_wasm_interface::Value> {
self.sandbox().get_global_val(instance_idx, name).expect("Failed to get global from sandbox")
}
}
#[cfg(not(feature = "std"))]
struct WasmAllocator;
#[cfg(all(not(feature = "disable_allocator"), not(feature = "std")))]
#[global_allocator]
static ALLOCATOR: WasmAllocator = WasmAllocator;
#[cfg(not(feature = "std"))]
mod allocator_impl {
use super::*;
use core::alloc::{GlobalAlloc, Layout};
unsafe impl GlobalAlloc for WasmAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
allocator::malloc(layout.size() as u32)
}
unsafe fn dealloc(&self, ptr: *mut u8, _: Layout) {
allocator::free(ptr)
}
}
}
#[cfg(all(not(feature = "disable_panic_handler"), not(feature = "std")))]
#[panic_handler]
#[no_mangle]
pub fn panic(info: &core::panic::PanicInfo) -> ! {
unsafe {
let message = sp_std::alloc::format!("{}", info);
logging::log(LogLevel::Error, "runtime", message.as_bytes());
core::arch::wasm32::unreachable();
}
}
#[cfg(all(not(feature = "disable_oom"), not(feature = "std")))]
#[alloc_error_handler]
pub fn oom(_: core::alloc::Layout) -> ! {
unsafe {
logging::log(LogLevel::Error, "runtime", b"Runtime memory exhausted. Aborting");
core::arch::wasm32::unreachable();
}
}
#[cfg(feature = "std")]
pub type TestExternalities = sp_state_machine::TestExternalities<sp_core::Blake2Hasher, u64>;
#[cfg(feature = "std")]
pub type SubstrateHostFunctions = (
storage::HostFunctions,
default_child_storage::HostFunctions,
misc::HostFunctions,
wasm_tracing::HostFunctions,
offchain::HostFunctions,
crypto::HostFunctions,
hashing::HostFunctions,
allocator::HostFunctions,
logging::HostFunctions,
sandbox::HostFunctions,
crate::trie::HostFunctions,
offchain_index::HostFunctions,
);
#[cfg(test)]
mod tests {
use super::*;
use sp_state_machine::BasicExternalities;
use sp_core::{
storage::Storage, map, traits::TaskExecutorExt, testing::TaskExecutor,
};
use std::any::TypeId;
#[test]
fn storage_works() {
let mut t = BasicExternalities::default();
t.execute_with(|| {
assert_eq!(storage::get(b"hello"), None);
storage::set(b"hello", b"world");
assert_eq!(storage::get(b"hello"), Some(b"world".to_vec()));
assert_eq!(storage::get(b"foo"), None);
storage::set(b"foo", &[1, 2, 3][..]);
});
t = BasicExternalities::new(Storage {
top: map![b"foo".to_vec() => b"bar".to_vec()],
children_default: map![],
});
t.execute_with(|| {
assert_eq!(storage::get(b"hello"), None);
assert_eq!(storage::get(b"foo"), Some(b"bar".to_vec()));
});
}
#[test]
fn read_storage_works() {
let value = b"\x0b\0\0\0Hello world".to_vec();
let mut t = BasicExternalities::new(Storage {
top: map![b":test".to_vec() => value.clone()],
children_default: map![],
});
t.execute_with(|| {
let mut v = [0u8; 4];
assert_eq!(storage::read(b":test", &mut v[..], 0).unwrap(), value.len() as u32);
assert_eq!(v, [11u8, 0, 0, 0]);
let mut w = [0u8; 11];
assert_eq!(storage::read(b":test", &mut w[..], 4).unwrap(), value.len() as u32 - 4);
assert_eq!(&w, b"Hello world");
});
}
#[test]
fn clear_prefix_works() {
let mut t = BasicExternalities::new(Storage {
top: map![
b":a".to_vec() => b"\x0b\0\0\0Hello world".to_vec(),
b":abcd".to_vec() => b"\x0b\0\0\0Hello world".to_vec(),
b":abc".to_vec() => b"\x0b\0\0\0Hello world".to_vec(),
b":abdd".to_vec() => b"\x0b\0\0\0Hello world".to_vec()
],
children_default: map![],
});
t.execute_with(|| {
storage::clear_prefix(b":abc");
assert!(storage::get(b":a").is_some());
assert!(storage::get(b":abdd").is_some());
assert!(storage::get(b":abcd").is_none());
assert!(storage::get(b":abc").is_none());
});
}
#[test]
fn batch_verify_start_finish_works() {
let mut ext = BasicExternalities::default();
ext.register_extension(TaskExecutorExt::new(TaskExecutor::new()));
ext.execute_with(|| {
crypto::start_batch_verify();
});
assert!(ext.extensions().get_mut(TypeId::of::<VerificationExt>()).is_some());
ext.execute_with(|| {
assert!(crypto::finish_batch_verify());
});
assert!(ext.extensions().get_mut(TypeId::of::<VerificationExt>()).is_none());
}
#[test]
fn long_sr25519_batching() {
let mut ext = BasicExternalities::default();
ext.register_extension(TaskExecutorExt::new(TaskExecutor::new()));
ext.execute_with(|| {
let pair = sr25519::Pair::generate_with_phrase(None).0;
crypto::start_batch_verify();
for it in 0..70 {
let msg = format!("Schnorrkel {}!", it);
let signature = pair.sign(msg.as_bytes());
crypto::sr25519_batch_verify(&signature, msg.as_bytes(), &pair.public());
}
crypto::sr25519_batch_verify(
&Default::default(),
&Vec::new(),
&Default::default(),
);
assert!(!crypto::finish_batch_verify());
crypto::start_batch_verify();
for it in 0..70 {
let msg = format!("Schnorrkel {}!", it);
let signature = pair.sign(msg.as_bytes());
crypto::sr25519_batch_verify(&signature, msg.as_bytes(), &pair.public());
}
assert!(crypto::finish_batch_verify());
});
}
#[test]
fn batching_works() {
let mut ext = BasicExternalities::default();
ext.register_extension(TaskExecutorExt::new(TaskExecutor::new()));
ext.execute_with(|| {
crypto::start_batch_verify();
crypto::ed25519_batch_verify(
&Default::default(),
&Vec::new(),
&Default::default(),
);
assert!(!crypto::finish_batch_verify());
crypto::start_batch_verify();
let pair = ed25519::Pair::generate_with_phrase(None).0;
let msg = b"Important message";
let signature = pair.sign(msg);
crypto::ed25519_batch_verify(&signature, msg, &pair.public());
let pair = ed25519::Pair::generate_with_phrase(None).0;
let msg = b"Even more important message";
let signature = pair.sign(msg);
crypto::ed25519_batch_verify(&signature, msg, &pair.public());
assert!(crypto::finish_batch_verify());
crypto::start_batch_verify();
let pair = ed25519::Pair::generate_with_phrase(None).0;
let msg = b"Important message";
let signature = pair.sign(msg);
crypto::ed25519_batch_verify(&signature, msg, &pair.public());
crypto::ed25519_batch_verify(
&Default::default(),
&Vec::new(),
&Default::default(),
);
assert!(!crypto::finish_batch_verify());
crypto::start_batch_verify();
let pair = ed25519::Pair::generate_with_phrase(None).0;
let msg = b"Ed25519 batching";
let signature = pair.sign(msg);
crypto::ed25519_batch_verify(&signature, msg, &pair.public());
let pair = sr25519::Pair::generate_with_phrase(None).0;
let msg = b"Schnorrkel rules";
let signature = pair.sign(msg);
crypto::sr25519_batch_verify(&signature, msg, &pair.public());
let pair = sr25519::Pair::generate_with_phrase(None).0;
let msg = b"Schnorrkel batches!";
let signature = pair.sign(msg);
crypto::sr25519_batch_verify(&signature, msg, &pair.public());
assert!(crypto::finish_batch_verify());
crypto::start_batch_verify();
let pair = sr25519::Pair::generate_with_phrase(None).0;
let msg = b"Schnorrkcel!";
let signature = pair.sign(msg);
crypto::sr25519_batch_verify(&signature, msg, &pair.public());
crypto::sr25519_batch_verify(
&Default::default(),
&Vec::new(),
&Default::default(),
);
assert!(!crypto::finish_batch_verify());
});
}
}