Skip to main content

telltale_vm/session/
state.rs

1/// State of a single session.
2///
3/// Stores per-endpoint local types (the type truth), message buffers,
4/// and lifecycle status. Matches Lean `SessionState`.
5#[derive(Debug, Serialize)]
6pub struct SessionState {
7    /// Session identifier.
8    pub sid: SessionId,
9    /// Role names in this session.
10    pub roles: Vec<String>,
11    /// Deterministic internal ids for participant roles.
12    #[serde(skip)]
13    role_ids: BTreeMap<String, u16>,
14    /// Per-endpoint local type state. This IS the type truth.
15    ///
16    /// Matches Lean `localTypes : List (Endpoint × LocalType)`.
17    pub local_types: BTreeMap<Endpoint, TypeEntry>,
18    /// Message buffers keyed by directed edge.
19    pub buffers: BTreeMap<Edge, SignedBuffer<Signature>>,
20    /// Deterministic internal edge lookup keyed by interned role ids.
21    #[serde(skip)]
22    edge_lookup: BTreeMap<(u16, u16), Edge>,
23    /// Deterministic internal ids for bound handlers.
24    #[serde(skip)]
25    handler_ids: BTreeMap<HandlerId, HandlerNumericId>,
26    /// Reverse lookup for internal handler ids.
27    #[serde(skip)]
28    handlers_by_id: Vec<HandlerId>,
29    /// Deterministic handler binding keyed by internal edge ids.
30    #[serde(skip)]
31    edge_handler_lookup: BTreeMap<(u16, u16), HandlerNumericId>,
32    /// Session-wide fallback handler id.
33    #[serde(skip)]
34    default_handler_id: Option<HandlerNumericId>,
35    /// Deterministic internal ids for branch labels reachable from current local types.
36    #[serde(skip)]
37    label_ids: BTreeMap<String, LabelNumericId>,
38    /// Reverse lookup for internal label ids.
39    #[serde(skip)]
40    labels_by_id: Vec<String>,
41    /// Cached branch resolution keyed by endpoint then label id.
42    #[serde(skip)]
43    branch_lookup: BTreeMap<Endpoint, BTreeMap<LabelNumericId, CachedBranch>>,
44    /// Per-edge authenticated leaves for Merkle-auth tracking.
45    pub auth_leaves: BTreeMap<Edge, Vec<Hash>>,
46    /// Per-edge Merkle trees for incremental authenticated updates.
47    #[serde(default)]
48    pub auth_trees: BTreeMap<Edge, AuthTree>,
49    /// Per-edge Merkle roots for signed-buffer history.
50    pub auth_roots: BTreeMap<Edge, Hash>,
51    /// Optional handler binding per edge.
52    pub edge_handlers: BTreeMap<Edge, HandlerId>,
53    /// Session-wide fallback handler id.
54    #[serde(default = "default_handler_id")]
55    pub default_handler: HandlerId,
56    /// Coherence trace by edge.
57    pub edge_traces: BTreeMap<Edge, Vec<ValType>>,
58    /// Current status.
59    pub status: SessionStatus,
60    /// Epoch counter for draining.
61    pub epoch: usize,
62}
63
64impl SessionState {
65    pub(crate) fn from_open_plan(
66        sid: SessionId,
67        plan: &SessionOpenPlan,
68        buffer_config: &BufferConfig,
69    ) -> Self {
70        let mut local_type_entries = Vec::with_capacity(plan.initial_types.len());
71        for (role, current, original) in &plan.initial_types {
72            local_type_entries.push((
73                Endpoint {
74                    sid,
75                    role: role.clone(),
76                },
77                TypeEntry {
78                    current: current.clone(),
79                    original: original.clone(),
80                },
81            ));
82        }
83        let local_types = local_type_entries.into_iter().collect();
84
85        let mut edge_entries = Vec::with_capacity(plan.edge_blueprint().len());
86        let mut buffer_entries = Vec::with_capacity(plan.edge_blueprint().len());
87        for (key, from, to) in plan.edge_blueprint() {
88            let edge = Edge::new(sid, from.clone(), to.clone());
89            edge_entries.push((*key, edge.clone()));
90            buffer_entries.push((edge, BoundedBuffer::new(buffer_config)));
91        }
92        let edge_lookup = edge_entries.into_iter().collect();
93        let buffers = buffer_entries.into_iter().collect();
94
95        let default_handler = default_handler_id();
96        let (handler_ids, handlers_by_id, edge_handler_lookup, default_handler_id) =
97            Self::build_handler_indexes(&plan.role_ids, &default_handler, &BTreeMap::new());
98
99        let mut state = Self {
100            sid,
101            roles: plan.roles.clone(),
102            role_ids: plan.role_ids.clone(),
103            local_types,
104            buffers,
105            edge_lookup,
106            handler_ids,
107            handlers_by_id,
108            edge_handler_lookup,
109            default_handler_id,
110            label_ids: BTreeMap::new(),
111            labels_by_id: Vec::new(),
112            branch_lookup: BTreeMap::new(),
113            auth_leaves: BTreeMap::new(),
114            auth_trees: BTreeMap::new(),
115            auth_roots: BTreeMap::new(),
116            edge_handlers: BTreeMap::new(),
117            default_handler,
118            edge_traces: BTreeMap::new(),
119            status: SessionStatus::Active,
120            epoch: 0,
121        };
122        for role in &plan.active_branch_roles {
123            state.refresh_endpoint_branch_lookup(&Endpoint {
124                sid,
125                role: role.clone(),
126            });
127        }
128        state
129    }
130
131    fn retained_session_core_bytes(&self) -> usize {
132        std::mem::size_of::<Self>()
133            .saturating_add(serialized_bytes(&self.sid))
134            .saturating_add(serialized_bytes(&self.roles))
135            .saturating_add(serialized_bytes(&self.role_ids))
136            .saturating_add(serialized_bytes(&self.edge_lookup))
137            .saturating_add(serialized_bytes(&self.handler_ids))
138            .saturating_add(serialized_bytes(&self.handlers_by_id))
139            .saturating_add(serialized_bytes(&self.edge_handler_lookup))
140            .saturating_add(serialized_bytes(&self.default_handler_id))
141            .saturating_add(serialized_bytes(&self.label_ids))
142            .saturating_add(serialized_bytes(&self.labels_by_id))
143            .saturating_add(serialized_bytes(&self.branch_lookup))
144            .saturating_add(serialized_bytes(&self.status))
145            .saturating_add(serialized_bytes(&self.epoch))
146    }
147
148    fn retained_local_type_bytes(&self) -> usize {
149        serialized_bytes(&self.local_types)
150    }
151
152    fn retained_buffer_bytes(&self) -> usize {
153        serialized_bytes(&self.buffers)
154    }
155
156    fn retained_trace_bytes(&self) -> usize {
157        serialized_bytes(&self.edge_traces)
158    }
159
160    fn retained_auth_bytes(&self) -> usize {
161        serialized_bytes(&self.auth_leaves)
162            .saturating_add(serialized_bytes(&self.auth_trees))
163            .saturating_add(serialized_bytes(&self.auth_roots))
164    }
165
166    fn retained_handler_bytes(&self) -> usize {
167        serialized_bytes(&self.edge_handlers)
168            .saturating_add(serialized_bytes(&self.default_handler))
169    }
170
171    fn rebuild_derived_indexes(&mut self) {
172        self.role_ids = Self::build_role_ids(&self.roles);
173        self.edge_lookup = Self::build_edge_lookup_from_buffers(&self.role_ids, &self.buffers);
174        self.refresh_handler_indexes();
175        self.label_ids = BTreeMap::new();
176        self.labels_by_id = Vec::new();
177        self.branch_lookup = BTreeMap::new();
178        let endpoints: Vec<Endpoint> = self.local_types.keys().cloned().collect();
179        for endpoint in endpoints {
180            self.refresh_endpoint_branch_lookup(&endpoint);
181        }
182    }
183
184    pub(crate) fn refresh_handler_indexes(&mut self) {
185        let (handler_ids, handlers_by_id, edge_handler_lookup, default_handler_id) =
186            Self::build_handler_indexes(&self.role_ids, &self.default_handler, &self.edge_handlers);
187        self.handler_ids = handler_ids;
188        self.handlers_by_id = handlers_by_id;
189        self.edge_handler_lookup = edge_handler_lookup;
190        self.default_handler_id = default_handler_id;
191    }
192
193    pub(crate) fn build_role_ids(roles: &[String]) -> BTreeMap<String, u16> {
194        roles
195            .iter()
196            .enumerate()
197            .map(|(idx, role)| {
198                (
199                    role.clone(),
200                    u16::try_from(idx).expect("role count should fit in u16"),
201                )
202            })
203            .collect()
204    }
205
206    pub(crate) fn build_edge_lookup_from_buffers(
207        role_ids: &BTreeMap<String, u16>,
208        buffers: &BTreeMap<Edge, SignedBuffer<Signature>>,
209    ) -> BTreeMap<EdgeKey, Edge> {
210        let mut lookup = BTreeMap::new();
211        for edge in buffers.keys() {
212            let Some(from_id) = role_ids.get(&edge.sender) else {
213                continue;
214            };
215            let Some(to_id) = role_ids.get(&edge.receiver) else {
216                continue;
217            };
218            lookup.insert((*from_id, *to_id), edge.clone());
219        }
220        lookup
221    }
222
223    pub(crate) fn build_handler_indexes(
224        role_ids: &BTreeMap<String, u16>,
225        default_handler: &str,
226        edge_handlers: &BTreeMap<Edge, HandlerId>,
227    ) -> HandlerIndexBuild {
228        let mut handler_ids = BTreeMap::new();
229        let mut handlers_by_id = Vec::new();
230        let intern_handler = |handler: &str,
231                              handler_ids: &mut BTreeMap<HandlerId, HandlerNumericId>,
232                              handlers_by_id: &mut Vec<HandlerId>|
233         -> HandlerNumericId {
234            if let Some(id) = handler_ids.get(handler) {
235                return *id;
236            }
237            let id = u16::try_from(handlers_by_id.len()).expect("handler count should fit in u16");
238            let owned = handler.to_string();
239            handler_ids.insert(owned.clone(), id);
240            handlers_by_id.push(owned);
241            id
242        };
243
244        let default_handler_id = (!default_handler.is_empty())
245            .then(|| intern_handler(default_handler, &mut handler_ids, &mut handlers_by_id));
246
247        let mut edge_handler_lookup = BTreeMap::new();
248        for (edge, handler) in edge_handlers {
249            let Some(from_id) = role_ids.get(&edge.sender) else {
250                continue;
251            };
252            let Some(to_id) = role_ids.get(&edge.receiver) else {
253                continue;
254            };
255            let handler_id = intern_handler(handler, &mut handler_ids, &mut handlers_by_id);
256            edge_handler_lookup.insert((*from_id, *to_id), handler_id);
257        }
258
259        (
260            handler_ids,
261            handlers_by_id,
262            edge_handler_lookup,
263            default_handler_id,
264        )
265    }
266
267    fn edge_for_roles(&self, from: &str, to: &str) -> Option<&Edge> {
268        let from_id = self.role_ids.get(from)?;
269        let to_id = self.role_ids.get(to)?;
270        self.edge_lookup.get(&(*from_id, *to_id))
271    }
272
273    fn edge_key_for_roles(&self, from: &str, to: &str) -> Option<(u16, u16)> {
274        let from_id = self.role_ids.get(from)?;
275        let to_id = self.role_ids.get(to)?;
276        Some((*from_id, *to_id))
277    }
278
279    fn intern_label(&mut self, label: &str) -> LabelNumericId {
280        if let Some(id) = self.label_ids.get(label) {
281            return *id;
282        }
283        let id = u16::try_from(self.labels_by_id.len()).expect("label count should fit in u16");
284        let owned = label.to_string();
285        self.label_ids.insert(owned.clone(), id);
286        self.labels_by_id.push(owned);
287        id
288    }
289
290    fn intern_handler_binding(&mut self, handler: &str) -> HandlerNumericId {
291        if let Some(id) = self.handler_ids.get(handler) {
292            return *id;
293        }
294        let id = u16::try_from(self.handlers_by_id.len()).expect("handler count should fit in u16");
295        let owned = handler.to_string();
296        self.handler_ids.insert(owned.clone(), id);
297        self.handlers_by_id.push(owned);
298        id
299    }
300
301    fn handler_by_id(&self, handler_id: HandlerNumericId) -> Option<&HandlerId> {
302        self.handlers_by_id.get(usize::from(handler_id))
303    }
304
305    fn branch_shape(local_type: &LocalTypeR) -> Option<(BranchDirection, &str, LocalBranches<'_>)> {
306        match local_type {
307            LocalTypeR::Send { partner, branches } => {
308                Some((BranchDirection::Send, partner.as_str(), branches.as_slice()))
309            }
310            LocalTypeR::Recv { partner, branches } => {
311                Some((BranchDirection::Recv, partner.as_str(), branches.as_slice()))
312            }
313            _ => None,
314        }
315    }
316
317    pub(crate) fn refresh_endpoint_branch_lookup(&mut self, ep: &Endpoint) {
318        self.branch_lookup.remove(ep);
319        let Some(entry) = self.local_types.get(ep) else {
320            return;
321        };
322        let Some((direction, partner, branches)) = Self::branch_shape(&entry.current) else {
323            return;
324        };
325        let partner = partner.to_string();
326        let branches: Vec<(String, Option<ValType>, LocalTypeR)> = branches
327            .iter()
328            .map(|(label, expected_type, continuation)| {
329                (
330                    label.name.clone(),
331                    expected_type.clone(),
332                    continuation.clone(),
333                )
334            })
335            .collect();
336
337        let mut endpoint_lookup = BTreeMap::new();
338        for (label, expected_type, continuation) in branches {
339            let label_id = self.intern_label(&label);
340            endpoint_lookup.insert(
341                label_id,
342                CachedBranch {
343                    direction,
344                    partner: partner.clone(),
345                    expected_type,
346                    continuation,
347                },
348            );
349        }
350        if !endpoint_lookup.is_empty() {
351            self.branch_lookup.insert(ep.clone(), endpoint_lookup);
352        }
353    }
354
355    /// Lookup a cached branch resolution for an endpoint and label.
356    #[must_use]
357    pub(crate) fn lookup_branch_resolution(
358        &self,
359        ep: &Endpoint,
360        label: &str,
361    ) -> Option<&CachedBranch> {
362        let label_id = self.label_ids.get(label)?;
363        self.branch_lookup.get(ep)?.get(label_id)
364    }
365
366    fn update_auth_tree(&mut self, edge: &Edge, signed: &SignedValue<Signature>) {
367        let bytes = bincode::serialize(signed).unwrap_or_default();
368        let leaf = DefaultVerificationModel::hash(HashTag::MerkleLeaf, &bytes);
369        self.auth_leaves.entry(edge.clone()).or_default().push(leaf);
370        let tree = self
371            .auth_trees
372            .entry(edge.clone())
373            .or_insert_with(|| AuthTree::new(Vec::new()));
374        tree.append_leaf(leaf);
375        self.auth_roots.insert(edge.clone(), tree.root());
376    }
377
378    /// Send a signed value from one role to another.
379    ///
380    /// # Errors
381    ///
382    /// Returns an error if no buffer exists for the given edge.
383    pub fn send_signed(
384        &mut self,
385        from: &str,
386        to: &str,
387        signed: &SignedValue<Signature>,
388    ) -> Result<crate::buffer::EnqueueResult, String> {
389        let edge = self
390            .edge_for_roles(from, to)
391            .cloned()
392            .ok_or_else(|| format!("no buffer for edge {from} → {to}"))?;
393        let buf = self
394            .buffers
395            .get_mut(&edge)
396            .ok_or_else(|| format!("no buffer for edge {from} → {to}"))?;
397        let result = buf.enqueue(signed.clone());
398        if matches!(result, crate::buffer::EnqueueResult::Ok) {
399            self.update_auth_tree(&edge, signed);
400        }
401        Ok(result)
402    }
403
404    /// Send a value from one role to another.
405    ///
406    /// Returns the enqueue result from the buffer.
407    ///
408    /// # Errors
409    ///
410    /// Returns an error if no buffer exists for the given edge.
411    pub fn send(
412        &mut self,
413        from: &str,
414        to: &str,
415        val: Value,
416    ) -> Result<crate::buffer::EnqueueResult, String> {
417        let signer = signing_key_for_endpoint(&Endpoint {
418            sid: self.sid,
419            role: from.to_string(),
420        });
421        let signature = signValue(&val, &signer);
422        self.send_signed(
423            from,
424            to,
425            &SignedValue {
426                payload: val,
427                signature,
428                sequence_no: 0,
429            },
430        )
431    }
432
433    /// Send a value from one role to another with explicit sequence number.
434    ///
435    /// # Errors
436    ///
437    /// Returns an error if no buffer exists for the given edge.
438    pub fn send_with_sequence(
439        &mut self,
440        from: &str,
441        to: &str,
442        val: Value,
443        sequence_no: u64,
444    ) -> Result<crate::buffer::EnqueueResult, String> {
445        let signer = signing_key_for_endpoint(&Endpoint {
446            sid: self.sid,
447            role: from.to_string(),
448        });
449        let signature = signValue(&val, &signer);
450        self.send_signed(
451            from,
452            to,
453            &SignedValue {
454                payload: val,
455                signature,
456                sequence_no,
457            },
458        )
459    }
460
461    /// Receive a signed value destined for a role from a specific sender.
462    pub fn recv_signed(&mut self, from: &str, to: &str) -> Option<SignedValue<Signature>> {
463        let edge = self.edge_for_roles(from, to)?.clone();
464        self.buffers.get_mut(&edge).and_then(|buf| buf.dequeue())
465    }
466
467    /// Receive and verify a value destined for a role from a specific sender.
468    ///
469    /// # Errors
470    ///
471    /// Returns an error if signature verification fails.
472    pub fn recv_verified_signed(
473        &mut self,
474        from: &str,
475        to: &str,
476    ) -> Result<Option<SignedValue<Signature>>, String> {
477        let sender = Endpoint {
478            sid: self.sid,
479            role: from.to_string(),
480        };
481        let verifying = verifying_key_for_endpoint(&sender);
482        let signed = self.recv_signed(from, to);
483        let Some(signed) = signed else {
484            return Ok(None);
485        };
486        if !verifySignedValue(&signed.payload, &signed.signature, &verifying) {
487            return Err(format!(
488                "signature verification failed on edge {from} -> {to}"
489            ));
490        }
491        Ok(Some(signed))
492    }
493
494    /// Receive and verify a value destined for a role from a specific sender.
495    ///
496    /// # Errors
497    ///
498    /// Returns an error if signature verification fails.
499    pub fn recv_verified(&mut self, from: &str, to: &str) -> Result<Option<Value>, String> {
500        Ok(self
501            .recv_verified_signed(from, to)?
502            .map(|signed| signed.payload))
503    }
504
505    /// Receive a value destined for a role from a specific sender.
506    pub fn recv(&mut self, from: &str, to: &str) -> Option<Value> {
507        self.recv_verified(from, to).ok().flatten()
508    }
509
510    /// Check if there is a message available on an edge.
511    #[must_use]
512    pub fn has_message(&self, from: &str, to: &str) -> bool {
513        let Some(edge) = self.edge_for_roles(from, to) else {
514            return false;
515        };
516        self.buffers.get(edge).is_some_and(|buf| !buf.is_empty())
517    }
518
519    /// Lookup an edge-bound handler by role pair using the internal numeric path.
520    #[must_use]
521    pub fn lookup_handler_for_roles(&self, from: &str, to: &str) -> Option<&HandlerId> {
522        if self.edge_handlers.is_empty() {
523            return None;
524        }
525        let edge_key = self.edge_key_for_roles(from, to)?;
526        let handler_id = self.edge_handler_lookup.get(&edge_key)?;
527        self.handler_by_id(*handler_id)
528    }
529
530    /// Lookup the session-wide fallback handler using the internal numeric path.
531    #[must_use]
532    pub fn default_handler_binding(&self) -> Option<&HandlerId> {
533        if self.default_handler.is_empty() {
534            return None;
535        }
536        let handler_id = self.default_handler_id?;
537        self.handler_by_id(handler_id)
538    }
539
540    /// Whether the session currently has any handler binding configured.
541    #[must_use]
542    pub fn has_bound_handler(&self) -> bool {
543        !self.default_handler.is_empty() || !self.edge_handlers.is_empty()
544    }
545}
546
547#[derive(Debug, Deserialize)]
548struct SessionStateSerde {
549    sid: SessionId,
550    roles: Vec<String>,
551    local_types: BTreeMap<Endpoint, TypeEntry>,
552    buffers: BTreeMap<Edge, SignedBuffer<Signature>>,
553    auth_leaves: BTreeMap<Edge, Vec<Hash>>,
554    #[serde(default)]
555    auth_trees: BTreeMap<Edge, AuthTree>,
556    auth_roots: BTreeMap<Edge, Hash>,
557    edge_handlers: BTreeMap<Edge, HandlerId>,
558    #[serde(default = "default_handler_id")]
559    default_handler: HandlerId,
560    edge_traces: BTreeMap<Edge, Vec<ValType>>,
561    status: SessionStatus,
562    epoch: usize,
563}