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 {}