corevm_host/
package.rs

1use crate::{fs, Arg, AudioMode, OutputStream, PageAddr, RangeSet, VideoMode};
2use alloc::vec::Vec;
3use codec::{ConstEncodedLen, Decode, Encode, MaxEncodedLen};
4use jam_types::{Hash, SegmentTreeRoot, ServiceId, SignedGas, VecMap, MEMO_LEN, SEGMENT_LEN};
5
6/// CoreVM-specific [work payload](jam_types::WorkPayload).
7///
8/// This is the input of the work package.
9#[derive(Encode, Decode, Debug, Clone)]
10pub struct CoreVmPayload {
11	/// Inner VM gas.
12	pub gas: SignedGas,
13	/// The prior (known) VM state from which the program run should continue.
14	///
15	/// Equals [`VmState::initial`] on the first program run.
16	pub vm_state: VmState,
17	/// Execution environment location in the virtual file system.
18	pub exec_ref: fs::BlockRef,
19}
20
21/// CoreVM-specific [work output](jam_types::WorkOutput).
22///
23/// This is the output of the work package.
24#[derive(Encode, Decode, Debug)]
25pub struct CoreVmOutput {
26	/// The specification of the VM's output data.
27	pub vm_output: VmOutput,
28	/// The posterior VM state on which the program run was suspended.
29	///
30	/// Use this state in [`CoreVmPayload`] to continue the execution in another work package.
31	pub vm_state: VmState,
32	pub old_hash: Hash,
33	pub new_hash: Hash,
34	/// Memory pages that were imported and read from/written to while refining the package.
35	pub touched_imported_pages: VecMap<u64, Hash>,
36	/// Memory pages that were read from/written to while refining this work package.
37	///
38	/// Include deallocated pages indicated by zero hashes.
39	pub updated_pages: VecMap<u64, Hash>,
40	/// Execution environment location in the virtual file system.
41	pub exec_ref: fs::BlockRef,
42}
43
44/// The specification of the VM's output data.
45///
46/// Includes the information that is needed to decode work package exports and also the final state
47/// after the VM invocation (remaining gas and the outcome).
48///
49/// Exports have the following structure:
50///
51/// `[ memory pages | output stream 0 | ... | output stream N | null segments ]`
52#[derive(Encode, Decode, Debug)]
53pub struct VmOutput {
54	/// Remainig inner VM gas.
55	pub remaining_gas: SignedGas,
56	/// The outcome of the inner VM invocation.
57	pub outcome: Outcome,
58	/// The no. of exported memory pages.
59	pub num_memory_pages: u32,
60	/// Per-stream output size.
61	pub stream_len: [u32; OutputStream::COUNT],
62}
63
64impl VmOutput {
65	/// Get the size of the stream `i`.
66	pub fn stream_len(&self, i: OutputStream) -> u32 {
67		self.stream_len[i as usize - 1]
68	}
69
70	/// Total length of all streams.
71	pub fn all_streams_len(&self) -> u32 {
72		self.stream_len.iter().sum()
73	}
74
75	/// Returns `true` if all streams are empty.
76	pub fn is_empty(&self) -> bool {
77		self.stream_len.iter().all(|len| *len == 0)
78	}
79
80	/// Get the number of output segments.
81	pub fn num_output_segments(&self) -> u32 {
82		self.stream_len.iter().sum::<u32>().div_ceil(SEGMENT_LEN as u32)
83	}
84
85	/// Get the start and end offset of the specified stream.
86	///
87	/// The offsets are measured from the first exported segment.
88	pub fn get_stream_range(&self, stream: OutputStream) -> (u32, u32) {
89		let mut start = 0;
90		let mut end = 0;
91		for i in OutputStream::ALL {
92			let len = self.stream_len[i as usize - 1];
93			if i == stream {
94				end = start + len;
95				break;
96			}
97			start += len;
98		}
99		let offset = self.num_memory_pages * SEGMENT_LEN as u32;
100		(offset + start, offset + end)
101	}
102}
103
104/// The result code returned by CoreVM service.
105///
106/// It is similar to `jam_pvm_common::InvokeOutcome` but does not include host-call
107/// faults because they are automatically handled by CoreVM.
108#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Eq, Clone, Copy)]
109pub enum Outcome {
110	/// Completed normally.
111	Halt,
112	/// Completed with a panic.
113	Panic,
114	/// Completed with a page fault that couldn't be handled by CoreVM.
115	///
116	/// Undhandled page fault might occur because _either_ the max. no. of exports is reached _or_
117	/// the corresponding page was not imported by the work package. In the former case the
118	/// program needs to be resumed in the next work package without any further action from the
119	/// builder. In the latter case the page with the specified address has to be imported on the
120	/// next program run to continue.
121	PageFault { page: u64, num_pages: u64 },
122	/// Completed by running out of gas.
123	OutOfGas,
124	/// The inner VM has reached the max. no. of exports while appending new data to the output
125	/// stream.
126	OutputLimitReached,
127	/// The program produced one time-slot worth of video frames or audio samples.
128	///
129	/// If both streams are active, then both streams need to have one time-slot worth of data to
130	/// trigger this outcome.
131	TimeLimitReached,
132}
133
134impl core::fmt::Debug for Outcome {
135	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
136		match self {
137			Self::Halt => f.write_str("Halt"),
138			Self::Panic => f.write_str("Panic"),
139			Self::PageFault { page, num_pages } =>
140				f.debug_tuple("PageFault").field(&format_args!("{page}+{num_pages}")).finish(),
141			Self::OutOfGas => f.write_str("OutOfGas"),
142			Self::OutputLimitReached => f.write_str("OutputLimitReached"),
143			Self::TimeLimitReached => f.write_str("TimeLimitReached"),
144		}
145	}
146}
147
148impl ConstEncodedLen for Outcome {}
149
150/// Inner VM state.
151#[derive(Encode, Decode, Debug, Clone)]
152pub struct VmState {
153	/// Registers.
154	pub regs: [u64; 13],
155	/// Program counter.
156	pub program_counter: u64,
157	/// Mapped heap memory pages' indices.
158	///
159	/// These pages were allocated by the guest but not necessarily read from/written to.
160	///
161	/// Note that it's possible to include only a subset of the pages if you're sure that they will
162	/// not be touched again during the program run. Howeve,r if such a page is touched the engine
163	/// will panic.
164	pub mapped_heap_pages: RangeSet,
165	/// All memory pages that were read from/written to by the guest but haven't been unmapped
166	/// yet.
167	///
168	/// Note that it's possible to include only a subset of the pages if you're sure that they will
169	/// not be touched again during the program run. However, if such a page is touched the
170	/// engine will silently initialize it from the default: from zeroes for stack/heap and from
171	/// the program's RW data section for RW data. The program might continue without an error
172	/// after that, but the output will most likely be incorrect.
173	pub resident_pages: RangeSet,
174	/// The state of the Linux kernel syscall layer.
175	pub kernel: KernelState,
176	/// The index of the host-call that needs to be restarted on resuming the program execution.
177	#[doc(hidden)]
178	pub restart_host_call: Option<u64>,
179	/// Video output mode.
180	pub video: Option<VideoMode>,
181	/// Audio output mode.
182	pub audio: Option<AudioMode>,
183}
184
185impl VmState {
186	/// Create initial state.
187	pub const fn initial() -> Self {
188		Self {
189			regs: [0; 13],
190			program_counter: 0,
191			mapped_heap_pages: RangeSet::new(),
192			resident_pages: RangeSet::new(),
193			kernel: KernelState { fds: VecMap::new() },
194			restart_host_call: None,
195			video: None,
196			audio: None,
197		}
198	}
199}
200
201/// Storage keys used by CoreVM.
202#[derive(Encode, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
203#[non_exhaustive]
204pub enum StorageKey {
205	/// Inner VM gas.
206	Gas,
207	/// The hash of this CoreVM instance.
208	///
209	/// Computed with `corevm_engine::compute_state_hash`.
210	StateHash,
211	/// The last known metadata of a memory page with the specified address.
212	///
213	/// An instance of [`PageInfo`](crate::PageInfo).
214	PageInfo(PageAddr),
215	/// The output data specification of the VM.
216	///
217	/// An instance of [`VmSpec`](crate::VmSpec).
218	VmSpec,
219	/// The current video mode.
220	///
221	/// An instance of [`VideoMode`](crate::VideoMode).
222	VideoMode,
223	/// The current audio mode.
224	///
225	/// An instance of [`AudioMode`](crate::AudioMode).
226	AudioMode,
227	/// Execution environment location in the virtual file system.
228	///
229	/// The environment ([`ExecEnv`]) itself is stored as a file.
230	ExecEnvRef,
231	/// The owner of this CoreVM instance; they can reset the code.
232	Owner,
233	/// The pages currently in active use by the program.
234	ResidentPages,
235}
236
237/// Memory page metadata.
238#[derive(Encode, Decode, Debug)]
239pub struct PageInfo {
240	/// The hash of the page segment.
241	pub hash: Hash,
242	/// Segment-root of the package exports where the page contents are stored.
243	pub exports_root: SegmentTreeRoot,
244	/// The index of the exported segment where the page contents are stored.
245	pub export_index: u16,
246}
247
248/// Same as [`CoreVmOutput`] but only includes the data necessary to continue program execution.
249#[derive(Encode, Decode, Debug)]
250pub struct VmSpec {
251	/// Segment-root of the package exports where the actual output is stored.
252	pub exports_root: SegmentTreeRoot,
253	/// The specification of the VM's output data.
254	pub output: VmOutput,
255	pub state: VmState,
256}
257
258/// Guest execution environment.
259#[derive(Encode, Decode, Clone, Debug)]
260pub struct ExecEnv {
261	/// Program location in the virtual file system.
262	pub program: fs::BlockRef,
263	/// Root directory location in the virtual file system.
264	pub root_dir: fs::BlockRef,
265	// TODO @ivan add current working directory
266	/// Command-line arguments.
267	pub args: Vec<Arg>,
268	/// Environment variables.
269	pub env: Vec<Arg>,
270}
271
272/// Instruction to send to the CoreVM service.
273#[derive(Encode, Decode, Debug)]
274pub enum CoreVmInstruction {
275	/// Reset the VM.
276	Reset {
277		/// Inner VM gas.
278		gas: SignedGas,
279		/// Reference to a file where the environment ([`ExecEnv`]) is stored.
280		exec_ref: fs::BlockRef,
281	},
282	/// Set the owner of this CoreVM instance.
283	SetOwner(ServiceId),
284}
285
286impl MaxEncodedLen for CoreVmInstruction {
287	fn max_encoded_len() -> usize {
288		MEMO_LEN
289	}
290}
291
292/// The state of the Linux kernel syscall layer.
293#[derive(Encode, Decode, Clone, Debug, Default)]
294pub struct KernelState {
295	/// Opened file descriptors.
296	pub fds: VecMap<u32, KernelFd>,
297}
298
299/// In-kernel file descriptor state.
300#[derive(Encode, Decode, MaxEncodedLen, Clone, Debug)]
301pub struct KernelFd {
302	/// File reference in the virtual FS.
303	pub block_ref: fs::BlockRef,
304	/// File read/write offset.
305	pub position: u64,
306}
307
308impl ConstEncodedLen for KernelFd {}