use sim_kernel::{Error, EventKind, EventLedger, Expr, Ref, Result, Symbol};
use sim_value::build;
use super::{reference_edge_id, reference_glance_profile, reference_pose_receipt};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RouteSwapProof {
pub same_session_id: bool,
pub same_ledger: bool,
pub consent_survived: bool,
pub events_advanced: bool,
pub peer_surface_registered: bool,
}
impl RouteSwapProof {
pub fn to_expr(&self) -> Expr {
build::map(vec![
("kind", build::qsym("device/reference", "route-swap-proof")),
("same-session-id", Expr::Bool(self.same_session_id)),
("same-ledger", Expr::Bool(self.same_ledger)),
("consent-survived", Expr::Bool(self.consent_survived)),
("events-advanced", Expr::Bool(self.events_advanced)),
(
"peer-surface-registered",
Expr::Bool(self.peer_surface_registered),
),
])
}
}
pub fn prove_route_swap() -> Result<RouteSwapProof> {
let edge = reference_edge_id();
let id = edge.as_symbol().clone();
let receipt = reference_pose_receipt(9, 100);
let consent_expr = receipt.to_expr();
let mut session = ReferenceRouteSession::new(id.clone(), reference_link_symbol("direct"))?;
session.bind_consent(consent_expr.clone())?;
let ledger = session.ledger.clone();
let events_before = session.events.len_for_run(&ledger);
let mut hub = sim_lib_web_bridge::SurfaceHub::new();
let peer =
sim_lib_web_bridge::register_device_peer(&mut hub, &edge, &reference_glance_profile());
session.rebind(reference_link_symbol("relay"))?;
Ok(RouteSwapProof {
same_session_id: session.id == id,
same_ledger: session.ledger == ledger,
consent_survived: session.consent.as_ref() == Some(&consent_expr),
events_advanced: session.events.len_for_run(&ledger) == events_before + 1,
peer_surface_registered: peer.namespace.as_deref() == Some("device/peer"),
})
}
#[derive(Clone, Debug)]
struct ReferenceRouteSession {
id: Symbol,
link: Symbol,
ledger: Ref,
events: EventLedger,
consent: Option<Expr>,
}
impl ReferenceRouteSession {
fn new(id: Symbol, link: Symbol) -> Result<Self> {
let ledger = Ref::Symbol(id.clone());
let mut session = Self {
id,
link,
ledger,
events: EventLedger::new(),
consent: None,
};
session.record_event("open")?;
Ok(session)
}
fn bind_consent(&mut self, consent: Expr) -> Result<()> {
match sim_value::access::field_sym(&consent, "session") {
Some(session) if session == self.id => {}
Some(session) => {
return Err(Error::HostError(format!(
"consent session '{session}' does not match device edge session '{}'",
self.id
)));
}
None => {
return Err(Error::HostError(
"device edge consent is missing session".to_owned(),
));
}
}
self.consent = Some(consent);
self.record_event("consent-bound")
}
fn rebind(&mut self, link: Symbol) -> Result<()> {
self.link = link;
self.record_event("rebind")
}
fn record_event(&mut self, name: &str) -> Result<()> {
self.events.push(
self.ledger.clone(),
EventKind::Trace(Ref::Symbol(Symbol::qualified("device/edge", name))),
)?;
Ok(())
}
}
fn reference_link_symbol(name: &str) -> Symbol {
Symbol::qualified("device/link", name.to_owned())
}