sim/runtime/reference_device/
consent.rs1use sim_kernel::{Cx, Expr, Result};
2use sim_lib_view_device::{
3 ConsentReceipt, DeviceCapability, DeviceSampleStore, EdgeId, FrameClock, RateClass,
4 RetentionReaper, StoreKey, StoredSample, require_with_consent,
5};
6use sim_value::build;
7
8#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct ConsentProof {
11 pub denied_without_kernel_grant: bool,
13 pub denied_without_visible_grant: bool,
15 pub sample_evicted: bool,
17 pub content_evicted: bool,
19}
20
21impl ConsentProof {
22 pub fn to_expr(&self) -> Expr {
24 build::map(vec![
25 ("kind", build::qsym("device/reference", "consent-proof")),
26 (
27 "denied-without-kernel-grant",
28 Expr::Bool(self.denied_without_kernel_grant),
29 ),
30 (
31 "denied-without-visible-grant",
32 Expr::Bool(self.denied_without_visible_grant),
33 ),
34 ("sample-evicted", Expr::Bool(self.sample_evicted)),
35 ("content-evicted", Expr::Bool(self.content_evicted)),
36 ])
37 }
38}
39
40pub fn reference_edge_id() -> EdgeId {
42 EdgeId::named("reference-device")
43}
44
45pub fn reference_pose_receipt(seq: u64, retain_ms: u64) -> ConsentReceipt {
47 ConsentReceipt::new(
48 vec![DeviceCapability::Pose.grant_symbol()],
49 retain_ms,
50 Vec::new(),
51 reference_edge_id(),
52 seq,
53 )
54}
55
56pub fn require_reference_pose(cx: &Cx, receipt: &ConsentReceipt) -> Result<()> {
58 require_with_consent(
59 cx,
60 DeviceCapability::Pose.as_str(),
61 receipt,
62 &reference_edge_id(),
63 )
64}
65
66pub fn prove_consent_without_kernel_grant(cx: &Cx) -> ConsentProof {
68 let receipt = reference_pose_receipt(7, 5);
69 let empty_receipt = ConsentReceipt::new(Vec::new(), 5, Vec::new(), reference_edge_id(), 8);
70 let denied_without_kernel_grant = require_reference_pose(cx, &receipt).is_err();
71 let denied_without_visible_grant = require_reference_pose(cx, &empty_receipt).is_err();
72 let retention = prove_retention_reaper(&receipt);
73 ConsentProof {
74 denied_without_kernel_grant,
75 denied_without_visible_grant,
76 sample_evicted: retention.sample_evicted,
77 content_evicted: retention.content_evicted,
78 }
79}
80
81#[derive(Clone, Debug, PartialEq, Eq)]
83pub struct RetentionProof {
84 pub sample_evicted: bool,
86 pub content_evicted: bool,
88}
89
90pub fn prove_retention_reaper(receipt: &ConsentReceipt) -> RetentionProof {
92 let sample_key = StoreKey::named("pose-sample");
93 let content_key = StoreKey::named("pose-content");
94 let mut store = DeviceSampleStore::new();
95 store.insert_content(content_key.clone(), build::text("pose payload"));
96 store.insert_sample(StoredSample::new(
97 sample_key.clone(),
98 receipt.seq,
99 0,
100 vec![content_key.clone()],
101 build::map(vec![("pose", build::uint(1))]),
102 ));
103 let _evicted = RetentionReaper::new().sweep(
104 &mut store,
105 std::slice::from_ref(receipt),
106 FrameClock::new(
107 receipt.retain_ms.saturating_add(2),
108 RateClass::safe_default(),
109 ),
110 );
111 RetentionProof {
112 sample_evicted: !store.contains_sample(&sample_key),
113 content_evicted: !store.contains_content(&content_key),
114 }
115}