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