corevm-engine 0.1.28

CoreVM engine that drives program execution either on the builder or CoreVM service side
Documentation
use corevm_host::{fs, Outcome, PageNum};
use jam_pvm_common::ApiError;

/// Engine initialization error.
#[derive(thiserror::Error, Debug)]
pub enum InitError {
	#[error("JAM error: {0:?}")]
	Jam(ApiError),
	#[error("CoreVM FS error: {0:?}")]
	Fs(fs::Error),
	#[error("Invalid program blob")]
	ProgramBlob,
	#[error("Invalid exec blob")]
	ExecBlob,
	#[error("None or both `main` and `_pvm_start` entry points were found")]
	EntryPoint,
	#[error("Invalid memory page was imported into the package")]
	InvalidPage,
	#[error("Input limit reached while initializing the engine")]
	InputLimitReached,
}

impl From<polkavm::program::ProgramParseError> for InitError {
	fn from(_: polkavm::program::ProgramParseError) -> Self {
		Self::ProgramBlob
	}
}

impl From<ApiError> for InitError {
	fn from(e: ApiError) -> Self {
		Self::Jam(e)
	}
}

impl From<fs::Error> for InitError {
	fn from(e: fs::Error) -> Self {
		Self::Fs(e)
	}
}

/// Fatal engine error after which the continuation is not possible.
#[derive(thiserror::Error, Debug)]
pub enum FatalError {
	#[error("Input space limit reached while restarting a host-call")]
	NotEnoughInputSpace,
	#[error("Output space limit reached while restarting a host-call")]
	NotEnoughOutputSpace,
	#[error("Unknown host-call")]
	UnknownHostCall,
	#[error("Guest attempted to read/write reserved/unmapped memory address")]
	InvalidMemoryAccess,
}

/// Engine runtime error.
#[derive(thiserror::Error, Debug)]
pub enum RunError {
	#[error("JAM error: {0:?}")]
	Jam(ApiError),
	#[error("CoreVM FS error: {0:?}")]
	Fs(fs::Error),
	#[error("Fatal error: {0:?}")]
	Fatal(FatalError),
	#[error("Failed to initialize libc")]
	LibcInit,
}

impl From<ApiError> for RunError {
	fn from(e: ApiError) -> Self {
		Self::Jam(e)
	}
}

impl From<fs::Error> for RunError {
	fn from(e: fs::Error) -> Self {
		Self::Fs(e)
	}
}

impl From<FatalError> for RunError {
	fn from(e: FatalError) -> Self {
		Self::Fatal(e)
	}
}

#[derive(Debug)]
pub(crate) enum HostCallError {
	Jam(ApiError),
	Fs(fs::Error),
	Outcome(Outcome),
	Fatal(FatalError),
}

impl From<Outcome> for HostCallError {
	fn from(outcome: Outcome) -> Self {
		Self::Outcome(outcome)
	}
}

impl From<TimeLimitReached> for HostCallError {
	fn from(_: TimeLimitReached) -> Self {
		Self::Outcome(Outcome::TimeLimitReached)
	}
}

impl From<OutputLimitReached> for HostCallError {
	fn from(_: OutputLimitReached) -> Self {
		Self::Outcome(Outcome::OutputLimitReached)
	}
}

impl From<InputLimitReached> for HostCallError {
	fn from(_: InputLimitReached) -> Self {
		Self::Outcome(Outcome::InputLimitReached)
	}
}

impl From<ApiError> for HostCallError {
	fn from(e: ApiError) -> Self {
		Self::Jam(e)
	}
}

impl From<fs::Error> for HostCallError {
	fn from(e: fs::Error) -> Self {
		Self::Fs(e)
	}
}

impl From<FatalError> for HostCallError {
	fn from(e: FatalError) -> Self {
		Self::Fatal(e)
	}
}

impl From<TouchError> for HostCallError {
	fn from(e: TouchError) -> Self {
		match e {
			TouchError::PageFault { page, num_pages } =>
				Self::Outcome(Outcome::PageFault { page, num_pages }),
			TouchError::Jam(e) => Self::Jam(e),
			TouchError::InvalidMemoryAccess => Self::Fatal(FatalError::InvalidMemoryAccess),
			TouchError::InputLimitReached => Self::Outcome(Outcome::InputLimitReached),
		}
	}
}

impl From<polkakernel::MachineError> for HostCallError {
	fn from(e: polkakernel::MachineError) -> Self {
		match e {
			polkakernel::MachineError::BadAddress => Self::Fatal(FatalError::InvalidMemoryAccess),
		}
	}
}

impl From<TooLargeWorkOutput> for HostCallError {
	fn from(_: TooLargeWorkOutput) -> Self {
		Self::Outcome(Outcome::OutputLimitReached)
	}
}

#[derive(Debug)]
pub(crate) enum TouchError {
	PageFault { page: PageNum, num_pages: u32 },
	Jam(ApiError),
	InvalidMemoryAccess,
	InputLimitReached,
}

impl From<ApiError> for TouchError {
	fn from(e: ApiError) -> Self {
		Self::Jam(e)
	}
}

impl From<InputLimitReached> for TouchError {
	fn from(_: InputLimitReached) -> Self {
		Self::InputLimitReached
	}
}

/// The guest produced/consumed one time-slot worth of video frames and audio samples.
#[derive(Debug)]
pub(crate) struct TimeLimitReached;

/// The guest produced `WorkItem::export_count` worth of export segments.
///
/// These include updated memory pages, video/audio/console output and page map.
#[derive(Debug)]
pub(crate) struct OutputLimitReached;

/// Max. work output size exceeded.
#[derive(Debug)]
pub(crate) struct TooLargeWorkOutput;

/// The guest reached [`max_input`](jam_types::max_input) input size.
///
/// This can happen on page import or while reading input stream or receiving a message.
/// This error is only used in the simulator and should never occur on JAM node.
#[derive(Debug)]
pub(crate) struct InputLimitReached;