Skip to main content

clawft_kernel/licence/
store_sync.rs

1//! Read accessors the mesh exchange needs from the stores (ADR-106 phase 1b):
2//! what is held, for the cheap pre-verify filters, and what to serve in a
3//! catch-up sync. Nothing here changes state.
4
5use std::ops::Bound;
6
7use super::{
8    ApprovalStore, CheckoutGrantStore, SignedApproval, SignedBinding, SignedEnvelope, SignedGrant,
9};
10
11impl CheckoutGrantStore {
12    /// The stored binding for the local mesh, whatever its state, with its
13    /// `seq`. An unbound record is still returned: its `seq` is what stops an
14    /// older bind from being replayed.
15    pub fn held_signed_binding(&self) -> Option<(u64, SignedBinding)> {
16        let local = self.local_mesh_id().get()?.to_hex();
17        let g = self.lock();
18        if g.poisoned.is_some() {
19            return None;
20        }
21        let h = g.binding.as_ref().filter(|h| h.body.mesh_id == local)?;
22        Some((h.body.seq, h.signed.clone()))
23    }
24
25    /// The grant key of the binding in effect, lower-case hex.
26    pub fn bound_grant_key(&self) -> Option<String> {
27        self.run(|g, ev| self.binding_in_effect(g, ev).ok().map(|b| b.grant_pubkey))
28    }
29
30    /// The current grant held for `cog_id` `version`, with its `seq`.
31    pub fn held_grant(&self, cog_id: &str, version: &str) -> Option<(u64, SignedGrant)> {
32        let g = self.lock();
33        let h = g
34            .slots
35            .get(&(cog_id.to_owned(), version.to_owned()))?
36            .current
37            .as_ref()?;
38        Some((h.body.seq, h.signed.clone()))
39    }
40
41    /// A page of the highest-`seq` grant per (cog, version), as `(seq, cog,
42    /// version, grant)` ordered by that tuple and strictly after `after`,
43    /// within `max_entries` and `max_bytes`; and whether more follow. Empty
44    /// unless a binding is in effect. Expired and withdrawn grants are
45    /// included: they are `seq` tombstones. Only the keys of the whole set
46    /// are sorted; only the page's envelopes are cloned.
47    pub fn sync_grants_page(
48        &self,
49        after: Option<(u64, &str, &str)>,
50        max_entries: usize,
51        max_bytes: usize,
52    ) -> (Vec<(u64, String, String, SignedGrant)>, bool) {
53        self.run(|g, ev| {
54            if self.binding_in_effect(g, ev).is_err() {
55                return (Vec::new(), false);
56            }
57            let mut keys: Vec<(u64, &String, &String)> = g
58                .slots
59                .iter()
60                .filter_map(|((cog, ver), s)| Some((s.current.as_ref()?.body.seq, cog, ver)))
61                .filter(|(seq, cog, ver)| {
62                    after.is_none_or(|(s, c, v)| (*seq, cog.as_str(), ver.as_str()) > (s, c, v))
63                })
64                .collect();
65            keys.sort();
66            let (mut out, mut bytes) = (Vec::new(), 0usize);
67            for (seq, cog, ver) in &keys {
68                let h = g.slots[&((*cog).clone(), (*ver).clone())].current.as_ref();
69                let Some(h) = h else { continue };
70                let n = env_size(&h.signed);
71                if out.len() >= max_entries || bytes + n > max_bytes {
72                    return (out, true);
73                }
74                bytes += n;
75                out.push((*seq, (*cog).clone(), (*ver).clone(), h.signed.clone()));
76            }
77            (out, false)
78        })
79    }
80}
81
82/// Wire size of an envelope, for the sync caps.
83pub(super) fn env_size(e: &SignedEnvelope) -> usize {
84    e.payload.len() + e.public_key.len() + e.signature.len()
85}
86
87impl ApprovalStore {
88    /// True when an approval with this content key is held.
89    pub fn holds(&self, content_key: &str) -> bool {
90        self.lock().0.contains_key(content_key)
91    }
92
93    /// A page of active approvals (for the local mesh) as `(content key,
94    /// approval)` ordered by content key and strictly after `after`, within
95    /// `max_entries` and `max_bytes`; and whether more follow. Empty while
96    /// poisoned.
97    pub fn sync_approvals_page(
98        &self,
99        after: Option<&str>,
100        max_entries: usize,
101        max_bytes: usize,
102    ) -> (Vec<(String, SignedApproval)>, bool) {
103        let Some(local) = self.local.get().map(|m| m.to_hex()) else {
104            return (Vec::new(), false);
105        };
106        let g = self.lock();
107        if g.1.is_some() {
108            return (Vec::new(), false);
109        }
110        let from = after.map_or(Bound::Unbounded, |a| Bound::Excluded(a.to_owned()));
111        let (mut out, mut bytes) = (Vec::new(), 0usize);
112        for (k, (s, a)) in g.0.range((from, Bound::Unbounded)) {
113            if a.mesh_id != local {
114                continue;
115            }
116            let n = env_size(s);
117            if out.len() >= max_entries || bytes + n > max_bytes {
118                return (out, true);
119            }
120            bytes += n;
121            out.push((k.clone(), s.clone()));
122        }
123        (out, false)
124    }
125}