Skip to main content

sim/runtime/reference_device/
route.rs

1use sim_kernel::{Error, EventKind, EventLedger, Expr, Ref, Result, Symbol};
2use sim_value::build;
3
4use super::{reference_edge_id, reference_glance_profile, reference_pose_receipt};
5
6/// Result of the route-swap survival proof.
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct RouteSwapProof {
9    /// Whether the session id remains unchanged.
10    pub same_session_id: bool,
11    /// Whether the ledger reference remains unchanged.
12    pub same_ledger: bool,
13    /// Whether bound visible consent remains attached.
14    pub consent_survived: bool,
15    /// Whether the event ledger records the rebind.
16    pub events_advanced: bool,
17    /// Whether the device peer surface keeps the reference namespace.
18    pub peer_surface_registered: bool,
19}
20
21impl RouteSwapProof {
22    /// Encodes the proof as expression data for cookbook recipes.
23    pub fn to_expr(&self) -> Expr {
24        build::map(vec![
25            ("kind", build::qsym("device/reference", "route-swap-proof")),
26            ("same-session-id", Expr::Bool(self.same_session_id)),
27            ("same-ledger", Expr::Bool(self.same_ledger)),
28            ("consent-survived", Expr::Bool(self.consent_survived)),
29            ("events-advanced", Expr::Bool(self.events_advanced)),
30            (
31                "peer-surface-registered",
32                Expr::Bool(self.peer_surface_registered),
33            ),
34        ])
35    }
36}
37
38/// Runs the route-swap survival proof.
39pub fn prove_route_swap() -> Result<RouteSwapProof> {
40    let edge = reference_edge_id();
41    let id = edge.as_symbol().clone();
42    let receipt = reference_pose_receipt(9, 100);
43    let consent_expr = receipt.to_expr();
44    let mut session = ReferenceRouteSession::new(id.clone(), reference_link_symbol("direct"))?;
45    session.bind_consent(consent_expr.clone())?;
46    let ledger = session.ledger.clone();
47    let events_before = session.events.len_for_run(&ledger);
48
49    let mut hub = sim_lib_web_bridge::SurfaceHub::new();
50    let peer =
51        sim_lib_web_bridge::register_device_peer(&mut hub, &edge, &reference_glance_profile());
52
53    session.rebind(reference_link_symbol("relay"))?;
54
55    Ok(RouteSwapProof {
56        same_session_id: session.id == id,
57        same_ledger: session.ledger == ledger,
58        consent_survived: session.consent.as_ref() == Some(&consent_expr),
59        events_advanced: session.events.len_for_run(&ledger) == events_before + 1,
60        peer_surface_registered: peer.namespace.as_deref() == Some("device/peer"),
61    })
62}
63
64#[derive(Clone, Debug)]
65struct ReferenceRouteSession {
66    id: Symbol,
67    link: Symbol,
68    ledger: Ref,
69    events: EventLedger,
70    consent: Option<Expr>,
71}
72
73impl ReferenceRouteSession {
74    fn new(id: Symbol, link: Symbol) -> Result<Self> {
75        let ledger = Ref::Symbol(id.clone());
76        let mut session = Self {
77            id,
78            link,
79            ledger,
80            events: EventLedger::new(),
81            consent: None,
82        };
83        session.record_event("open")?;
84        Ok(session)
85    }
86
87    fn bind_consent(&mut self, consent: Expr) -> Result<()> {
88        match sim_value::access::field_sym(&consent, "session") {
89            Some(session) if session == self.id => {}
90            Some(session) => {
91                return Err(Error::HostError(format!(
92                    "consent session '{session}' does not match device edge session '{}'",
93                    self.id
94                )));
95            }
96            None => {
97                return Err(Error::HostError(
98                    "device edge consent is missing session".to_owned(),
99                ));
100            }
101        }
102        self.consent = Some(consent);
103        self.record_event("consent-bound")
104    }
105
106    fn rebind(&mut self, link: Symbol) -> Result<()> {
107        self.link = link;
108        self.record_event("rebind")
109    }
110
111    fn record_event(&mut self, name: &str) -> Result<()> {
112        self.events.push(
113            self.ledger.clone(),
114            EventKind::Trace(Ref::Symbol(Symbol::qualified("device/edge", name))),
115        )?;
116        Ok(())
117    }
118}
119
120fn reference_link_symbol(name: &str) -> Symbol {
121    Symbol::qualified("device/link", name.to_owned())
122}