javm_cap/kernel_image.rs
1//! Kernel-assisted Image registry.
2//!
3//! Certain `Cap::Instance` values are recognized by their `image_hash_chain`
4//! as kernel-internal (Gas/Quota unit handles, factories, yield markers). The
5//! kernel short-circuits their state access — no bytecode dispatch. From
6//! userspace they still look like ordinary `Cap::Instance` values.
7//!
8//! This registry lives in `javm-cap` (not the engine crates) so every layer —
9//! the recompiler/interpreter host, the chain genesis, the fuzz harness — agrees
10//! on the well-known `image_hash` for each kernel Image. The hashes are
11//! `Blake2b256(b"kernel:<name>")` placeholders; a later chain-genesis pass can
12//! finalize the canonical encoding (the labels are the source of truth).
13
14use crate::cap::CapHash;
15use crate::hash::{Blake2b256, Hash};
16
17/// Identifies which kernel-assisted Image a given `image_hash_chain` refers to.
18///
19/// The four kernel-assisted Instance variants of the v3 design are
20/// `Gas{meter_key}`, `Quota{quota_key}`, `YieldSender{yield_key}` (the EMIT
21/// right) and `YieldReceiver{Vec<yield_key>}` (the CATCH right), plus the two
22/// kernel-internal tables (`GasMeter`, `StorageQuota`) the unit handles index.
23/// The older per-syscall factory/marker images (set_gas_meter, mint_gas,
24/// yield_catcher, oog_marker, …) are gone: a syscall is now a `host_yield` of a
25/// `YieldSender` carrying a `kernel:*` yield_key, caught by the kernel as the
26/// root YieldReceiver — so those names are yield_keys, not Images.
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
28pub enum KernelImage {
29 /// Kernel-internal gas meter table (never in chain hands).
30 GasMeter,
31 /// Kernel-internal storage quota table (never in chain hands).
32 StorageQuota,
33 /// Per-Instance `Gas{meter_key}` unit handle. State: a `meter_key: Key`
34 /// packed into the handle's registers (see
35 /// [`crate::slot::key_to_regs`]).
36 Gas,
37 /// Per-Instance `Quota{quota_key}` unit handle.
38 Quota,
39 /// `YieldSender{yield_key}` — the EMIT right for a yield_key. State: the
40 /// `yield_key: Key` packed into the handle's registers.
41 YieldSender,
42 /// `YieldReceiver{Vec<yield_key>}` — the CATCH right: the set of yield_keys
43 /// an Instance catches. State (a serialized `Set<Key>`) lives in the
44 /// handle's `mem` DataCap; the kernel short-circuits it during routing.
45 YieldReceiver,
46 /// Per-Instance `File{file_id}` handle.
47 File,
48 /// Per-Instance HostOpen handle.
49 HostOpen,
50 /// Per-Instance HostSave handle.
51 HostSave,
52}
53
54/// All kernel Images, for registry iteration.
55pub const ALL_KERNEL_IMAGES: [KernelImage; 9] = [
56 KernelImage::GasMeter,
57 KernelImage::StorageQuota,
58 KernelImage::Gas,
59 KernelImage::Quota,
60 KernelImage::YieldSender,
61 KernelImage::YieldReceiver,
62 KernelImage::File,
63 KernelImage::HostOpen,
64 KernelImage::HostSave,
65];
66
67const fn const_kernel_image_label(kind: KernelImage) -> &'static [u8] {
68 match kind {
69 KernelImage::GasMeter => b"kernel:gasmeter",
70 KernelImage::StorageQuota => b"kernel:storagequota",
71 KernelImage::Gas => b"kernel:gas",
72 KernelImage::Quota => b"kernel:quota",
73 KernelImage::YieldSender => b"kernel:yieldsender",
74 KernelImage::YieldReceiver => b"kernel:yieldreceiver",
75 KernelImage::File => b"kernel:file",
76 KernelImage::HostOpen => b"kernel:host_open",
77 KernelImage::HostSave => b"kernel:host_save",
78 }
79}
80
81/// The well-known `image_hash` for a kernel-assisted Image.
82pub fn kernel_image_hash(kind: KernelImage) -> CapHash {
83 Blake2b256::hash(const_kernel_image_label(kind))
84}
85
86/// Look up a kernel-assisted Image by its `image_hash_chain`. `None` for a
87/// user Image (the common case). Linear scan over ~15 entries — only called at
88/// Instance entry / yield routing, not on the hot path.
89pub fn recognize_kernel_image(hash: CapHash) -> Option<KernelImage> {
90 ALL_KERNEL_IMAGES
91 .into_iter()
92 .find(|kind| kernel_image_hash(*kind) == hash)
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn hashes_are_deterministic_and_distinct() {
101 for (i, a) in ALL_KERNEL_IMAGES.iter().enumerate() {
102 assert_eq!(kernel_image_hash(*a), kernel_image_hash(*a));
103 for b in &ALL_KERNEL_IMAGES[i + 1..] {
104 assert_ne!(
105 kernel_image_hash(*a),
106 kernel_image_hash(*b),
107 "{a:?} vs {b:?}"
108 );
109 }
110 }
111 }
112
113 #[test]
114 fn recognize_round_trips_and_rejects_unknown() {
115 assert_eq!(
116 recognize_kernel_image(kernel_image_hash(KernelImage::Gas)),
117 Some(KernelImage::Gas)
118 );
119 assert_eq!(
120 recognize_kernel_image(Blake2b256::hash(b"user:image")),
121 None
122 );
123 }
124}