1use std::collections::BTreeMap;
8
9use serde::{Deserialize, Serialize};
10use serde_json::Value as JsonValue;
11use telltale_types::{LocalTypeR, ValType};
12
13use crate::buffer::{BoundedBuffer, BufferConfig, SignedBuffer, SignedValue};
14use crate::coroutine::Value;
15use crate::instr::Endpoint;
16use crate::verification::{
17 signValue, signing_key_for_endpoint, verifySignedValue, verifying_key_for_endpoint, AuthTree,
18 DefaultVerificationModel, Hash, HashTag, Signature, VerificationModel,
19};
20
21pub type SessionId = usize;
23
24pub type HandlerId = String;
26
27pub const DEFAULT_HANDLER_ID: &str = "default_handler";
29
30fn default_handler_id() -> HandlerId {
31 DEFAULT_HANDLER_ID.to_string()
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
36pub struct Edge {
37 pub sid: SessionId,
39 pub sender: String,
41 pub receiver: String,
43}
44
45impl Edge {
46 #[must_use]
48 pub fn new(sid: SessionId, sender: impl Into<String>, receiver: impl Into<String>) -> Self {
49 Self {
50 sid,
51 sender: sender.into(),
52 receiver: receiver.into(),
53 }
54 }
55}
56
57#[derive(Debug, Deserialize)]
58struct EdgeJson {
59 sid: Option<SessionId>,
60 sender: String,
61 receiver: String,
62}
63
64pub fn decode_edge_json(
70 value: &JsonValue,
71 session_hint: Option<SessionId>,
72) -> Result<Edge, String> {
73 let raw: EdgeJson =
74 serde_json::from_value(value.clone()).map_err(|e| format!("invalid edge json: {e}"))?;
75
76 let sid = raw
77 .sid
78 .or(session_hint)
79 .ok_or_else(|| "missing sid in edge json".to_string())?;
80 Ok(Edge::new(sid, raw.sender, raw.receiver))
81}
82
83#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
85pub enum SessionStatus {
86 Active,
88 Draining,
90 Closed,
92 Cancelled,
94 Faulted {
96 reason: String,
98 },
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct TypeEntry {
104 pub current: LocalTypeR,
106 pub original: LocalTypeR,
108}
109
110#[derive(Debug, Serialize, Deserialize)]
115pub struct SessionState {
116 pub sid: SessionId,
118 pub roles: Vec<String>,
120 pub local_types: BTreeMap<Endpoint, TypeEntry>,
124 pub buffers: BTreeMap<Edge, SignedBuffer<Signature>>,
126 pub auth_leaves: BTreeMap<Edge, Vec<Hash>>,
128 #[serde(default)]
130 pub auth_trees: BTreeMap<Edge, AuthTree>,
131 pub auth_roots: BTreeMap<Edge, Hash>,
133 pub edge_handlers: BTreeMap<Edge, HandlerId>,
135 #[serde(default = "default_handler_id")]
137 pub default_handler: HandlerId,
138 pub edge_traces: BTreeMap<Edge, Vec<ValType>>,
140 pub status: SessionStatus,
142 pub epoch: usize,
144}
145
146impl SessionState {
147 fn update_auth_tree(&mut self, edge: &Edge, signed: &SignedValue<Signature>) {
148 let bytes = serde_json::to_vec(signed).unwrap_or_default();
149 let leaf = DefaultVerificationModel::hash(HashTag::MerkleLeaf, &bytes);
150 self.auth_leaves.entry(edge.clone()).or_default().push(leaf);
151 let tree = self
152 .auth_trees
153 .entry(edge.clone())
154 .or_insert_with(|| AuthTree::new(Vec::new()));
155 tree.append_leaf(leaf);
156 self.auth_roots.insert(edge.clone(), tree.root());
157 }
158
159 pub fn send_signed(
165 &mut self,
166 from: &str,
167 to: &str,
168 signed: &SignedValue<Signature>,
169 ) -> Result<crate::buffer::EnqueueResult, String> {
170 let edge = Edge::new(self.sid, from, to);
171 let buf = self
172 .buffers
173 .get_mut(&edge)
174 .ok_or_else(|| format!("no buffer for edge {from} → {to}"))?;
175 let result = buf.enqueue(signed.clone());
176 if matches!(result, crate::buffer::EnqueueResult::Ok) {
177 self.update_auth_tree(&edge, signed);
178 }
179 Ok(result)
180 }
181
182 pub fn send(
190 &mut self,
191 from: &str,
192 to: &str,
193 val: Value,
194 ) -> Result<crate::buffer::EnqueueResult, String> {
195 let signer = signing_key_for_endpoint(&Endpoint {
196 sid: self.sid,
197 role: from.to_string(),
198 });
199 let signature = signValue(&val, &signer);
200 self.send_signed(
201 from,
202 to,
203 &SignedValue {
204 payload: val,
205 signature,
206 },
207 )
208 }
209
210 pub fn recv_signed(&mut self, from: &str, to: &str) -> Option<SignedValue<Signature>> {
212 let edge = Edge::new(self.sid, from, to);
213 self.buffers.get_mut(&edge).and_then(|buf| buf.dequeue())
214 }
215
216 pub fn recv_verified(&mut self, from: &str, to: &str) -> Result<Option<Value>, String> {
222 let sender = Endpoint {
223 sid: self.sid,
224 role: from.to_string(),
225 };
226 let verifying = verifying_key_for_endpoint(&sender);
227 let signed = self.recv_signed(from, to);
228 let Some(signed) = signed else {
229 return Ok(None);
230 };
231 if !verifySignedValue(&signed.payload, &signed.signature, &verifying) {
232 return Err(format!(
233 "signature verification failed on edge {from} -> {to}"
234 ));
235 }
236 Ok(Some(signed.payload))
237 }
238
239 pub fn recv(&mut self, from: &str, to: &str) -> Option<Value> {
241 self.recv_verified(from, to).ok().flatten()
242 }
243
244 #[must_use]
246 pub fn has_message(&self, from: &str, to: &str) -> bool {
247 let edge = Edge::new(self.sid, from, to);
248 self.buffers.get(&edge).is_some_and(|buf| !buf.is_empty())
249 }
250}
251
252#[derive(Debug, Default, Serialize, Deserialize)]
257pub struct SessionStore {
258 sessions: BTreeMap<SessionId, SessionState>,
259 next_id: SessionId,
260}
261
262impl SessionStore {
263 #[must_use]
265 pub fn new() -> Self {
266 Self::default()
267 }
268
269 pub fn open_with_sid(
273 &mut self,
274 sid: SessionId,
275 roles: Vec<String>,
276 buffer_config: &BufferConfig,
277 initial_types: &BTreeMap<String, LocalTypeR>,
278 ) -> SessionId {
279 let mut local_types = BTreeMap::new();
281 for role in &roles {
282 if let Some(lt) = initial_types.get(role) {
283 let ep = Endpoint {
284 sid,
285 role: role.clone(),
286 };
287 local_types.insert(
288 ep,
289 TypeEntry {
290 current: unfold_mu(lt),
291 original: lt.clone(),
292 },
293 );
294 }
295 }
296
297 let mut buffers = BTreeMap::new();
299 for from in &roles {
300 for to in &roles {
301 if from != to {
302 let edge = Edge::new(sid, from.clone(), to.clone());
303 buffers.insert(edge, BoundedBuffer::new(buffer_config));
304 }
305 }
306 }
307
308 let state = SessionState {
309 sid,
310 roles,
311 local_types,
312 buffers,
313 auth_leaves: BTreeMap::new(),
314 auth_trees: BTreeMap::new(),
315 auth_roots: BTreeMap::new(),
316 edge_handlers: BTreeMap::new(),
317 default_handler: default_handler_id(),
318 edge_traces: BTreeMap::new(),
319 status: SessionStatus::Active,
320 epoch: 0,
321 };
322
323 self.sessions.insert(sid, state);
324 self.next_id = self.next_id.max(sid.saturating_add(1));
325 sid
326 }
327
328 pub fn open(
332 &mut self,
333 roles: Vec<String>,
334 buffer_config: &BufferConfig,
335 initial_types: &BTreeMap<String, LocalTypeR>,
336 ) -> SessionId {
337 let sid = self.next_id;
338 self.open_with_sid(sid, roles, buffer_config, initial_types)
339 }
340
341 #[must_use]
343 pub fn next_session_id(&self) -> SessionId {
344 self.next_id
345 }
346
347 #[must_use]
353 pub fn lookup_type(&self, ep: &Endpoint) -> Option<&LocalTypeR> {
354 self.sessions
355 .get(&ep.sid)?
356 .local_types
357 .get(ep)
358 .map(|e| &e.current)
359 }
360
361 pub fn update_type(&mut self, ep: &Endpoint, new_type: LocalTypeR) {
365 if let Some(session) = self.sessions.get_mut(&ep.sid) {
366 if let Some(entry) = session.local_types.get_mut(ep) {
367 entry.current = new_type;
368 }
369 }
370 }
371
372 pub fn update_original(&mut self, ep: &Endpoint, new_original: LocalTypeR) {
374 if let Some(session) = self.sessions.get_mut(&ep.sid) {
375 if let Some(entry) = session.local_types.get_mut(ep) {
376 entry.original = new_original;
377 }
378 }
379 }
380
381 #[must_use]
383 pub fn original_type(&self, ep: &Endpoint) -> Option<&LocalTypeR> {
384 self.sessions
385 .get(&ep.sid)?
386 .local_types
387 .get(ep)
388 .map(|e| &e.original)
389 }
390
391 pub fn remove_type(&mut self, ep: &Endpoint) {
393 if let Some(session) = self.sessions.get_mut(&ep.sid) {
394 session.local_types.remove(ep);
395 }
396 }
397
398 #[must_use]
402 pub fn get(&self, sid: SessionId) -> Option<&SessionState> {
403 self.sessions.get(&sid)
404 }
405
406 pub fn get_mut(&mut self, sid: SessionId) -> Option<&mut SessionState> {
408 self.sessions.get_mut(&sid)
409 }
410
411 pub fn iter(&self) -> impl Iterator<Item = &SessionState> {
413 self.sessions.values()
414 }
415
416 pub fn close(&mut self, sid: SessionId) -> Result<(), String> {
422 let session = self
423 .sessions
424 .get_mut(&sid)
425 .ok_or_else(|| format!("session {sid} not found"))?;
426
427 session.status = SessionStatus::Closed;
428 session.buffers.clear();
429 session.edge_traces.clear();
430 session.epoch = session.epoch.saturating_add(1);
431 Ok(())
432 }
433
434 #[must_use]
436 pub fn active_count(&self) -> usize {
437 self.sessions
438 .values()
439 .filter(|s| s.status == SessionStatus::Active)
440 .count()
441 }
442
443 #[must_use]
445 pub fn session_ids(&self) -> Vec<SessionId> {
446 self.sessions.keys().copied().collect()
447 }
448
449 #[must_use]
451 pub fn lookup_handler(&self, edge: &Edge) -> Option<&HandlerId> {
452 self.sessions.get(&edge.sid)?.edge_handlers.get(edge)
453 }
454
455 #[must_use]
457 pub fn default_handler_for_session(&self, sid: SessionId) -> Option<&HandlerId> {
458 Some(&self.sessions.get(&sid)?.default_handler)
459 }
460
461 pub fn set_default_handler_for_session(&mut self, sid: SessionId, handler: HandlerId) {
463 if let Some(session) = self.sessions.get_mut(&sid) {
464 session.default_handler = handler;
465 }
466 }
467
468 pub fn update_handler(&mut self, edge: &Edge, handler: HandlerId) {
470 if let Some(session) = self.sessions.get_mut(&edge.sid) {
471 session.edge_handlers.insert(edge.clone(), handler);
472 }
473 }
474
475 #[must_use]
477 pub fn lookup_trace(&self, edge: &Edge) -> Option<&[ValType]> {
478 self.sessions
479 .get(&edge.sid)?
480 .edge_traces
481 .get(edge)
482 .map(Vec::as_slice)
483 }
484
485 pub fn update_trace(&mut self, edge: &Edge, trace: Vec<ValType>) {
487 if let Some(session) = self.sessions.get_mut(&edge.sid) {
488 session.edge_traces.insert(edge.clone(), trace);
489 }
490 }
491}
492
493#[must_use]
499pub fn unfold_mu(lt: &LocalTypeR) -> LocalTypeR {
501 match lt {
502 LocalTypeR::Mu { body, .. } => unfold_mu(body),
503 other => other.clone(),
504 }
505}
506
507#[must_use]
512pub fn unfold_if_var(cont: &LocalTypeR, original: &LocalTypeR) -> LocalTypeR {
513 match cont {
514 LocalTypeR::Var(_) => unfold_mu(original),
515 LocalTypeR::Mu { .. } => unfold_mu(cont),
516 other => other.clone(),
517 }
518}
519
520#[must_use]
526pub fn unfold_if_var_with_scope(
527 cont: &LocalTypeR,
528 original: &LocalTypeR,
529) -> (LocalTypeR, Option<LocalTypeR>) {
530 match cont {
531 LocalTypeR::Var(_) => (unfold_mu(original), None),
532 LocalTypeR::Mu { .. } => (unfold_mu(cont), Some(cont.clone())),
533 other => (other.clone(), None),
534 }
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540 use serde_json::json;
541 use telltale_types::Label;
542
543 fn default_types() -> BTreeMap<String, LocalTypeR> {
544 let mut m = BTreeMap::new();
545 m.insert(
546 "A".to_string(),
547 LocalTypeR::mu(
548 "step",
549 LocalTypeR::Send {
550 partner: "B".into(),
551 branches: vec![(Label::new("msg"), None, LocalTypeR::var("step"))],
552 },
553 ),
554 );
555 m.insert(
556 "B".to_string(),
557 LocalTypeR::mu(
558 "step",
559 LocalTypeR::Recv {
560 partner: "A".into(),
561 branches: vec![(Label::new("msg"), None, LocalTypeR::var("step"))],
562 },
563 ),
564 );
565 m
566 }
567
568 #[test]
569 fn test_session_open_with_types() {
570 let mut store = SessionStore::new();
571 let types = default_types();
572 let sid = store.open(
573 vec!["A".into(), "B".into()],
574 &BufferConfig::default(),
575 &types,
576 );
577
578 let ep_a = Endpoint {
579 sid,
580 role: "A".into(),
581 };
582 let ep_b = Endpoint {
583 sid,
584 role: "B".into(),
585 };
586
587 assert!(matches!(
589 store.lookup_type(&ep_a),
590 Some(LocalTypeR::Send { .. })
591 ));
592 assert!(matches!(
593 store.lookup_type(&ep_b),
594 Some(LocalTypeR::Recv { .. })
595 ));
596 }
597
598 #[test]
599 fn test_type_advance_and_unfold() {
600 let mut store = SessionStore::new();
601 let types = default_types();
602 let sid = store.open(
603 vec!["A".into(), "B".into()],
604 &BufferConfig::default(),
605 &types,
606 );
607
608 let ep_a = Endpoint {
609 sid,
610 role: "A".into(),
611 };
612
613 let lt = store.lookup_type(&ep_a).unwrap().clone();
615 let (_, _vt, continuation) = match < {
616 LocalTypeR::Send { branches, .. } => branches.first().unwrap().clone(),
617 _ => panic!("expected Send"),
618 };
619
620 let original = store.original_type(&ep_a).unwrap();
622 let resolved = unfold_if_var(&continuation, original);
623 assert!(matches!(resolved, LocalTypeR::Send { .. }));
624
625 store.update_type(&ep_a, resolved);
627 assert!(matches!(
628 store.lookup_type(&ep_a),
629 Some(LocalTypeR::Send { .. })
630 ));
631 }
632
633 #[test]
634 fn test_session_send_recv() {
635 let mut store = SessionStore::new();
636 let sid = store.open(
637 vec!["A".into(), "B".into()],
638 &BufferConfig::default(),
639 &BTreeMap::new(),
640 );
641
642 let session = store.get_mut(sid).unwrap();
643 session.send("A", "B", Value::Nat(42)).unwrap();
644 assert!(session.has_message("A", "B"));
645 assert!(!session.has_message("B", "A"));
646
647 let val = session.recv("A", "B");
648 assert_eq!(val, Some(Value::Nat(42)));
649 }
650
651 #[test]
652 fn test_close_clears_buffers_and_traces_even_when_messages_pending() {
653 let mut store = SessionStore::new();
654 let sid = store.open(
655 vec!["A".into(), "B".into()],
656 &BufferConfig::default(),
657 &BTreeMap::new(),
658 );
659 let edge = Edge::new(sid, "A", "B");
660 store
661 .get_mut(sid)
662 .expect("session exists")
663 .send("A", "B", Value::Nat(7))
664 .expect("enqueue pending message");
665 store.update_trace(&edge, vec![ValType::Nat]);
666
667 store.close(sid).expect("close session");
668 let session = store.get(sid).expect("session exists after close");
669 assert_eq!(session.status, SessionStatus::Closed);
670 assert!(session.buffers.is_empty());
671 assert!(session.edge_traces.is_empty());
672 }
673
674 #[test]
675 fn test_namespace_isolation() {
676 let mut store = SessionStore::new();
677 let sid1 = store.open(
678 vec!["A".into(), "B".into()],
679 &BufferConfig::default(),
680 &BTreeMap::new(),
681 );
682 let sid2 = store.open(
683 vec!["A".into(), "B".into()],
684 &BufferConfig::default(),
685 &BTreeMap::new(),
686 );
687
688 assert_ne!(sid1, sid2);
689
690 store
691 .get_mut(sid1)
692 .unwrap()
693 .send("A", "B", Value::Nat(1))
694 .unwrap();
695 assert!(!store.get(sid2).unwrap().has_message("A", "B"));
696 }
697
698 #[test]
699 fn test_remove_type() {
700 let mut store = SessionStore::new();
701 let types = default_types();
702 let sid = store.open(
703 vec!["A".into(), "B".into()],
704 &BufferConfig::default(),
705 &types,
706 );
707
708 let ep_a = Endpoint {
709 sid,
710 role: "A".into(),
711 };
712 assert!(store.lookup_type(&ep_a).is_some());
713
714 store.remove_type(&ep_a);
715 assert!(store.lookup_type(&ep_a).is_none());
716 }
717
718 #[test]
719 fn test_cross_session_role_name_edge_collision_regression() {
720 let mut store = SessionStore::new();
721 let sid1 = store.open(
722 vec!["A".into(), "B".into()],
723 &BufferConfig::default(),
724 &BTreeMap::new(),
725 );
726 let sid2 = store.open(
727 vec!["A".into(), "B".into()],
728 &BufferConfig::default(),
729 &BTreeMap::new(),
730 );
731
732 let e1 = Edge::new(sid1, "A", "B");
733 let e2 = Edge::new(sid2, "A", "B");
734 assert_ne!(e1, e2, "edges from distinct sessions must not collide");
735 assert!(store
736 .get(sid1)
737 .expect("sid1 exists")
738 .buffers
739 .contains_key(&e1));
740 assert!(store
741 .get(sid2)
742 .expect("sid2 exists")
743 .buffers
744 .contains_key(&e2));
745 }
746
747 #[test]
748 fn test_edge_handler_and_trace_bindings() {
749 let mut store = SessionStore::new();
750 let sid = store.open(
751 vec!["A".into(), "B".into()],
752 &BufferConfig::default(),
753 &BTreeMap::new(),
754 );
755 let edge = Edge::new(sid, "A", "B");
756
757 assert!(store.lookup_handler(&edge).is_none());
758 store.update_handler(&edge, "handler/send".to_string());
759 assert_eq!(
760 store.lookup_handler(&edge).map(String::as_str),
761 Some("handler/send")
762 );
763
764 assert!(store.lookup_trace(&edge).is_none());
765 store.update_trace(&edge, vec![ValType::Nat]);
766 assert_eq!(store.lookup_trace(&edge), Some([ValType::Nat].as_slice()));
767 }
768
769 #[test]
770 fn test_decode_edge_json_requires_sid_sender_receiver() {
771 let sid_qualified = json!({
772 "sid": 7,
773 "sender": "A",
774 "receiver": "B"
775 });
776 let e = decode_edge_json(&sid_qualified, None).expect("decode sid-qualified edge");
777 assert_eq!(e, Edge::new(7, "A", "B"));
778
779 let no_sid = json!({
780 "sender": "A",
781 "receiver": "B"
782 });
783 let e2 = decode_edge_json(&no_sid, Some(11)).expect("decode edge with sid hint");
784 assert_eq!(e2, Edge::new(11, "A", "B"));
785
786 let legacy = json!({
787 "from": "A",
788 "to": "B",
789 "sid": 11
790 });
791 let err = decode_edge_json(&legacy, None).expect_err("legacy edge shape must be rejected");
792 assert!(err.contains("invalid edge json"), "unexpected error: {err}");
793 }
794}