1use std::sync::Arc;
15
16use javm::{InProcessKernelAssist, Vm};
17use javm_cap::{Cap, CapHash, CapHashOrRef, SlotIdx};
18use javm_exec::ExitReason;
19
20use crate::error::KernelError;
21use crate::state::State;
22
23pub struct Event {
25 pub endpoint_idx: u8,
27 pub payload: Vec<u8>,
30}
31
32pub struct Block {
35 pub events: Vec<Event>,
36}
37
38#[derive(Debug)]
40pub enum EventOutcome {
41 Halt { return_value: u64, gas_used: u64 },
42 Faulted { reason: String, gas_used: u64 },
43 Paused { gas_used: u64 },
44}
45
46pub fn apply_event(
54 state: &mut State,
55 vm: &mut Vm<InProcessKernelAssist>,
56 chain_instance_hash: &mut CapHash,
57 event: &Event,
58 gas_budget: u64,
59 _storage_quota: u64,
60) -> Result<EventOutcome, KernelError> {
61 let payload_hash = state.caps.put_cap(&Cap::data_inline(&event.payload))?;
63
64 let (image_hash_chain, image_hash, root_cnode_hash, mem_size, overlay_bufs, regs, pc) = {
69 let inst_cap = state
70 .caps
71 .get(CapHashOrRef::Hash(*chain_instance_hash))
72 .ok_or(KernelError::Invariant(
73 "apply_event: chain instance missing in cache",
74 ))?;
75 match &*inst_cap {
76 Cap::Instance(inst) => {
77 let cnode_hash = match &inst.root_cnode {
78 CapHashOrRef::Hash(h) => *h,
79 CapHashOrRef::Ref(_) => {
80 return Err(KernelError::Invariant(
81 "apply_event: chain instance root_cnode unsettled",
82 ));
83 }
84 };
85 let overlays: Vec<(u32, Vec<u8>)> = inst
86 .rw_overlays
87 .iter()
88 .map(|o| (o.start, o.bytes.to_vec()))
89 .collect();
90 (
91 inst.image_hash_chain,
92 inst.image_hash,
93 cnode_hash,
94 inst.mem_size,
95 overlays,
96 inst.regs,
97 inst.pc,
98 )
99 }
100 _ => {
101 return Err(KernelError::Invariant(
102 "apply_event: chain instance hash does not resolve to Cap::Instance",
103 ));
104 }
105 }
106 };
107
108 let working_cnode_ref = state
112 .caps
113 .promote_blob_to_instance(&root_cnode_hash)
114 .ok_or(KernelError::Invariant(
115 "apply_event: chain root cnode not in blobs",
116 ))?;
117 let mut cnode_arc =
118 state
119 .caps
120 .get_instance(&working_cnode_ref)
121 .ok_or(KernelError::Invariant(
122 "apply_event: promoted cnode missing in instances tier",
123 ))?;
124 {
125 let cnode_mut = match Arc::make_mut(&mut cnode_arc) {
126 Cap::CNode(cn) => cn,
127 _ => {
128 return Err(KernelError::Invariant(
129 "apply_event: chain root cnode is not Cap::CNode",
130 ));
131 }
132 };
133 cnode_mut.set(SlotIdx(0), Some(CapHashOrRef::Hash(payload_hash)))?;
134 }
135 state.caps.set_instance(&working_cnode_ref, cnode_arc)?;
136
137 let new_root_cnode_hash = state.caps.settle(CapHashOrRef::Ref(working_cnode_ref))?;
140
141 let overlay_slices: Vec<(u32, &[u8])> = overlay_bufs
144 .iter()
145 .map(|(s, b)| (*s, b.as_slice()))
146 .collect();
147 let new_chain_instance_hash = state.caps.put_cap(&Cap::instance_with_overlays(
148 image_hash_chain,
149 image_hash,
150 new_root_cnode_hash,
151 &overlay_slices,
152 mem_size,
153 regs,
154 pc,
155 0,
156 ))?;
157
158 let result = vm.invoke_cached(
160 &mut state.caps,
161 new_chain_instance_hash,
162 event.endpoint_idx,
163 [0u64; 4],
164 gas_budget,
165 )?;
166
167 Ok(match result {
169 javm::CallResult::Halt {
170 return_value,
171 post_instance_hash,
172 gas_used,
173 ..
174 } => {
175 *chain_instance_hash = post_instance_hash;
176 EventOutcome::Halt {
177 return_value,
178 gas_used,
179 }
180 }
181 javm::CallResult::Faulted {
182 reason, gas_used, ..
183 } => {
184 *chain_instance_hash = new_chain_instance_hash;
188 EventOutcome::Faulted {
189 reason: format_fault(reason),
190 gas_used,
191 }
192 }
193 javm::CallResult::Paused { gas_used, .. } => {
194 *chain_instance_hash = new_chain_instance_hash;
195 EventOutcome::Paused { gas_used }
196 }
197 })
198}
199
200fn format_fault(reason: ExitReason) -> String {
201 match reason {
202 ExitReason::Trap => "trap".into(),
203 ExitReason::Panic => "panic".into(),
204 ExitReason::OutOfGas => "out-of-gas".into(),
205 ExitReason::PageFault(addr) => format!("page-fault@{addr:#x}"),
206 ExitReason::HostCall(idx) => format!("host-call:{idx}"),
207 ExitReason::Ecall => "ecall".into(),
208 ExitReason::Halt => "halt".into(),
209 }
210}
211
212pub fn apply_block(
214 state: &mut State,
215 vm: &mut Vm<InProcessKernelAssist>,
216 chain_instance_hash: &mut CapHash,
217 block: &Block,
218 gas_per_event: u64,
219 quota_per_event: u64,
220) -> Result<Vec<EventOutcome>, KernelError> {
221 let mut outcomes = Vec::with_capacity(block.events.len());
222 for event in &block.events {
223 outcomes.push(apply_event(
224 state,
225 vm,
226 chain_instance_hash,
227 event,
228 gas_per_event,
229 quota_per_event,
230 )?);
231 }
232 Ok(outcomes)
233}