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}