corevm 0.1.26

The JAM CoreVM Service, a container service for running regular software on JAM.
Documentation
use bytes::Bytes;
use corevm_engine::{InnerVm, OuterVm};
use corevm_host::{fs, PageSegmentOps, Range, RangeSet, PAGE_SIZE};
use jam_pvm_common::{
	refine::{
		self, export_slice, expunge, foreign_lookup, invoke, machine, peek_into, poke, void, zero,
	},
	ApiError, InvokeOutcome,
};
use jam_types::{PageMode, Segment, SignedGas, VecMap, SEGMENT_LEN};
use log::{debug, trace};

/// An implementation of `OuterVM` that forwards all host-calls to JAM.
#[derive(Default)]
pub struct JamOuterVm {
	imported_pages: VecMap<u64, Segment>,
}

impl JamOuterVm {
	pub fn new() -> Result<Self, ApiError> {
		let mut imported_pages = VecMap::new();
		let mut i = 0;
		while let Some(segment) = import_page(i) {
			let address = segment.page_address();
			let page = address / PAGE_SIZE;
			trace!("Imported page {page}/{address:#x}");
			imported_pages.insert(address, segment);
			i += 1;
		}
		debug!(
			"Imported page(s): {:?}",
			imported_pages
				.iter()
				.map(|(address, _)| {
					let page = address / PAGE_SIZE;
					Range::new(page, page + 1)
				})
				.collect::<RangeSet>()
		);
		Ok(Self { imported_pages })
	}
}

fn import_page(i: usize) -> Option<Segment> {
	let buf = refine::extrinsic_slice(0, i * SEGMENT_LEN, SEGMENT_LEN)?;
	if buf.len() == SEGMENT_LEN {
		Segment::try_from(buf).ok()
	} else {
		None
	}
}

impl OuterVm for JamOuterVm {
	type InnerVm = JamInnerVm;

	fn get_imported_page(&mut self, address: u64) -> Option<Segment> {
		self.imported_pages.get(&address).cloned()
	}

	fn export(&mut self, segment: &[u8]) -> Result<(), ApiError> {
		export_slice(segment)?;
		Ok(())
	}

	fn read_file_block(&mut self, block_ref: &fs::BlockRef) -> Option<Bytes> {
		foreign_lookup(block_ref.service_id, &block_ref.hash.0).map(Into::into)
	}

	fn get_export_count(&mut self) -> u16 {
		let work_package = refine::work_package();
		assert_eq!(1, work_package.items.len());
		work_package.items[0].export_count
	}

	fn get_auth_output_len(&mut self) -> u32 {
		refine::auth_trace().len() as u32
	}

	fn machine(&mut self, code: &[u8], program_counter: u64) -> Result<Self::InnerVm, ApiError> {
		Ok(JamInnerVm(machine(code, program_counter)?))
	}
}

/// An implementation of `InnerVM` that forwards all host-calls to JAM.
pub struct JamInnerVm(pub u64);

impl InnerVm for JamInnerVm {
	fn void(&mut self, page: u64, num_pages: u64) -> Result<(), ApiError> {
		void(self.0, page, num_pages)
	}

	fn zero(&mut self, page: u64, num_pages: u64) -> Result<(), ApiError> {
		zero(self.0, page, num_pages, PageMode::ReadWrite)
	}

	fn poke(&mut self, outer_src: &[u8], inner_dst: u64) -> Result<(), ApiError> {
		poke(self.0, outer_src, inner_dst)
	}

	fn peek_into(&mut self, outer_dst: &mut [u8], inner_src: u64) -> Result<(), ApiError> {
		peek_into(self.0, outer_dst, inner_src)
	}

	fn expunge(self) -> Result<u64, ApiError> {
		expunge(self.0)
	}

	fn invoke(
		&mut self,
		gas: SignedGas,
		regs: [u64; 13],
	) -> Result<(InvokeOutcome, SignedGas, [u64; 13]), ApiError> {
		invoke(self.0, gas, regs)
	}
}