feat: initialize Kurdistan SDK - independent fork of Polkadot SDK
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program 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.
|
||||
|
||||
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Rust executor possible errors.
|
||||
|
||||
/// Result type alias.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Error type.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Error {
|
||||
#[error("Error calling api function: {0}")]
|
||||
ApiError(Box<dyn std::error::Error + Send + Sync>),
|
||||
|
||||
#[error("Method not found: '{0}'")]
|
||||
MethodNotFound(String),
|
||||
|
||||
#[error("On-chain runtime does not specify version")]
|
||||
VersionInvalid,
|
||||
|
||||
#[error("Externalities error")]
|
||||
Externalities,
|
||||
|
||||
#[error("Invalid index provided")]
|
||||
InvalidIndex,
|
||||
|
||||
#[error("Invalid type returned (should be u64)")]
|
||||
InvalidReturn,
|
||||
|
||||
#[error("Runtime panicked: {0}")]
|
||||
RuntimePanicked(String),
|
||||
|
||||
#[error("Invalid memory reference")]
|
||||
InvalidMemoryReference,
|
||||
|
||||
#[error("The runtime doesn't provide a global named `__heap_base` of type `i32`")]
|
||||
HeapBaseNotFoundOrInvalid,
|
||||
|
||||
#[error("The runtime must not have the `start` function defined")]
|
||||
RuntimeHasStartFn,
|
||||
|
||||
#[error("Other: {0}")]
|
||||
Other(String),
|
||||
|
||||
#[error(transparent)]
|
||||
Allocator(#[from] sc_allocator::Error),
|
||||
|
||||
#[error("Host function {0} execution failed with: {1}")]
|
||||
FunctionExecution(String, String),
|
||||
|
||||
#[error("No table exported by wasm blob")]
|
||||
NoTable,
|
||||
|
||||
#[error("No table entry with index {0} in wasm blob exported table")]
|
||||
NoTableEntryWithIndex(u32),
|
||||
|
||||
#[error("Table element with index {0} is not a function in wasm blob exported table")]
|
||||
TableElementIsNotAFunction(u32),
|
||||
|
||||
#[error("Table entry with index {0} in wasm blob is null")]
|
||||
FunctionRefIsNull(u32),
|
||||
|
||||
#[error(transparent)]
|
||||
RuntimeConstruction(#[from] WasmError),
|
||||
|
||||
#[error("Shared memory is not supported")]
|
||||
SharedMemUnsupported,
|
||||
|
||||
#[error("Imported globals are not supported yet")]
|
||||
ImportedGlobalsUnsupported,
|
||||
|
||||
#[error("initializer expression can have only up to 2 expressions in wasm 1.0")]
|
||||
InitializerHasTooManyExpressions,
|
||||
|
||||
#[error("Invalid initializer expression provided {0}")]
|
||||
InvalidInitializerExpression(String),
|
||||
|
||||
#[error("Execution aborted due to panic: {0}")]
|
||||
AbortedDueToPanic(MessageWithBacktrace),
|
||||
|
||||
#[error("Execution aborted due to trap: {0}")]
|
||||
AbortedDueToTrap(MessageWithBacktrace),
|
||||
|
||||
#[error("Output exceeds bounds of wasm memory")]
|
||||
OutputExceedsBounds,
|
||||
}
|
||||
|
||||
impl From<&'static str> for Error {
|
||||
fn from(err: &'static str) -> Error {
|
||||
Error::Other(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Error {
|
||||
fn from(err: String) -> Error {
|
||||
Error::Other(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Type for errors occurring during Wasm runtime construction.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum WasmError {
|
||||
#[error("Code could not be read from the state.")]
|
||||
CodeNotFound,
|
||||
|
||||
#[error("Failure to reinitialize runtime instance from snapshot.")]
|
||||
ApplySnapshotFailed,
|
||||
|
||||
/// Failure to erase the wasm memory.
|
||||
///
|
||||
/// Depending on the implementation might mean failure of allocating memory.
|
||||
#[error("Failure to erase the wasm memory: {0}")]
|
||||
ErasingFailed(String),
|
||||
|
||||
#[error("Wasm code failed validation.")]
|
||||
InvalidModule,
|
||||
|
||||
#[error("Wasm code could not be deserialized.")]
|
||||
CantDeserializeWasm,
|
||||
|
||||
#[error("The module does not export a linear memory named `memory`.")]
|
||||
InvalidMemory,
|
||||
|
||||
#[error("The number of heap pages requested is disallowed by the module.")]
|
||||
InvalidHeapPages,
|
||||
|
||||
/// Instantiation error.
|
||||
#[error("{0}")]
|
||||
Instantiation(String),
|
||||
|
||||
/// Other error happened.
|
||||
#[error("Other error happened while constructing the runtime: {0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl From<polkavm::program::ProgramParseError> for WasmError {
|
||||
fn from(error: polkavm::program::ProgramParseError) -> Self {
|
||||
WasmError::Other(error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<polkavm::Error> for WasmError {
|
||||
fn from(error: polkavm::Error) -> Self {
|
||||
WasmError::Other(error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<polkavm::Error> for Error {
|
||||
fn from(error: polkavm::Error) -> Self {
|
||||
Error::Other(error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// An error message with an attached backtrace.
|
||||
#[derive(Debug)]
|
||||
pub struct MessageWithBacktrace {
|
||||
/// The error message.
|
||||
pub message: String,
|
||||
|
||||
/// The backtrace associated with the error message.
|
||||
pub backtrace: Option<Backtrace>,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MessageWithBacktrace {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
fmt.write_str(&self.message)?;
|
||||
if let Some(ref backtrace) = self.backtrace {
|
||||
fmt.write_str("\nWASM backtrace:\n")?;
|
||||
backtrace.backtrace_string.fmt(fmt)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A WASM backtrace.
|
||||
#[derive(Debug)]
|
||||
pub struct Backtrace {
|
||||
/// The string containing the backtrace.
|
||||
pub backtrace_string: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Backtrace {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
fmt.write_str(&self.backtrace_string)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program 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.
|
||||
|
||||
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! A set of common definitions that are needed for defining execution engines.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unused_crate_dependencies)]
|
||||
|
||||
pub mod error;
|
||||
pub mod runtime_blob;
|
||||
pub mod util;
|
||||
pub mod wasm_runtime;
|
||||
|
||||
pub(crate) fn is_polkavm_enabled() -> bool {
|
||||
std::env::var_os("SUBSTRATE_ENABLE_POLKAVM").map_or(false, |value| value == "1")
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program 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.
|
||||
|
||||
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! This module allows for inspection and instrumentation, i.e. modifying the module to alter it's
|
||||
//! structure or behavior, of a wasm module.
|
||||
//!
|
||||
//! ## Instrumentation
|
||||
//!
|
||||
//! In ideal world, there would be no instrumentation. However, in the real world the execution
|
||||
//! engines we use are somewhat limited in their APIs or abilities.
|
||||
//!
|
||||
//! To give you some examples:
|
||||
//!
|
||||
//! We need to reset the globals because when we
|
||||
//! execute the Substrate Runtime, we do not drop and create the instance anew, instead
|
||||
//! we restore some selected parts of the state.
|
||||
//!
|
||||
//! - stack depth metering can be performed via instrumentation or deferred to the engine and say be
|
||||
//! added directly in machine code. Implementing this in machine code is rather cumbersome so
|
||||
//! instrumentation looks like a good solution.
|
||||
//!
|
||||
//! Stack depth metering is needed to make a wasm blob
|
||||
//! execution deterministic, which in turn is needed by the Teyrchain Validation Function in
|
||||
//! Pezkuwi.
|
||||
//!
|
||||
//! ## Inspection
|
||||
//!
|
||||
//! Inspection of a wasm module may be needed to extract some useful information, such as to extract
|
||||
//! data segment snapshot, which is helpful for quickly restoring the initial state of instances.
|
||||
//! Inspection can be also useful to prove that a wasm module possesses some properties, such as,
|
||||
//! is free of any floating point operations, which is a useful step towards making instances
|
||||
//! produced from such a module deterministic.
|
||||
|
||||
mod runtime_blob;
|
||||
|
||||
pub use runtime_blob::RuntimeBlob;
|
||||
@@ -0,0 +1,234 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program 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.
|
||||
|
||||
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{error::WasmError, wasm_runtime::HeapAllocStrategy};
|
||||
use polkavm::ArcBytes;
|
||||
use wasm_instrument::parity_wasm::elements::{
|
||||
deserialize_buffer, serialize, ExportEntry, External, Internal, MemorySection, MemoryType,
|
||||
Module, Section,
|
||||
};
|
||||
|
||||
/// A program blob containing a Substrate runtime.
|
||||
#[derive(Clone)]
|
||||
pub struct RuntimeBlob(BlobKind);
|
||||
|
||||
#[derive(Clone)]
|
||||
enum BlobKind {
|
||||
WebAssembly(Module),
|
||||
PolkaVM((polkavm::ProgramBlob, ArcBytes)),
|
||||
}
|
||||
|
||||
impl RuntimeBlob {
|
||||
/// Create `RuntimeBlob` from the given WASM or PolkaVM compressed program blob.
|
||||
///
|
||||
/// See [`sp_maybe_compressed_blob`] for details about decompression.
|
||||
pub fn uncompress_if_needed(wasm_code: &[u8]) -> Result<Self, WasmError> {
|
||||
use sp_maybe_compressed_blob::CODE_BLOB_BOMB_LIMIT;
|
||||
let wasm_code = sp_maybe_compressed_blob::decompress(wasm_code, CODE_BLOB_BOMB_LIMIT)
|
||||
.map_err(|e| WasmError::Other(format!("Decompression error: {:?}", e)))?;
|
||||
Self::new(&wasm_code)
|
||||
}
|
||||
|
||||
/// Create `RuntimeBlob` from the given WASM or PolkaVM program blob.
|
||||
///
|
||||
/// Returns `Err` if the blob cannot be deserialized.
|
||||
///
|
||||
/// Will only accept a PolkaVM program if the `SUBSTRATE_ENABLE_POLKAVM` environment
|
||||
/// variable is set to `1`.
|
||||
pub fn new(raw_blob: &[u8]) -> Result<Self, WasmError> {
|
||||
if raw_blob.starts_with(b"PVM\0") {
|
||||
if crate::is_polkavm_enabled() {
|
||||
let raw = ArcBytes::from(raw_blob);
|
||||
let blob = polkavm::ProgramBlob::parse(raw.clone())?;
|
||||
return Ok(Self(BlobKind::PolkaVM((blob, raw))));
|
||||
} else {
|
||||
return Err(WasmError::Other("expected a WASM runtime blob, found a PolkaVM runtime blob; set the 'SUBSTRATE_ENABLE_POLKAVM' environment variable to enable the experimental PolkaVM-based executor".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
let raw_module: Module = deserialize_buffer(raw_blob)
|
||||
.map_err(|e| WasmError::Other(format!("cannot deserialize module: {:?}", e)))?;
|
||||
Ok(Self(BlobKind::WebAssembly(raw_module)))
|
||||
}
|
||||
|
||||
/// Run a pass that instrument this module so as to introduce a deterministic stack height
|
||||
/// limit.
|
||||
///
|
||||
/// It will introduce a global mutable counter. The instrumentation will increase the counter
|
||||
/// according to the "cost" of the callee. If the cost exceeds the `stack_depth_limit` constant,
|
||||
/// the instrumentation will trap. The counter will be decreased as soon as the the callee
|
||||
/// returns.
|
||||
///
|
||||
/// The stack cost of a function is computed based on how much locals there are and the maximum
|
||||
/// depth of the wasm operand stack.
|
||||
///
|
||||
/// Only valid for WASM programs; will return an error if the blob is a PolkaVM program.
|
||||
pub fn inject_stack_depth_metering(self, stack_depth_limit: u32) -> Result<Self, WasmError> {
|
||||
let injected_module =
|
||||
wasm_instrument::inject_stack_limiter(self.into_webassembly_blob()?, stack_depth_limit)
|
||||
.map_err(|e| {
|
||||
WasmError::Other(format!("cannot inject the stack limiter: {:?}", e))
|
||||
})?;
|
||||
|
||||
Ok(Self(BlobKind::WebAssembly(injected_module)))
|
||||
}
|
||||
|
||||
/// Converts a WASM memory import into a memory section and exports it.
|
||||
///
|
||||
/// Does nothing if there's no memory import.
|
||||
///
|
||||
/// May return an error in case the WASM module is invalid.
|
||||
///
|
||||
/// Only valid for WASM programs; will return an error if the blob is a PolkaVM program.
|
||||
pub fn convert_memory_import_into_export(&mut self) -> Result<(), WasmError> {
|
||||
let raw_module = self.as_webassembly_blob_mut()?;
|
||||
let import_section = match raw_module.import_section_mut() {
|
||||
Some(import_section) => import_section,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let import_entries = import_section.entries_mut();
|
||||
for index in 0..import_entries.len() {
|
||||
let entry = &import_entries[index];
|
||||
let memory_ty = match entry.external() {
|
||||
External::Memory(memory_ty) => *memory_ty,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
let memory_name = entry.field().to_owned();
|
||||
import_entries.remove(index);
|
||||
|
||||
raw_module
|
||||
.insert_section(Section::Memory(MemorySection::with_entries(vec![memory_ty])))
|
||||
.map_err(|error| {
|
||||
WasmError::Other(format!(
|
||||
"can't convert a memory import into an export: failed to insert a new memory section: {}",
|
||||
error
|
||||
))
|
||||
})?;
|
||||
|
||||
if raw_module.export_section_mut().is_none() {
|
||||
// A module without an export section is somewhat unrealistic, but let's do this
|
||||
// just in case to cover all of our bases.
|
||||
raw_module
|
||||
.insert_section(Section::Export(Default::default()))
|
||||
.expect("an export section can be always inserted if it doesn't exist; qed");
|
||||
}
|
||||
raw_module
|
||||
.export_section_mut()
|
||||
.expect("export section already existed or we just added it above, so it always exists; qed")
|
||||
.entries_mut()
|
||||
.push(ExportEntry::new(memory_name, Internal::Memory(0)));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Modifies the blob's memory section according to the given `heap_alloc_strategy`.
|
||||
///
|
||||
/// Will return an error in case there is no memory section present,
|
||||
/// or if the memory section is empty.
|
||||
///
|
||||
/// Only valid for WASM programs; will return an error if the blob is a PolkaVM program.
|
||||
pub fn setup_memory_according_to_heap_alloc_strategy(
|
||||
&mut self,
|
||||
heap_alloc_strategy: HeapAllocStrategy,
|
||||
) -> Result<(), WasmError> {
|
||||
let raw_module = self.as_webassembly_blob_mut()?;
|
||||
let memory_section = raw_module
|
||||
.memory_section_mut()
|
||||
.ok_or_else(|| WasmError::Other("no memory section found".into()))?;
|
||||
|
||||
if memory_section.entries().is_empty() {
|
||||
return Err(WasmError::Other("memory section is empty".into()));
|
||||
}
|
||||
for memory_ty in memory_section.entries_mut() {
|
||||
let initial = memory_ty.limits().initial();
|
||||
let (min, max) = match heap_alloc_strategy {
|
||||
HeapAllocStrategy::Dynamic { maximum_pages } => {
|
||||
// Ensure `initial <= maximum_pages`
|
||||
(maximum_pages.map(|m| m.min(initial)).unwrap_or(initial), maximum_pages)
|
||||
},
|
||||
HeapAllocStrategy::Static { extra_pages } => {
|
||||
let pages = initial.saturating_add(extra_pages);
|
||||
(pages, Some(pages))
|
||||
},
|
||||
};
|
||||
*memory_ty = MemoryType::new(min, max);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Scans the wasm blob for the first section with the name that matches the given. Returns the
|
||||
/// contents of the custom section if found or `None` otherwise.
|
||||
///
|
||||
/// Only valid for WASM programs; will return an error if the blob is a PolkaVM program.
|
||||
pub fn custom_section_contents(&self, section_name: &str) -> Option<&[u8]> {
|
||||
self.as_webassembly_blob()
|
||||
.ok()?
|
||||
.custom_sections()
|
||||
.find(|cs| cs.name() == section_name)
|
||||
.map(|cs| cs.payload())
|
||||
}
|
||||
|
||||
/// Consumes this runtime blob and serializes it.
|
||||
pub fn serialize(self) -> Vec<u8> {
|
||||
match self.0 {
|
||||
BlobKind::WebAssembly(raw_module) =>
|
||||
serialize(raw_module).expect("serializing into a vec should succeed; qed"),
|
||||
BlobKind::PolkaVM(ref blob) => blob.1.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_webassembly_blob(&self) -> Result<&Module, WasmError> {
|
||||
match self.0 {
|
||||
BlobKind::WebAssembly(ref raw_module) => Ok(raw_module),
|
||||
BlobKind::PolkaVM(..) => Err(WasmError::Other(
|
||||
"expected a WebAssembly program; found a PolkaVM program blob".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_webassembly_blob_mut(&mut self) -> Result<&mut Module, WasmError> {
|
||||
match self.0 {
|
||||
BlobKind::WebAssembly(ref mut raw_module) => Ok(raw_module),
|
||||
BlobKind::PolkaVM(..) => Err(WasmError::Other(
|
||||
"expected a WebAssembly program; found a PolkaVM program blob".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_webassembly_blob(self) -> Result<Module, WasmError> {
|
||||
match self.0 {
|
||||
BlobKind::WebAssembly(raw_module) => Ok(raw_module),
|
||||
BlobKind::PolkaVM(..) => Err(WasmError::Other(
|
||||
"expected a WebAssembly program; found a PolkaVM program blob".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a reference to the inner PolkaVM program blob, if this is a PolkaVM program.
|
||||
pub fn as_polkavm_blob(&self) -> Option<&polkavm::ProgramBlob> {
|
||||
match self.0 {
|
||||
BlobKind::WebAssembly(..) => None,
|
||||
BlobKind::PolkaVM((ref blob, _)) => Some(blob),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program 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.
|
||||
|
||||
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Utilities used by all backends
|
||||
|
||||
use crate::error::Result;
|
||||
use sp_wasm_interface::Pointer;
|
||||
use std::ops::Range;
|
||||
|
||||
/// Construct a range from an offset to a data length after the offset.
|
||||
/// Returns None if the end of the range would exceed some maximum offset.
|
||||
pub fn checked_range(offset: usize, len: usize, max: usize) -> Option<Range<usize>> {
|
||||
let end = offset.checked_add(len)?;
|
||||
(end <= max).then(|| offset..end)
|
||||
}
|
||||
|
||||
/// Provides safe memory access interface using an external buffer
|
||||
pub trait MemoryTransfer {
|
||||
/// Read data from a slice of memory into a newly allocated buffer.
|
||||
///
|
||||
/// Returns an error if the read would go out of the memory bounds.
|
||||
fn read(&self, source_addr: Pointer<u8>, size: usize) -> Result<Vec<u8>>;
|
||||
|
||||
/// Read data from a slice of memory into a destination buffer.
|
||||
///
|
||||
/// Returns an error if the read would go out of the memory bounds.
|
||||
fn read_into(&self, source_addr: Pointer<u8>, destination: &mut [u8]) -> Result<()>;
|
||||
|
||||
/// Write data to a slice of memory.
|
||||
///
|
||||
/// Returns an error if the write would go out of the memory bounds.
|
||||
fn write_from(&self, dest_addr: Pointer<u8>, source: &[u8]) -> Result<()>;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program 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.
|
||||
|
||||
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Definitions for a wasm runtime.
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
pub use sc_allocator::AllocationStats;
|
||||
|
||||
/// Default heap allocation strategy.
|
||||
pub const DEFAULT_HEAP_ALLOC_STRATEGY: HeapAllocStrategy =
|
||||
HeapAllocStrategy::Static { extra_pages: DEFAULT_HEAP_ALLOC_PAGES };
|
||||
|
||||
/// Default heap allocation pages.
|
||||
pub const DEFAULT_HEAP_ALLOC_PAGES: u32 = 2048;
|
||||
|
||||
/// A trait that defines an abstract WASM runtime module.
|
||||
///
|
||||
/// This can be implemented by an execution engine.
|
||||
pub trait WasmModule: Sync + Send {
|
||||
/// Create a new instance.
|
||||
fn new_instance(&self) -> Result<Box<dyn WasmInstance>, Error>;
|
||||
}
|
||||
|
||||
/// A trait that defines an abstract wasm module instance.
|
||||
///
|
||||
/// This can be implemented by an execution engine.
|
||||
pub trait WasmInstance: Send {
|
||||
/// Call a method on this WASM instance.
|
||||
///
|
||||
/// Before execution, instance is reset.
|
||||
///
|
||||
/// Returns the encoded result on success.
|
||||
fn call(&mut self, method: &str, data: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
self.call_with_allocation_stats(method, data).0
|
||||
}
|
||||
|
||||
/// Call a method on this WASM instance.
|
||||
///
|
||||
/// Before execution, instance is reset.
|
||||
///
|
||||
/// Returns the encoded result on success.
|
||||
fn call_with_allocation_stats(
|
||||
&mut self,
|
||||
method: &str,
|
||||
data: &[u8],
|
||||
) -> (Result<Vec<u8>, Error>, Option<AllocationStats>);
|
||||
|
||||
/// Call an exported method on this WASM instance.
|
||||
///
|
||||
/// Before execution, instance is reset.
|
||||
///
|
||||
/// Returns the encoded result on success.
|
||||
fn call_export(&mut self, method: &str, data: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
self.call(method.into(), data)
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines the heap pages allocation strategy the wasm runtime should use.
|
||||
///
|
||||
/// A heap page is defined as 64KiB of memory.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq)]
|
||||
pub enum HeapAllocStrategy {
|
||||
/// Allocate a static number of heap pages.
|
||||
///
|
||||
/// The total number of allocated heap pages is the initial number of heap pages requested by
|
||||
/// the wasm file plus the `extra_pages`.
|
||||
Static {
|
||||
/// The number of pages that will be added on top of the initial heap pages requested by
|
||||
/// the wasm file.
|
||||
extra_pages: u32,
|
||||
},
|
||||
/// Allocate the initial heap pages as requested by the wasm file and then allow it to grow
|
||||
/// dynamically.
|
||||
Dynamic {
|
||||
/// The absolute maximum size of the linear memory (in pages).
|
||||
///
|
||||
/// When `Some(_)` the linear memory will be allowed to grow up to this limit.
|
||||
/// When `None` the linear memory will be allowed to grow up to the maximum limit supported
|
||||
/// by WASM (4GB).
|
||||
maximum_pages: Option<u32>,
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user