1use javm::{KernelImage, kernel_image_hash};
19use javm_cap::abi as cap_abi;
20use javm_cap::image::Image;
21use javm_cap::{CNodeCap, CacheDirectory, Cap, CapHash, CapHashOrRef, NUM_REGS, SlotIdx};
22
23use crate::abi;
24use crate::error::KernelError;
25use crate::state::State;
26
27pub struct Genesis {
30 pub state: State,
31 pub chain_instance_hash: CapHash,
32 pub chain_image_hash: CapHash,
33 pub root_cnode_hash: CapHash,
34}
35
36fn publish_kernel_unit_cap(
42 cache: &mut CacheDirectory,
43 image: KernelImage,
44 placeholder_image_hash: CapHash,
45 empty_cnode_hash: CapHash,
46 id: u64,
47) -> Result<CapHash, KernelError> {
48 let mut regs = [0u64; NUM_REGS];
49 regs[0] = id;
50 let cap = Cap::instance_with_overlays(
51 kernel_image_hash(image),
52 placeholder_image_hash,
53 empty_cnode_hash,
54 &[],
55 0,
56 regs,
57 0,
58 0,
59 );
60 Ok(cache.put_cap(&cap)?)
61}
62
63fn publish_kernel_stateless_cap(
67 cache: &mut CacheDirectory,
68 image: KernelImage,
69 placeholder_image_hash: CapHash,
70 empty_cnode_hash: CapHash,
71) -> Result<CapHash, KernelError> {
72 publish_kernel_unit_cap(cache, image, placeholder_image_hash, empty_cnode_hash, 0)
73}
74
75pub fn genesis(chain_image: Image) -> Result<Genesis, KernelError> {
82 let mut state = State::new();
83
84 use javm_cap::image::PinnedCap;
88 let mut chain_pinned_hashes: Vec<(SlotIdx, CapHash)> = Vec::new();
89 let mut chain_initial_hashes: Vec<(SlotIdx, CapHash)> = Vec::new();
90 for (slot, pinned) in &chain_image.pinned_slots {
91 let h = match pinned {
92 PinnedCap::Data { content, size } => state
93 .caps
94 .put_cap(&Cap::data_inline_with_size(content, *size))?,
95 PinnedCap::Image { content_hash } => *content_hash,
96 };
97 chain_pinned_hashes.push((*slot, h));
98 }
99 for (slot, init) in &chain_image.initial_slots {
100 let h = state
101 .caps
102 .put_cap(&Cap::data_inline_with_size(&init.content, init.size))?;
103 chain_initial_hashes.push((*slot, h));
104 }
105
106 let chain_image_hash = state.caps.put_cap(&Cap::image_with_slots(
108 &chain_image,
109 &chain_pinned_hashes,
110 &chain_initial_hashes,
111 )?)?;
112
113 let placeholder_image_hash = state.caps.put_cap(&Cap::image_with_slots(
119 &placeholder_kernel_image(),
120 &[],
121 &[],
122 )?)?;
123
124 let empty_cnode_hash = state.caps.put_cap(&Cap::empty_cnode(0)?)?;
128
129 let gas_hash = publish_kernel_unit_cap(
131 &mut state.caps,
132 KernelImage::Gas,
133 placeholder_image_hash,
134 empty_cnode_hash,
135 0,
136 )?;
137 let quota_hash = publish_kernel_unit_cap(
138 &mut state.caps,
139 KernelImage::Quota,
140 placeholder_image_hash,
141 empty_cnode_hash,
142 0,
143 )?;
144 let yield_catcher_hash = publish_kernel_stateless_cap(
145 &mut state.caps,
146 KernelImage::YieldCatcher,
147 placeholder_image_hash,
148 empty_cnode_hash,
149 )?;
150 let set_gas_meter_hash = publish_kernel_stateless_cap(
151 &mut state.caps,
152 KernelImage::SetGasMeter,
153 placeholder_image_hash,
154 empty_cnode_hash,
155 )?;
156 let set_storage_quota_hash = publish_kernel_stateless_cap(
157 &mut state.caps,
158 KernelImage::SetStorageQuota,
159 placeholder_image_hash,
160 empty_cnode_hash,
161 )?;
162 let mint_gas_hash = publish_kernel_stateless_cap(
163 &mut state.caps,
164 KernelImage::MintGas,
165 placeholder_image_hash,
166 empty_cnode_hash,
167 )?;
168 let mint_quota_hash = publish_kernel_stateless_cap(
169 &mut state.caps,
170 KernelImage::MintQuota,
171 placeholder_image_hash,
172 empty_cnode_hash,
173 )?;
174 let create_yc_hash = publish_kernel_stateless_cap(
175 &mut state.caps,
176 KernelImage::CreateYieldCatcher,
177 placeholder_image_hash,
178 empty_cnode_hash,
179 )?;
180 let host_open_hash = publish_kernel_stateless_cap(
181 &mut state.caps,
182 KernelImage::HostOpen,
183 placeholder_image_hash,
184 empty_cnode_hash,
185 )?;
186 let host_save_hash = publish_kernel_stateless_cap(
187 &mut state.caps,
188 KernelImage::HostSave,
189 placeholder_image_hash,
190 empty_cnode_hash,
191 )?;
192
193 let mut entries: Vec<(SlotIdx, CapHashOrRef)> = vec![
199 (abi::BARE_GAS_SLOT, CapHashOrRef::Hash(gas_hash)),
200 (abi::BARE_QUOTA_SLOT, CapHashOrRef::Hash(quota_hash)),
201 (
202 abi::BARE_YIELD_CATCHER_SLOT,
203 CapHashOrRef::Hash(yield_catcher_hash),
204 ),
205 (
206 abi::BARE_SET_GAS_METER_SLOT,
207 CapHashOrRef::Hash(set_gas_meter_hash),
208 ),
209 (
210 abi::BARE_SET_STORAGE_QUOTA_SLOT,
211 CapHashOrRef::Hash(set_storage_quota_hash),
212 ),
213 (abi::BARE_MINT_GAS_SLOT, CapHashOrRef::Hash(mint_gas_hash)),
214 (
215 abi::BARE_MINT_QUOTA_SLOT,
216 CapHashOrRef::Hash(mint_quota_hash),
217 ),
218 (
219 abi::BARE_CREATE_YIELD_CATCHER_SLOT,
220 CapHashOrRef::Hash(create_yc_hash),
221 ),
222 (abi::BARE_HOST_OPEN_SLOT, CapHashOrRef::Hash(host_open_hash)),
223 (abi::BARE_HOST_SAVE_SLOT, CapHashOrRef::Hash(host_save_hash)),
224 ];
225
226 for (slot, h) in &chain_pinned_hashes {
229 entries.push((*slot, CapHashOrRef::Hash(*h)));
230 }
231 for (slot, h) in &chain_initial_hashes {
232 entries.push((*slot, CapHashOrRef::Hash(*h)));
233 }
234
235 let root_cnode_hash = {
237 let mut cnode = CNodeCap::new(8).map_err(KernelError::from)?;
238 for (slot, target) in &entries {
239 cnode
240 .set(*slot, Some(target.clone()))
241 .map_err(KernelError::from)?;
242 }
243 state.caps.put_cap(&Cap::CNode(cnode))?
244 };
245
246 let (mem_size, overlays) = build_overlays(&chain_image);
251 let overlay_slices: Vec<(u32, &[u8])> = overlays
252 .iter()
253 .map(|(start, bytes)| (*start, bytes.as_slice()))
254 .collect();
255
256 let chain_instance_hash = state.caps.put_cap(&Cap::instance_with_overlays(
261 chain_image_hash,
262 chain_image_hash,
263 root_cnode_hash,
264 &overlay_slices,
265 mem_size,
266 [0u64; NUM_REGS],
267 0,
268 0,
269 ))?;
270
271 let _ = cap_abi::BARE_GAS_SLOT; Ok(Genesis {
274 state,
275 chain_instance_hash,
276 chain_image_hash,
277 root_cnode_hash,
278 })
279}
280
281pub(crate) fn build_overlays(image: &Image) -> (u32, Vec<(u32, Vec<u8>)>) {
290 use javm_cap::image::PinnedCap;
291 let mut mem_size: u32 = 0;
292 let mut overlays: Vec<(u32, Vec<u8>)> = Vec::new();
293
294 for mapping in &image.memory_mappings {
295 let end = (mapping.start + mapping.size) as u32;
296 if end > mem_size {
297 mem_size = end;
298 }
299
300 let target = mapping.source.target();
301 if let Some(PinnedCap::Data { content, .. }) = image.pinned_slots.get(&target) {
302 if !content.is_empty() {
303 overlays.push((mapping.start as u32, content.clone()));
304 }
305 } else if let Some(init) = image.initial_slots.get(&target)
306 && !init.content.is_empty()
307 {
308 overlays.push((mapping.start as u32, init.content.clone()));
309 }
310 }
311
312 (mem_size, overlays)
313}
314
315fn placeholder_kernel_image() -> Image {
319 use std::collections::BTreeMap;
320 Image {
321 code: vec![0u8], packed_bitmask: vec![0x01],
323 jump_table: Vec::new(),
324 endpoints: BTreeMap::new(),
325 memory_mappings: Vec::new(),
326 gas_slots: Vec::new(),
327 quota_slots: Vec::new(),
328 pinned_slots: BTreeMap::new(),
329 initial_slots: BTreeMap::new(),
330 yield_marker_slot: None,
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337 use javm_cap::Cap;
338
339 fn empty_chain_image() -> Image {
340 use std::collections::BTreeMap;
341 Image {
342 code: vec![10u8, 0],
343 packed_bitmask: vec![0x03],
345 jump_table: Vec::new(),
346 endpoints: BTreeMap::new(),
347 memory_mappings: Vec::new(),
348 gas_slots: vec![abi::BARE_GAS_SLOT],
349 quota_slots: vec![abi::BARE_QUOTA_SLOT],
350 pinned_slots: BTreeMap::new(),
351 initial_slots: BTreeMap::new(),
352 yield_marker_slot: Some(abi::BARE_YIELD_CATCHER_SLOT),
353 }
354 }
355
356 #[test]
357 fn genesis_publishes_chain_instance() {
358 let g = genesis(empty_chain_image()).expect("genesis");
359 let inst = g
361 .state
362 .caps
363 .get(CapHashOrRef::Hash(g.chain_instance_hash))
364 .expect("chain instance present");
365 assert!(matches!(&*inst, Cap::Instance(_)));
366 }
367
368 #[test]
369 fn genesis_populates_root_cnode_with_kernel_caps() {
370 let g = genesis(empty_chain_image()).expect("genesis");
371 let cn_arc = g
372 .state
373 .caps
374 .get(CapHashOrRef::Hash(g.root_cnode_hash))
375 .expect("root cnode present");
376 let cn = match &*cn_arc {
377 Cap::CNode(cn) => cn.clone(),
378 _ => panic!("root cnode is not Cap::CNode"),
379 };
380 assert!(cn.get(abi::BARE_GAS_SLOT).is_some());
381 assert!(cn.get(abi::BARE_QUOTA_SLOT).is_some());
382 assert!(cn.get(abi::BARE_YIELD_CATCHER_SLOT).is_some());
383 assert!(cn.get(abi::BARE_HOST_OPEN_SLOT).is_some());
384 assert!(cn.get(abi::BARE_HOST_SAVE_SLOT).is_some());
385 }
386
387 #[test]
388 fn genesis_is_deterministic() {
389 let g1 = genesis(empty_chain_image()).expect("g1");
390 let g2 = genesis(empty_chain_image()).expect("g2");
391 assert_eq!(g1.chain_instance_hash, g2.chain_instance_hash);
392 assert_eq!(g1.chain_image_hash, g2.chain_image_hash);
393 assert_eq!(g1.root_cnode_hash, g2.root_cnode_hash);
394 }
395}