corevm_engine/
host_calls.rs

1use crate::ProgramData;
2use alloc::vec::Vec;
3use core::ops::Range;
4use corevm_host::{
5	FileRef, PageSegment, MAX_FILE_BLOCK_LEN, MAX_FILE_LEN, MIN_FILE_BLOCK_LEN, PAGE_SIZE,
6};
7use jam_pvm_common::{ApiError, InvokeOutcome};
8use jam_types::{Hash, ServiceId, SignedGas};
9
10/// A trait that abstracts JAM calls related to an inner VM.
11pub trait InnerVm {
12	fn void(&mut self, page: u64, num_pages: u64) -> Result<(), ApiError>;
13	fn zero(&mut self, page: u64, num_pages: u64) -> Result<(), ApiError>;
14	fn poke(&mut self, outer_src: &[u8], inner_dst: u64) -> Result<(), ApiError>;
15	fn peek_into(&mut self, outer_dst: &mut [u8], inner_src: u64) -> Result<(), ApiError>;
16	fn invoke(
17		&mut self,
18		gas: SignedGas,
19		regs: [u64; 13],
20	) -> Result<(InvokeOutcome, SignedGas, [u64; 13]), ApiError>;
21	fn expunge(self) -> Result<u64, ApiError>;
22
23	fn zero_poke(&mut self, outer_src: &[u8], inner_dst: u64) -> Result<(), ApiError> {
24		// Make pages writable by calling `zero`.
25		let page = inner_dst / PAGE_SIZE;
26		let num_pages = (outer_src.len() as u64).div_ceil(PAGE_SIZE);
27		self.zero(page, num_pages)?;
28		// Copy the data.
29		self.poke(outer_src, inner_dst)?;
30		Ok(())
31	}
32
33	fn poke_ro_rw_data_page(
34		&mut self,
35		data: &[u8],
36		address_range: &Range<u64>,
37		address: u64,
38	) -> Result<(), ApiError> {
39		debug_assert_eq!(0, address % PAGE_SIZE);
40		debug_assert!(address_range.contains(&address));
41		let data_start = address - address_range.start;
42		if data_start < data.len() as u64 {
43			// copy non-zero data
44			let data_start = data_start as usize;
45			let data_end = (data_start + PAGE_SIZE as usize).min(data.len());
46			let data_range = data_start..data_end;
47			self.zero_poke(&data[data_range], address)?;
48		} else {
49			// zero-out the remaining data
50			let page = address / PAGE_SIZE;
51			self.zero(page, 1)?;
52		}
53		Ok(())
54	}
55
56	fn poke_ro_data_page(
57		&mut self,
58		address: u64,
59		program_data: &ProgramData,
60	) -> Result<(), ApiError> {
61		self.poke_ro_rw_data_page(&program_data.ro_data[..], program_data.ro_data_range(), address)
62	}
63
64	fn poke_rw_data_page(
65		&mut self,
66		address: u64,
67		program_data: &ProgramData,
68	) -> Result<(), ApiError> {
69		self.poke_ro_rw_data_page(&program_data.rw_data[..], program_data.rw_data_range(), address)
70	}
71
72	fn zero_stack_page(
73		&mut self,
74		address: u64,
75		program_data: &ProgramData,
76	) -> Result<(), ApiError> {
77		debug_assert_eq!(0, address % PAGE_SIZE);
78		debug_assert!(program_data.stack_range().contains(&address));
79		let page = address / PAGE_SIZE;
80		self.zero(page, 1)
81	}
82
83	fn zero_heap_page(&mut self, address: u64, program_data: &ProgramData) -> Result<(), ApiError> {
84		debug_assert_eq!(0, address % PAGE_SIZE);
85		debug_assert!(program_data.heap_range().contains(&address));
86		let page = address / PAGE_SIZE;
87		self.zero(page, 1)
88	}
89}
90
91pub trait OuterVm {
92	type InnerVm: crate::InnerVm;
93
94	fn import(&mut self, i: usize) -> Option<PageSegment>;
95	fn export(&mut self, segment: &[u8]) -> Result<(), ApiError>;
96	fn lookup(&mut self, service_id: ServiceId, hash: &Hash) -> Option<Vec<u8>>;
97	fn get_export_count(&mut self) -> u16;
98	fn get_auth_output_len(&mut self) -> u32;
99	fn machine(&mut self, code: &[u8], program_counter: u64) -> Result<Self::InnerVm, ApiError>;
100
101	fn read_file(&mut self, file: &FileRef) -> Option<Vec<u8>> {
102		let mut data = Vec::new();
103		let mut next_hash = file.hash;
104		while next_hash != [0; 32] {
105			let block = self.lookup(file.service_id, &next_hash)?;
106			if !(MIN_FILE_BLOCK_LEN..=MAX_FILE_BLOCK_LEN).contains(&(block.len() as u64)) {
107				// The file block is too large.
108				return None;
109			}
110			if data.len() + block.len() > MAX_FILE_LEN as usize {
111				// The file is too large.
112				return None;
113			}
114			data.extend_from_slice(&block[32..]);
115			next_hash = block[..32].try_into().expect("The length is 32");
116		}
117		Some(data)
118	}
119}