Newer
Older
deadline: Option<Timestamp>,
) -> Result<(), HttpError> {
self.extension::<OffchainWorkerExt>()
.expect("http_request_write_body can be called only in the offchain worker context")
.http_request_write_body(request_id, chunk, deadline)
}
/// Block and wait for the responses for given requests.
///
/// Returns a vector of request statuses (the len is the same as ids).
/// Note that if deadline is not provided the method will block indefinitely,
/// otherwise unready responses will produce `DeadlineReached` status.
///
/// Passing `None` as deadline blocks forever.
fn http_response_wait(
&mut self,
ids: &[HttpRequestId],
deadline: Option<Timestamp>,
) -> Vec<HttpRequestStatus> {
self.extension::<OffchainWorkerExt>()
.expect("http_response_wait can be called only in the offchain worker context")
.http_response_wait(ids, deadline)
}
/// Read all response headers.
///
/// Returns a vector of pairs `(HeaderKey, HeaderValue)`.
/// NOTE response headers have to be read before response body.
fn http_response_headers(&mut self, request_id: HttpRequestId) -> Vec<(Vec<u8>, Vec<u8>)> {
self.extension::<OffchainWorkerExt>()
.expect("http_response_headers can be called only in the offchain worker context")
.http_response_headers(request_id)
}
/// Read a chunk of body response to given buffer.
///
/// Returns the number of bytes written or an error in case a deadline
/// is reached or server closed the connection.
/// If `0` is returned it means that the response has been fully consumed
/// and the `request_id` is now invalid.
/// NOTE this implies that response headers must be read before draining the body.
/// Passing `None` as a deadline blocks forever.
fn http_response_read_body(
&mut self,
request_id: HttpRequestId,
buffer: &mut [u8],
deadline: Option<Timestamp>,
) -> Result<u32, HttpError> {
self.extension::<OffchainWorkerExt>()
.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)
/// Set the authorized nodes and authorized_only flag.
fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool) {
self.extension::<OffchainWorkerExt>()
.expect("set_authorized_nodes can be called only in the offchain worker context")
.set_authorized_nodes(nodes, authorized_only)
}
}
/// Wasm only interface that provides functions for calling into the allocator.
#[runtime_interface(wasm_only)]
/// Malloc the given number of bytes and return the pointer to the allocated memory location.
fn malloc(&mut self, size: u32) -> Pointer<u8> {
self.allocate_memory(size).expect("Failed to allocate memory")
}
/// Free the given pointer.
fn free(&mut self, ptr: Pointer<u8>) {
self.deallocate_memory(ptr).expect("Failed to deallocate memory")
}
}
/// Interface that provides functions for logging from within the runtime.
#[runtime_interface]
pub trait Logging {
/// Request to print a log message on the host.
///
/// Note that this will be only displayed if the host is enabled to display log messages with
/// given level and target.
///
/// Instead of using directly, prefer setting up `RuntimeLogger` and using `log` macros.
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,
)
}
/// Returns the max log level used by the host.
fn max_level() -> LogLevelFilter {
log::max_level().into()
}
}
#[derive(Encode, Decode)]
/// Crossing is a helper wrapping any Encode-Decodeable type
/// for transferring over the wasm barrier.
pub struct Crossing<T: Encode + Decode>(T);
impl<T: Encode + Decode> PassBy for Crossing<T> {
type PassBy = sp_runtime_interface::pass_by::Codec<Self>;
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
impl<T: Encode + Decode> Crossing<T> {
/// Convert into the inner type
pub fn into_inner(self) -> T {
self.0
}
}
// useful for testing
impl<T> core::default::Default for Crossing<T>
where T: core::default::Default + Encode + Decode
{
fn default() -> Self {
Self(Default::default())
}
}
/// Interface to provide tracing facilities for wasm. Modelled after tokios `tracing`-crate
/// interfaces. See `sp-tracing` for more information.
#[runtime_interface(wasm_only, no_tracing)]
pub trait WasmTracing {
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
/// Whether the span described in `WasmMetadata` should be traced wasm-side
/// On the host converts into a static Metadata and checks against the global `tracing` dispatcher.
///
/// When returning false the calling code should skip any tracing-related execution. In general
/// within the same block execution this is not expected to change and it doesn't have to be
/// checked more than once per metadata. This exists for optimisation purposes but is still not
/// cheap as it will jump the wasm-native-barrier every time it is called. So an implementation might
/// chose to cache the result for the execution of the entire block.
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)
})
}
/// Open a new span with the given attributes. Return the u64 Id of the span.
///
/// On the native side this goes through the default `tracing` dispatcher to register the span
/// and then calls `clone_span` with the ID to signal that we are keeping it around on the wasm-
/// side even after the local span is dropped. The resulting ID is then handed over to the wasm-
/// side.
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| {
// inform dispatch that we'll keep the ID around
// then enter it immediately
let final_id = d.clone_span(&id);
d.enter(&final_id);
final_id.into_u64()
}),
_ => {
0
}
}
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
}
/// Emit the given event to the global tracer on the native side
fn event(&mut self, event: Crossing<sp_tracing::WasmEntryAttributes>) {
event.into_inner().emit();
}
/// Signal that a given span-id has been exited. On native, this directly
/// proxies the span to the global dispatcher.
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};
static TRACING_SET: AtomicBool = AtomicBool::new(false);
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
/// The PassingTracingSubscriber implements `tracing_core::Subscriber`
/// and pushes the information across the runtime interface to the host
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) {
// Do nothing, we already entered the span previously
}
/// Not implemented! We do not support recording values later
/// Will panic when used.
fn record(&self, span: &Id, values: &Record<'_>) {
unimplemented!{} // this usage is not supported
}
/// Not implemented! We do not support recording values later
/// Will panic when used.
fn record_follows_from(&self, span: &Id, follows: &Id) {
unimplemented!{ } // this usage is not supported
}
fn event(&self, event: &Event<'_>) {
wasm_tracing::event(Crossing(event.into()))
}
fn exit(&self, span: &Id) {
wasm_tracing::exit(span.into_u64())
}
}
/// Initialize tracing of sp_tracing on wasm with `with-tracing` enabled.
/// Can be called multiple times from within the same process and will only
/// set the global bridging subscriber once.
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 {
/// Initialize tracing of sp_tracing not necessary – noop. To enable build
/// without std and with the `with-tracing`-feature.
pub fn init_tracing() { }
}
pub use tracing_setup::init_tracing;
/// Wasm-only interface that provides functions for interacting with the sandbox.
#[runtime_interface(wasm_only)]
pub trait Sandbox {
/// Instantiate a new sandbox instance with the given `wasm_code`.
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")
}
/// Invoke `function` in the sandbox with `sandbox_idx`.
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")
}
/// Create a new memory instance with the given `initial` and `maximum` size.
fn memory_new(&mut self, initial: u32, maximum: u32) -> u32 {
self.sandbox()
.memory_new(initial, maximum)
.expect("Failed to create new memory with sandbox")
}
/// Get the memory starting at `offset` from the instance with `memory_idx` into the buffer.
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")
}
/// Set the memory in the given `memory_idx` to the given value at `offset`.
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")
}
/// Teardown the memory instance with the given `memory_idx`.
fn memory_teardown(&mut self, memory_idx: u32) {
self.sandbox().memory_teardown(memory_idx).expect("Failed to teardown memory with sandbox")
}
/// Teardown the sandbox instance with the given `instance_idx`.
fn instance_teardown(&mut self, instance_idx: u32) {
self.sandbox().instance_teardown(instance_idx).expect("Failed to teardown sandbox instance")
/// Get the value from a global with the given `name`. The sandbox is determined by the given
/// `instance_idx`.
///
/// Returns `Some(_)` when the requested global variable could be found.
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")
}
}
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
/// Wasm host functions for managing tasks.
///
/// This should not be used directly. Use `sp_tasks` for running parallel tasks instead.
#[runtime_interface(wasm_only)]
pub trait RuntimeTasks {
/// Wasm host function for spawning task.
///
/// This should not be used directly. Use `sp_tasks::spawn` instead.
fn spawn(dispatcher_ref: u32, entry: u32, payload: Vec<u8>) -> u64 {
sp_externalities::with_externalities(|mut ext|{
let runtime_spawn = ext.extension::<RuntimeSpawnExt>()
.expect("Cannot spawn without dynamic runtime dispatcher (RuntimeSpawnExt)");
runtime_spawn.spawn_call(dispatcher_ref, entry, payload)
}).expect("`RuntimeTasks::spawn`: called outside of externalities context")
}
/// Wasm host function for joining a task.
///
/// This should not be used directly. Use `join` of `sp_tasks::spawn` result instead.
fn join(handle: u64) -> Vec<u8> {
sp_externalities::with_externalities(|mut ext| {
let runtime_spawn = ext.extension::<RuntimeSpawnExt>()
.expect("Cannot join without dynamic runtime dispatcher (RuntimeSpawnExt)");
runtime_spawn.join(handle)
}).expect("`RuntimeTasks::join`: called outside of externalities context")
}
}
/// Allocator used by Substrate when executing the Wasm runtime.
#[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)
}
}
}
/// A default panic handler for WASM environment.
#[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();
/// A default OOM handler for WASM environment.
#[cfg(all(not(feature = "disable_oom"), not(feature = "std")))]
#[alloc_error_handler]
pub fn oom(_: core::alloc::Layout) -> ! {
logging::log(LogLevel::Error, "runtime", b"Runtime memory exhausted. Aborting");
core::arch::wasm32::unreachable();
Svyatoslav Nikolsky
committed
/// Type alias for Externalities implementation used in tests.
#[cfg(feature = "std")]
pub type TestExternalities = sp_state_machine::TestExternalities<sp_core::Blake2Hasher, u64>;
/// The host functions Substrate provides for the Wasm runtime environment.
///
/// All these host functions will be callable from inside the Wasm environment.
#[cfg(feature = "std")]
pub type SubstrateHostFunctions = (
storage::HostFunctions,
wasm_tracing::HostFunctions,
offchain::HostFunctions,
crypto::HostFunctions,
hashing::HostFunctions,
allocator::HostFunctions,
logging::HostFunctions,
sandbox::HostFunctions,
crate::trie::HostFunctions,
);
#[cfg(test)]
mod tests {
use super::*;
use sp_core::{
storage::Storage, map, traits::TaskExecutorExt, testing::TaskExecutor,
};
#[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()],
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()],
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()
],
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());
});
}
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(|| {
// invalid ed25519 signature
crypto::start_batch_verify();
crypto::ed25519_batch_verify(
&Default::default(),
&Vec::new(),
&Default::default(),
);
assert!(!crypto::finish_batch_verify());
// 2 valid ed25519 signatures
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());
// 1 valid, 1 invalid ed25519 signature
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());
// 1 valid ed25519, 2 valid sr25519
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());
// 1 valid sr25519, 1 invalid sr25519
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());
});
}