1use serde_json::json;
4
5use crate::coroutine::Fault;
6use crate::effect::EffectTraceEntry;
7use crate::output_condition::{
8 verify_output_condition, OutputConditionCheck, OutputConditionHint, OutputConditionMeta,
9 OutputConditionPolicy,
10};
11use crate::vm::ObsEvent;
12
13pub(crate) fn apply_output_condition_gate(
15 policy: &OutputConditionPolicy,
16 checks: &mut Vec<OutputConditionCheck>,
17 trace: &mut Vec<ObsEvent>,
18 tick: u64,
19 output_hint: Option<OutputConditionHint>,
20) -> Result<(), Fault> {
21 let digest = "vm.output_digest.unspecified".to_string();
22 let meta = match output_hint {
23 Some(h) => OutputConditionMeta::from_hint(h, digest),
24 None => OutputConditionMeta::default_observable(digest),
25 };
26 let passed = verify_output_condition(policy, &meta);
27 checks.push(OutputConditionCheck {
28 meta: meta.clone(),
29 passed,
30 });
31 trace.push(ObsEvent::OutputConditionChecked {
32 tick,
33 predicate_ref: meta.predicate_ref.clone(),
34 witness_ref: meta.witness_ref.clone(),
35 output_digest: meta.output_digest.clone(),
36 passed,
37 });
38 if passed {
39 Ok(())
40 } else {
41 Err(Fault::OutputCondition {
42 predicate_ref: meta.predicate_ref,
43 })
44 }
45}
46
47pub(crate) fn effect_trace_entry_for_event(
49 ev: &ObsEvent,
50 effect_id: u64,
51 handler_identity: &str,
52 ordering_key: u64,
53) -> Option<EffectTraceEntry> {
54 match ev {
55 ObsEvent::Sent {
56 session,
57 from,
58 to,
59 label,
60 ..
61 } => Some(EffectTraceEntry {
62 effect_id,
63 effect_kind: "send_decision".to_string(),
64 inputs: json!({
65 "session": session,
66 "from": from,
67 "to": to,
68 "label": label,
69 }),
70 outputs: json!({"committed": true}),
71 handler_identity: handler_identity.to_string(),
72 ordering_key,
73 topology: None,
74 }),
75 ObsEvent::Received {
76 session,
77 from,
78 to,
79 label,
80 ..
81 } => Some(EffectTraceEntry {
82 effect_id,
83 effect_kind: "handle_recv".to_string(),
84 inputs: json!({
85 "session": session,
86 "from": from,
87 "to": to,
88 "label": label,
89 }),
90 outputs: json!({"committed": true}),
91 handler_identity: handler_identity.to_string(),
92 ordering_key,
93 topology: None,
94 }),
95 ObsEvent::Invoked { coro_id, role, .. } => Some(EffectTraceEntry {
96 effect_id,
97 effect_kind: "invoke_step".to_string(),
98 inputs: json!({
99 "coro_id": coro_id,
100 "role": role,
101 }),
102 outputs: json!({"ok": true}),
103 handler_identity: handler_identity.to_string(),
104 ordering_key,
105 topology: None,
106 }),
107 ObsEvent::Acquired {
108 session,
109 role,
110 layer,
111 ..
112 } => Some(EffectTraceEntry {
113 effect_id,
114 effect_kind: "handle_acquire".to_string(),
115 inputs: json!({
116 "session": session,
117 "role": role,
118 "layer": layer,
119 }),
120 outputs: json!({"granted": true}),
121 handler_identity: handler_identity.to_string(),
122 ordering_key,
123 topology: None,
124 }),
125 ObsEvent::Released {
126 session,
127 role,
128 layer,
129 ..
130 } => Some(EffectTraceEntry {
131 effect_id,
132 effect_kind: "handle_release".to_string(),
133 inputs: json!({
134 "session": session,
135 "role": role,
136 "layer": layer,
137 }),
138 outputs: json!({"ok": true}),
139 handler_identity: handler_identity.to_string(),
140 ordering_key,
141 topology: None,
142 }),
143 _ => None,
144 }
145}