corevm_engine/
host_calls.rs

1use crate::ProgramData;
2use alloc::vec::Vec;
3use bytes::Bytes;
4use core::ops::Range;
5use corevm_host::{
6	fs::{self, ReadBlock},
7	PAGE_SIZE,
8};
9use jam_pvm_common::{ApiError, InvokeOutcome};
10use jam_types::{Segment, SignedGas};
11
12/// A trait that abstracts JAM calls related to an inner VM.
13pub trait InnerVm {
14	fn void(&mut self, page: u64, num_pages: u64) -> Result<(), ApiError>;
15	fn zero(&mut self, page: u64, num_pages: u64) -> Result<(), ApiError>;
16	fn poke(&mut self, outer_src: &[u8], inner_dst: u64) -> Result<(), ApiError>;
17	fn peek_into(&mut self, outer_dst: &mut [u8], inner_src: u64) -> Result<(), ApiError>;
18	fn invoke(
19		&mut self,
20		gas: SignedGas,
21		regs: [u64; 13],
22	) -> Result<(InvokeOutcome, SignedGas, [u64; 13]), ApiError>;
23	fn expunge(self) -> Result<u64, ApiError>;
24
25	fn zero_poke(&mut self, outer_src: &[u8], inner_dst: u64) -> Result<(), ApiError> {
26		// Make pages writable by calling `zero`.
27		let page = inner_dst / PAGE_SIZE;
28		let num_pages = (outer_src.len() as u64).div_ceil(PAGE_SIZE);
29		self.zero(page, num_pages)?;
30		// Copy the data.
31		self.poke(outer_src, inner_dst)?;
32		Ok(())
33	}
34
35	fn poke_ro_rw_data_page(
36		&mut self,
37		data: &[u8],
38		address_range: &Range<u64>,
39		address: u64,
40	) -> Result<(), ApiError> {
41		debug_assert_eq!(0, address % PAGE_SIZE);
42		debug_assert!(address_range.contains(&address));
43		let data_start = address - address_range.start;
44		if data_start < data.len() as u64 {
45			// copy non-zero data
46			let data_start = data_start as usize;
47			let data_end = (data_start + PAGE_SIZE as usize).min(data.len());
48			let data_range = data_start..data_end;
49			self.zero_poke(&data[data_range], address)?;
50		} else {
51			// zero-out the remaining data
52			let page = address / PAGE_SIZE;
53			self.zero(page, 1)?;
54		}
55		Ok(())
56	}
57
58	fn poke_ro_data_page(
59		&mut self,
60		address: u64,
61		program_data: &ProgramData,
62	) -> Result<(), ApiError> {
63		self.poke_ro_rw_data_page(&program_data.ro_data[..], program_data.ro_data_range(), address)
64	}
65
66	fn poke_rw_data_page(
67		&mut self,
68		address: u64,
69		program_data: &ProgramData,
70	) -> Result<(), ApiError> {
71		self.poke_ro_rw_data_page(&program_data.rw_data[..], program_data.rw_data_range(), address)
72	}
73
74	fn zero_stack_page(
75		&mut self,
76		address: u64,
77		program_data: &ProgramData,
78	) -> Result<(), ApiError> {
79		debug_assert_eq!(0, address % PAGE_SIZE);
80		debug_assert!(program_data.stack_range().contains(&address));
81		let page = address / PAGE_SIZE;
82		self.zero(page, 1)
83	}
84
85	fn zero_heap_page(&mut self, address: u64, program_data: &ProgramData) -> Result<(), ApiError> {
86		debug_assert_eq!(0, address % PAGE_SIZE);
87		debug_assert!(program_data.heap_range().contains(&address));
88		let page = address / PAGE_SIZE;
89		self.zero(page, 1)
90	}
91}
92
93/// A trait that abstracts JAM host-calls available in a refine environment and related to an outer
94/// VM.
95pub trait OuterVm {
96	/// Inner VM wrapper.
97	type InnerVm: crate::InnerVm;
98
99	/// Returns a memory page with the specified address that was imported into work package or
100	/// `None` if such page doesn't exist.
101	fn get_imported_page(&mut self, address: u64) -> Option<Segment>;
102
103	/// Export `segment` from the work package.
104	///
105	/// The segment length must not be longer than [`SEGMENT_LEN`](jam_types::SEGMENT_LEN).
106	/// If it's shorter, the rest of of the bytes are zeroed.
107	fn export(&mut self, segment: &[u8]) -> Result<(), ApiError>;
108
109	/// Reads file system block specified by its reference.
110	fn read_file_block(&mut self, block_ref: &fs::BlockRef) -> Option<Bytes>;
111
112	/// Returns the number of exports that the current work package has.
113	fn get_export_count(&mut self) -> u16;
114
115	/// Returns the length of the authorizer output.
116	fn get_auth_output_len(&mut self) -> u32;
117
118	/// Creates a new inner VM and returns its wrapped handle.
119	fn machine(&mut self, code: &[u8], program_counter: u64) -> Result<Self::InnerVm, ApiError>;
120
121	/// Returns the contents of a file specified by its main block reference.
122	///
123	/// Uses [`read_file_block`](Self::read_file_block) to read file blocks.
124	fn read_file(&mut self, block_ref: &fs::BlockRef) -> Result<Vec<u8>, fs::Error> {
125		let mut block_reader = self.block_reader();
126		let buf = fs::read(block_ref, &mut block_reader)?;
127		Ok(buf)
128	}
129
130	/// Returns file system block reader that uses [`read_file_block`](Self::read_file_block) to
131	/// read file blocks.
132	fn block_reader(&mut self) -> Lookup<'_, Self> {
133		Lookup { outer_vm: self }
134	}
135}
136
137/// An implementation of [`ReadBlock`] that uses `OuterVM::read_file_block` to read files blocks.
138pub struct Lookup<'a, O: OuterVm + ?Sized> {
139	pub outer_vm: &'a mut O,
140}
141
142impl<O: OuterVm + ?Sized> ReadBlock for Lookup<'_, O> {
143	fn read_block(&mut self, block_ref: &fs::BlockRef) -> Result<Bytes, fs::IoError> {
144		self.outer_vm.read_file_block(block_ref).ok_or(fs::IoError)
145	}
146}