1use crate::abi::{CapabilityMask, Principal, Tick};
12use crate::state::{Authorized, Effect, EntityMeta, Op, ScheduledActionId, ScheduledEntry};
13
14use super::event::KernelEvent;
15use super::stage::{LedgerOp, PendingSignal, ScheduledEntryDelta, StagedStateDelta, StepStage};
16
17pub(crate) fn dispatch<'i>(
24 effect: Effect<'i, Authorized>,
25 stage: &mut StepStage,
26 now: Tick,
27 next_scheduled_id: &mut u64,
28 parent_caps: CapabilityMask,
29) {
30 let instance_id = effect.instance_id;
31 let effect_principal = match &effect.principal {
32 Principal::Unauthenticated => Principal::Unauthenticated,
33 Principal::External(e) => Principal::External(*e),
34 Principal::System => Principal::System,
35 };
36 match effect.op {
37 Op::SpawnEntity { id, owner } => {
38 stage.state_ops.push(StagedStateDelta::SpawnEntity {
39 id,
40 meta: EntityMeta {
41 owner,
42 created: now,
43 },
44 });
45 stage.ledger_delta.ops.push(LedgerOp::AddEntity(id));
46 stage.id_counters.next_entity_advance =
47 stage.id_counters.next_entity_advance.saturating_add(1);
48 }
49 Op::DespawnEntity { id } => {
50 stage.state_ops.push(StagedStateDelta::DespawnEntity { id });
51 stage.ledger_delta.ops.push(LedgerOp::RemoveEntity(id));
52 }
53 Op::SetComponent {
54 entity,
55 type_code,
56 bytes,
57 size,
58 } => {
59 stage.state_ops.push(StagedStateDelta::SetComponent {
60 entity,
61 type_code,
62 bytes,
63 size,
64 });
65 stage.ledger_delta.ops.push(LedgerOp::AddComponent {
66 entity,
67 type_code,
68 size,
69 });
70 }
71 Op::RemoveComponent {
72 entity,
73 type_code,
74 size,
75 } => {
76 stage.state_ops.push(StagedStateDelta::RemoveComponent {
77 entity,
78 type_code,
79 size,
80 });
81 stage.ledger_delta.ops.push(LedgerOp::RemoveComponent {
82 entity,
83 type_code,
84 size,
85 });
86 }
87 Op::EmitEvent {
88 actor,
89 event_type_code,
90 event_bytes,
91 } => {
92 stage.events.push_back(KernelEvent::DomainEventEmitted {
93 instance: instance_id,
94 actor,
95 event_type_code,
96 bytes: event_bytes,
97 });
98 }
99 Op::ScheduleAction {
100 at,
101 actor,
102 action_type_code,
103 action_bytes,
104 } => {
105 *next_scheduled_id = next_scheduled_id.saturating_add(1);
106 let id = ScheduledActionId::new(*next_scheduled_id)
107 .expect("next_scheduled_id incremented before use; non-zero");
108 stage
109 .schedule_deltas
110 .push(ScheduledEntryDelta::Add(ScheduledEntry {
111 id,
112 at,
113 actor,
114 principal: effect_principal,
119 action_type_code,
120 action_bytes: action_bytes.to_vec(),
121 caps_ceiling: parent_caps,
122 }));
123 stage.id_counters.next_scheduled_advance =
124 stage.id_counters.next_scheduled_advance.saturating_add(1);
125 }
126 Op::SendSignal {
127 target,
128 route,
129 payload,
130 } => {
131 stage.pending_signals.push(PendingSignal {
137 target,
138 route,
139 payload,
140 principal: effect_principal,
141 });
142 }
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use bytes::Bytes;
150
151 use crate::abi::{CapabilityMask, EntityId, InstanceId, Principal, RouteId, Tick, TypeCode};
152 use crate::state::authz::authorize;
153 use crate::state::{Effect, Op, Unverified};
154
155 fn inst() -> InstanceId {
156 InstanceId::new(1).unwrap()
157 }
158 fn ent(n: u64) -> EntityId {
159 EntityId::new(n).unwrap()
160 }
161 fn caps() -> CapabilityMask {
162 CapabilityMask::all()
163 }
164
165 fn auth_system(op: Op) -> Effect<'static, crate::state::Authorized> {
166 let e: Effect<'static, Unverified> = Effect::new(inst(), Principal::System, op);
167 authorize(CapabilityMask::SYSTEM, e).expect("system always authorized")
168 }
169
170 #[test]
171 fn dispatch_spawn_entity_pushes_state_op_ledger_and_advances_counter() {
172 let mut stage = StepStage::default();
173 let mut next_id: u64 = 0;
174 let e = auth_system(Op::SpawnEntity {
175 id: ent(1),
176 owner: Principal::System,
177 });
178 dispatch(e, &mut stage, Tick(5), &mut next_id, caps());
179 assert_eq!(stage.state_ops.len(), 1);
180 assert_eq!(stage.ledger_delta.ops.len(), 1);
181 assert_eq!(stage.id_counters.next_entity_advance, 1);
182 assert_eq!(next_id, 0); }
184
185 #[test]
186 fn dispatch_despawn_entity_pushes_state_and_ledger_remove() {
187 let mut stage = StepStage::default();
188 let mut next_id: u64 = 0;
189 let e = auth_system(Op::DespawnEntity { id: ent(1) });
190 dispatch(e, &mut stage, Tick(0), &mut next_id, caps());
191 assert_eq!(stage.state_ops.len(), 1);
192 assert_eq!(stage.ledger_delta.ops.len(), 1);
193 }
194
195 #[test]
196 fn dispatch_set_component_pushes_state_and_ledger_add() {
197 let mut stage = StepStage::default();
198 let mut next_id: u64 = 0;
199 let e = auth_system(Op::SetComponent {
200 entity: ent(1),
201 type_code: TypeCode(7),
202 bytes: Bytes::from_static(b"data"),
203 size: 4,
204 });
205 dispatch(e, &mut stage, Tick(0), &mut next_id, caps());
206 assert_eq!(stage.state_ops.len(), 1);
207 assert_eq!(stage.ledger_delta.ops.len(), 1);
208 }
209
210 #[test]
211 fn dispatch_remove_component_pushes_state_and_ledger_remove() {
212 let mut stage = StepStage::default();
213 let mut next_id: u64 = 0;
214 let e = auth_system(Op::RemoveComponent {
215 entity: ent(1),
216 type_code: TypeCode(7),
217 size: 4,
218 });
219 dispatch(e, &mut stage, Tick(0), &mut next_id, caps());
220 assert_eq!(stage.state_ops.len(), 1);
221 assert_eq!(stage.ledger_delta.ops.len(), 1);
222 }
223
224 #[test]
225 fn dispatch_emit_event_pushes_kernel_event() {
226 let mut stage = StepStage::default();
227 let mut next_id: u64 = 0;
228 let e = auth_system(Op::EmitEvent {
229 actor: Some(ent(1)),
230 event_type_code: TypeCode(2),
231 event_bytes: Bytes::from_static(b"evt"),
232 });
233 dispatch(e, &mut stage, Tick(0), &mut next_id, caps());
234 assert_eq!(stage.events.len(), 1);
235 match stage.events.front().unwrap() {
236 KernelEvent::DomainEventEmitted {
237 event_type_code, ..
238 } => {
239 assert_eq!(*event_type_code, TypeCode(2));
240 }
241 _ => panic!("expected DomainEventEmitted"),
242 }
243 }
244
245 #[test]
246 fn dispatch_schedule_action_advances_id_and_pushes_delta() {
247 let mut stage = StepStage::default();
248 let mut next_id: u64 = 0;
249 let e = auth_system(Op::ScheduleAction {
250 at: Tick(10),
251 actor: None,
252 action_type_code: TypeCode(3),
253 action_bytes: Bytes::from_static(b"a"),
254 });
255 dispatch(e, &mut stage, Tick(0), &mut next_id, caps());
256 assert_eq!(stage.schedule_deltas.len(), 1);
257 assert_eq!(next_id, 1);
258 assert_eq!(stage.id_counters.next_scheduled_advance, 1);
259 match &stage.schedule_deltas[0] {
262 ScheduledEntryDelta::Add(entry) => {
263 assert!(matches!(entry.principal, Principal::System));
264 assert_eq!(entry.caps_ceiling, caps());
265 }
266 ScheduledEntryDelta::Remove(_) => panic!("expected Add"),
267 }
268 }
269
270 #[test]
271 fn dispatch_send_signal_stages_pending_without_speculative_refcount() {
272 let mut stage = StepStage::default();
278 let mut next_id: u64 = 0;
279 let route = RouteId(42);
280 let e = auth_system(Op::SendSignal {
281 target: inst(),
282 route,
283 payload: Bytes::from_static(b"hello"),
284 });
285 dispatch(e, &mut stage, Tick(0), &mut next_id, caps());
286 assert_eq!(stage.pending_signals.len(), 1);
287 }
288
289 #[test]
290 fn dispatch_send_signal_twice_stages_two_pending() {
291 let mut stage = StepStage::default();
292 let mut next_id: u64 = 0;
293 let route = RouteId(42);
294 for _ in 0..3 {
295 let e = auth_system(Op::SendSignal {
296 target: inst(),
297 route,
298 payload: Bytes::new(),
299 });
300 dispatch(e, &mut stage, Tick(0), &mut next_id, caps());
301 }
302 assert_eq!(stage.pending_signals.len(), 3);
303 }
304}