pub mod executor;
use bytes::Bytes;
use executor::ExecuteError;
use thiserror::Error;
use casper_executor_wasm_common::{
error::{CallError, TrapCode, CALLEE_SUCCEEDED},
flags::ReturnFlags,
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct InterfaceVersion(u32);
impl From<u32> for InterfaceVersion {
fn from(value: u32) -> Self {
InterfaceVersion(value)
}
}
pub type HostResult = Result<(), CallError>;
#[must_use]
pub fn u32_from_host_result(result: HostResult) -> u32 {
match result {
Ok(()) => CALLEE_SUCCEEDED,
Err(host_error) => host_error.into_u32(),
}
}
#[derive(Debug, Error)]
pub enum Resolver {
#[error("export {name} not found.")]
Export { name: String },
#[error("function pointer {index} not found.")]
Table { index: u32 },
}
#[derive(Error, Debug)]
pub enum ExportError {
#[error("incompatible type")]
IncompatibleType,
#[error("missing export {0}")]
Missing(String),
}
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum MemoryError {
#[error("memory access out of bounds")]
HeapOutOfBounds,
#[error("address calculation overflow")]
Overflow,
#[error("string is not valid utf-8")]
NonUtf8String,
}
#[derive(Error, Debug)]
pub enum InternalHostError {
#[error("type conversion failure")]
TypeConversion,
#[error("contract already exists")]
ContractAlreadyExists,
#[error("tracking copy error")]
TrackingCopy,
#[error("failed building execution request")]
ExecuteRequestBuildFailure,
#[error("unexpected entity kind")]
UnexpectedEntityKind,
#[error("failed reading total balance")]
TotalBalanceReadFailure,
#[error("total balance exceeded u64::MAX")]
TotalBalanceOverflow,
#[error("remaining gas exceeded the gas limit")]
RemainingGasExceedsGasLimit,
#[error("account not found under key")]
AccountRecordNotFound,
#[error("message did not have a checksum")]
MessageChecksumMissing,
}
#[derive(Debug, Error)]
pub enum VMError {
#[error("Return 0x{flags:?} {data:?}")]
Return {
flags: ReturnFlags,
data: Option<Bytes>,
},
#[error("export: {0}")]
Export(ExportError),
#[error("Out of gas")]
OutOfGas,
#[error("Trap: {0}")]
Trap(TrapCode),
#[error("Internal host error")]
Internal(#[from] InternalHostError),
#[error("Execute error: {0}")]
Execute(#[from] ExecuteError),
}
impl VMError {
pub fn into_output_data(self) -> Option<Bytes> {
match self {
VMError::Return { data, .. } => data,
_ => None,
}
}
}
pub type VMResult<T> = Result<T, VMError>;
#[derive(Clone, Debug)]
pub struct Config {
gas_limit: u64,
memory_limit: u32,
}
impl Config {
#[must_use]
pub fn gas_limit(&self) -> u64 {
self.gas_limit
}
#[must_use]
pub fn memory_limit(&self) -> u32 {
self.memory_limit
}
}
#[derive(Clone, Debug, Default)]
pub struct ConfigBuilder {
gas_limit: Option<u64>,
memory_limit: Option<u32>,
}
impl ConfigBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_gas_limit(mut self, gas_limit: u64) -> Self {
self.gas_limit = Some(gas_limit);
self
}
#[must_use]
pub fn with_memory_limit(mut self, memory_limit: u32) -> Self {
self.memory_limit = Some(memory_limit);
self
}
#[must_use]
pub fn build(self) -> Config {
let gas_limit = self.gas_limit.expect("Required field missing: gas_limit");
let memory_limit = self
.memory_limit
.expect("Required field missing: memory_limit");
Config {
gas_limit,
memory_limit,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MeteringPoints {
Remaining(u64),
Exhausted,
}
impl MeteringPoints {
pub fn try_into_remaining(self) -> Result<u64, Self> {
if let Self::Remaining(v) = self {
Ok(v)
} else {
Err(self)
}
}
}
pub trait Caller {
type Context;
fn context(&self) -> &Self::Context;
fn context_mut(&mut self) -> &mut Self::Context;
fn bytecode(&self) -> Bytes;
fn has_export(&self, name: &str) -> bool;
fn memory_read(&self, offset: u32, size: usize) -> VMResult<Vec<u8>> {
let mut vec = vec![0; size];
self.memory_read_into(offset, &mut vec)?;
Ok(vec)
}
fn memory_read_into(&self, offset: u32, output: &mut [u8]) -> VMResult<()>;
fn memory_write(&self, offset: u32, data: &[u8]) -> VMResult<()>;
fn alloc(&mut self, idx: u32, size: usize, ctx: u32) -> VMResult<u32>;
fn gas_consumed(&mut self) -> MeteringPoints;
fn consume_gas(&mut self, value: u64) -> VMResult<()>;
}
#[derive(Debug, Error)]
pub enum WasmPreparationError {
#[error("Missing export {0}")]
MissingExport(String),
#[error("Compile error: {0}")]
Compile(String),
#[error("Memory instantiation error: {0}")]
Memory(String),
#[error("Instantiation error: {0}")]
Instantiation(String),
}
#[derive(Debug)]
pub struct GasUsage {
gas_limit: u64,
remaining_points: u64,
}
impl GasUsage {
#[must_use]
pub fn new(gas_limit: u64, remaining_points: u64) -> Self {
GasUsage {
gas_limit,
remaining_points,
}
}
#[must_use]
pub fn gas_spent(&self) -> u64 {
debug_assert!(self.remaining_points <= self.gas_limit);
self.gas_limit - self.remaining_points
}
#[must_use]
pub fn gas_limit(&self) -> u64 {
self.gas_limit
}
#[must_use]
pub fn remaining_points(&self) -> u64 {
self.remaining_points
}
}
pub trait WasmInstance {
type Context;
fn call_export(&mut self, name: &str) -> (Result<(), VMError>, GasUsage);
fn teardown(self) -> Self::Context;
}