corevm_host/package.rs
1use crate::{FileRef, OutputStream, RangeSet, VideoMode};
2use codec::{ConstEncodedLen, Decode, Encode, MaxEncodedLen};
3use jam_types::{CodeHash, Hash, SegmentTreeRoot, ServiceId, SignedGas, VecMap, SEGMENT_LEN};
4
5/// CoreVM-specific [work payload](jam_types::WorkPayload).
6///
7/// This is the input of the work package.
8#[derive(Encode, Decode, Debug, Clone)]
9pub struct CoreVmPayload {
10 /// Inner VM gas.
11 pub gas: SignedGas,
12 /// The prior (known) VM state from which the program run should continue.
13 ///
14 /// Equals [`VmState::initial`] on the first program run.
15 pub vm_state: VmState,
16 /// Program location in the virtual file system.
17 pub program: FileRef,
18}
19
20/// CoreVM-specific [work output](jam_types::WorkOutput).
21///
22/// This is the output of the work package.
23#[derive(Encode, Decode, Debug)]
24pub struct CoreVmOutput {
25 /// The specification of the VM's output data.
26 pub vm_output: VmOutput,
27 /// The posterior VM state on which the program run was suspended.
28 ///
29 /// Use this state in [`CorevmPayload`] to continue the execution in another work package.
30 pub vm_state: VmState,
31 pub old_hash: Hash,
32 pub new_hash: Hash,
33 /// Memory pages that were imported and read from/written to while refining the package.
34 pub touched_imported_pages: VecMap<u64, Hash>,
35 /// Memory pages that were read from/written to while refining this work package.
36 ///
37 /// Include deallocated pages indicated by zero hashes.
38 pub updated_pages: VecMap<u64, Hash>,
39 /// Video output mode.
40 ///
41 /// `None` if the mode was not changed in this program run.
42 pub video: Option<VideoMode>,
43 /// The guest code hash.
44 pub guest_code_hash: Hash,
45 /// The host service for the guest code.
46 pub guest_code_host: ServiceId,
47}
48
49/// The specification of the VM's output data.
50///
51/// Includes the information that is needed to decode work package exports and also the final state
52/// after the VM invocation (remaining gas and the outcome).
53///
54/// Exports have the following structure:
55///
56/// `[ memory pages | output stream 0 | ... | output stream N | null segments ]`
57#[derive(Encode, Decode, Debug)]
58pub struct VmOutput {
59 /// Remainig inner VM gas.
60 pub remaining_gas: SignedGas,
61 /// The outcome of the inner VM invocation.
62 pub outcome: Outcome,
63 /// The no. of exported memory pages.
64 pub num_memory_pages: u32,
65 /// Per-stream output size.
66 pub stream_len: [u32; OutputStream::COUNT],
67}
68
69impl VmOutput {
70 /// Get the size of the stream `i`.
71 pub fn stream_len(&self, i: OutputStream) -> u32 {
72 self.stream_len[i as usize - 1]
73 }
74
75 /// Get the number of output segments.
76 pub fn num_output_segments(&self) -> u32 {
77 self.stream_len.iter().sum::<u32>().div_ceil(SEGMENT_LEN as u32)
78 }
79
80 /// Get the start and end offset of the specified stream.
81 ///
82 /// The offsets are measured from the first exported segment.
83 pub fn get_stream_range(&self, stream: OutputStream) -> (u32, u32) {
84 let mut start = 0;
85 let mut end = 0;
86 for i in OutputStream::ALL {
87 let len = self.stream_len[i as usize - 1];
88 if i == stream {
89 end = start + len;
90 break;
91 }
92 start += len;
93 }
94 let offset = self.num_memory_pages * SEGMENT_LEN as u32;
95 (offset + start, offset + end)
96 }
97}
98
99/// The result code returned by CoreVM service.
100///
101/// It is similar to [`InvokeOutcome`](jam_pvm_common::InvokeOutcome) but does not include host-call
102/// faults because they are automatically handled by CoreVM.
103#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Eq, Clone, Copy)]
104pub enum Outcome {
105 /// Completed normally.
106 Halt,
107 /// Completed with a panic.
108 Panic,
109 /// Completed with a page fault that couldn't be handled by CoreVM.
110 ///
111 /// Undhandled page fault might occur because _either_ the max. no. of exports is reached _or_
112 /// the corresponding page was not imported by the work package. In the former case the
113 /// program needs to be resumed in the next work package without any further action from the
114 /// builder. In the latter case the page with the specified address has to be imported on the
115 /// next program run to continue.
116 PageFault { page: u64, num_pages: u64 },
117 /// Completed by running out of gas.
118 OutOfGas,
119 /// The inner VM has reached the max. no. of exports while appending new data to the output
120 /// stream.
121 OutputLimitReached,
122}
123
124impl core::fmt::Debug for Outcome {
125 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
126 match self {
127 Self::Halt => f.write_str("Halt"),
128 Self::Panic => f.write_str("Panic"),
129 Self::PageFault { page, num_pages } =>
130 f.debug_tuple("PageFault").field(&format_args!("{page}+{num_pages}")).finish(),
131 Self::OutOfGas => f.write_str("OutOfGas"),
132 Self::OutputLimitReached => f.write_str("OutputLimitReached"),
133 }
134 }
135}
136
137impl ConstEncodedLen for Outcome {}
138
139/// Inner VM state.
140#[derive(Encode, Decode, Debug, Clone)]
141pub struct VmState {
142 /// Registers.
143 pub regs: [u64; 13],
144 /// Program counter.
145 pub program_counter: u64,
146 /// The current frame number.
147 pub frame_number: u64,
148 /// Mapped heap memory pages' indices.
149 ///
150 /// These pages were allocated by the guest but not necessarily read from/written to.
151 ///
152 /// Note that it's possible to include only a subset of the pages if you're sure that they will
153 /// not be touched again during the program run. Howeve,r if such a page is touched the engine
154 /// will panic.
155 pub mapped_heap_pages: RangeSet,
156 /// All memory pages that were read from/written to by the guest but haven't been unmapped
157 /// yet.
158 ///
159 /// Note that it's possible to include only a subset of the pages if you're sure that they will
160 /// not be touched again during the program run. However, if such a page is touched the
161 /// engine will silently initialize it from the default: from zeroes for stack/heap and from
162 /// the program's RW data section for RW data. The program might continue without an error
163 /// after that, but the output will most likely be incorrect.
164 pub resident_pages: RangeSet,
165}
166
167impl VmState {
168 /// Create initial state.
169 pub const fn initial() -> Self {
170 Self {
171 regs: [0; 13],
172 program_counter: 0,
173 frame_number: 0,
174 mapped_heap_pages: RangeSet::new(),
175 resident_pages: RangeSet::new(),
176 }
177 }
178}
179
180/// Storage keys used by CoreVM.
181#[derive(Encode, Debug)]
182pub enum StorageKey {
183 /// The hash of this CoreVM instance.
184 ///
185 /// Computed with [`corevm_engine::compute_state_hash`].
186 StateHash,
187 /// The hash of the program blob.
188 ///
189 /// This is the first hash of the file.
190 ProgramHash,
191 /// The last known hash of the memory page with the specified address.
192 PageHash(u64),
193 /// The output data specification of the VM.
194 ///
195 /// An instance of [`VmSpec`](crate::VmSpec).
196 VmSpec,
197 /// The current video mode.
198 ///
199 /// An instance of [`VideoMode`](crate::VideoMode).
200 VideoMode,
201 /// The hash and service host of the guest code.
202 GuestCode,
203 /// The owner of this CoreVM instance; they can reset the code.
204 Owner,
205 /// The pages currently in active use by the program.
206 ResidentPages,
207}
208
209/// Same as [`CoreVmOutput`] but only includes the data necessary to continue program execution.
210#[derive(Encode, Decode, Debug)]
211pub struct VmSpec {
212 /// Segment-root of the package exports where the actual output is stored.
213 pub exports_root: SegmentTreeRoot,
214 /// The specification of the VM's output data.
215 pub output: VmOutput,
216 pub state: VmState,
217}
218
219/// Instruction to send to the CoreVM service.
220#[derive(Encode, Decode, Debug)]
221pub enum CoreVmInstruction {
222 /// Set the code or reset it to something new.
223 SetCode {
224 /// The code hash.
225 code_hash: CodeHash,
226 /// The service ID which hosts the code.
227 host: ServiceId,
228 },
229 /// Set the owner of this CoreVM instance.
230 SetOwner(ServiceId),
231}