umsh_node/
mac.rs

1use umsh_core::{ChannelId, ChannelKey, PublicKey};
2use umsh_mac::{
3    AddPeerError, CachedRoute, CapacityError, LocalIdentityId, MacError, MacEventRef, MacHandle,
4    PeerId, Platform, SendError, SendOptions, SendReceipt,
5};
6
7/// Pluggable backend that the node layer delegates to for MAC operations.
8///
9/// [`MacHandle`](umsh_mac::MacHandle) implements `MacBackend`, and test code can provide a
10/// lightweight fake. Making the node layer ([`Host`](crate::Host),
11/// [`LocalNode`](crate::LocalNode), …) generic over this trait keeps the MAC's eight
12/// fixed-capacity const generics confined to the `MacHandle` type rather than propagating
13/// through every node-layer type and free function.
14pub trait MacBackend: Clone {
15    /// Error type returned by send-oriented operations.
16    type SendError;
17    /// Error type returned by fixed-capacity operations.
18    type CapacityError;
19    /// Error type returned by the event-loop driver, [`next_event`](Self::next_event).
20    type RunError;
21
22    /// Drive the MAC until one wake cycle completes, invoking `on_event` for
23    /// each emitted event.
24    ///
25    /// This is the single wake-driven step the node layer builds `pump_once` /
26    /// `run` on top of; it waits for radio activity or a protocol deadline
27    /// rather than busy-polling.
28    async fn next_event(
29        &self,
30        on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>),
31    ) -> Result<(), Self::RunError>;
32
33    /// Add or refresh a peer.
34    async fn add_peer(
35        &self,
36        key: PublicKey,
37    ) -> Result<PeerId, MacBackendError<Self::SendError, Self::CapacityError>>;
38    /// Remove a peer and its per-peer transport state, reporting whether it
39    /// was registered.
40    async fn remove_peer(&self, key: &PublicKey) -> bool {
41        let _ = key;
42        false
43    }
44    /// Ensure `key` holds at least a transient (unpinned, LRU-evictable)
45    /// peer slot, so an explicit reply to a stranger can be sent. Reports
46    /// whether a slot is held.
47    async fn ensure_transient_peer(&self, key: &PublicKey) -> bool {
48        let _ = key;
49        false
50    }
51    /// Add or refresh a private channel.
52    async fn add_private_channel(
53        &self,
54        key: ChannelKey,
55    ) -> Result<(), MacBackendError<Self::SendError, Self::CapacityError>>;
56    /// Add or refresh a named channel.
57    async fn add_named_channel(
58        &self,
59        name: &str,
60    ) -> Result<(), MacBackendError<Self::SendError, Self::CapacityError>>;
61    /// Remove a channel and its replay state, reporting whether it was
62    /// registered.
63    async fn remove_channel(&self, key: &ChannelKey) -> bool {
64        let _ = key;
65        false
66    }
67    /// Queue a broadcast frame.
68    async fn send_broadcast(
69        &self,
70        from: LocalIdentityId,
71        payload: &[u8],
72        options: &SendOptions,
73    ) -> Result<SendReceipt, MacBackendError<Self::SendError, Self::CapacityError>>;
74    /// Queue a multicast frame.
75    async fn send_multicast(
76        &self,
77        from: LocalIdentityId,
78        channel: &ChannelId,
79        payload: &[u8],
80        options: &SendOptions,
81    ) -> Result<SendReceipt, MacBackendError<Self::SendError, Self::CapacityError>>;
82    /// Queue a unicast frame.
83    async fn send_unicast(
84        &self,
85        from: LocalIdentityId,
86        dst: &PublicKey,
87        payload: &[u8],
88        options: &SendOptions,
89    ) -> Result<Option<SendReceipt>, MacBackendError<Self::SendError, Self::CapacityError>>;
90    /// Queue a blind-unicast frame.
91    async fn send_blind_unicast(
92        &self,
93        from: LocalIdentityId,
94        dst: &PublicKey,
95        channel: &ChannelId,
96        payload: &[u8],
97        options: &SendOptions,
98    ) -> Result<Option<SendReceipt>, MacBackendError<Self::SendError, Self::CapacityError>>;
99    /// Fill `dest` with random bytes.
100    async fn fill_random(&self, dest: &mut [u8]);
101    /// Return the current MAC clock time.
102    async fn now_ms(&self) -> u64;
103
104    #[cfg(feature = "software-crypto")]
105    async fn register_ephemeral(
106        &self,
107        parent: LocalIdentityId,
108        identity: umsh_crypto::software::SoftwareIdentity,
109    ) -> Result<LocalIdentityId, MacBackendError<Self::SendError, Self::CapacityError>>;
110
111    #[cfg(feature = "software-crypto")]
112    async fn remove_ephemeral(&self, id: LocalIdentityId) -> bool;
113
114    /// Return the live TX frame counter for `from`, if it identifies a registered identity.
115    async fn frame_counter(&self, from: LocalIdentityId) -> Option<u32> {
116        let _ = from;
117        None
118    }
119
120    /// Return the persisted TX frame-counter boundary for `from`, if registered.
121    async fn persisted_frame_counter(&self, from: LocalIdentityId) -> Option<u32> {
122        let _ = from;
123        None
124    }
125
126    /// Invoke `f` for every peer registered in the MAC-layer peer registry.
127    ///
128    /// Covers all known peers, not just those with an active crypto session.
129    async fn for_each_peer(&self, f: &mut dyn FnMut(umsh_core::PublicKey)) {
130        let _ = f;
131    }
132
133    /// Invoke `f` for each peer with established crypto state for `from`.
134    /// Arguments are `(peer public key, last-accepted RX counter, persisted RX boundary)`.
135    async fn for_each_peer_counter(
136        &self,
137        from: LocalIdentityId,
138        f: &mut dyn FnMut(umsh_core::PublicKey, u32, u32),
139    ) {
140        let _ = (from, f);
141    }
142
143    /// Invoke `f` for each transmitter the radio has heard, with the most
144    /// recent measurements from it.
145    ///
146    /// This is what a peer-repeater listing reports about the hops it names:
147    /// a frame forwarded past this node or simply overheard proves a
148    /// neighbor was on the air, and no peer-oriented view sees it.
149    async fn for_each_transmitter_observation(
150        &self,
151        f: &mut dyn FnMut(umsh_mac::TransmitterObservation),
152    ) {
153        let _ = f;
154    }
155
156    /// Return the route the MAC currently has cached for `peer`.
157    async fn peer_route(&self, peer: &PublicKey) -> Option<CachedRoute> {
158        let _ = peer;
159        None
160    }
161
162    /// Forget the route cached for `peer`, returning whether one was held.
163    async fn clear_peer_route(&self, peer: &PublicKey) -> bool {
164        let _ = peer;
165        false
166    }
167
168    /// Install a route learned before this MAC existed, returning whether
169    /// `peer` was registered to receive it.
170    ///
171    /// A backend that keeps no route cache accepts none, which is the
172    /// same answer it gives to [`Self::peer_route`].
173    async fn restore_peer_route(&self, peer: &PublicKey, route: CachedRoute) -> bool {
174        let _ = (peer, route);
175        false
176    }
177}
178
179/// Normalized wrapper around MAC-backend failures.
180#[derive(Clone, Debug, PartialEq, Eq)]
181pub enum MacBackendError<S, C> {
182    /// Send-oriented MAC failure.
183    Send(S),
184    /// Capacity-related MAC failure.
185    Capacity(C),
186    /// A supplied public key did not decode to a valid Ed25519 point on the curve.
187    InvalidPublicKey,
188    /// A channel name failed canonicalization (non-ASCII or too long).
189    InvalidChannelName(umsh_crypto::ChannelNameError),
190}
191
192impl<
193    'a,
194    P: Platform,
195    const IDENTITIES: usize,
196    const PEERS: usize,
197    const CHANNELS: usize,
198    const ACKS: usize,
199    const TX: usize,
200    const FRAME: usize,
201    const DUP: usize,
202    const RN: usize,
203    const HN: usize,
204> MacBackend for MacHandle<'a, P, IDENTITIES, PEERS, CHANNELS, ACKS, TX, FRAME, DUP, RN, HN>
205{
206    type SendError = SendError;
207    type CapacityError = CapacityError;
208    type RunError = MacError<<P::Radio as umsh_hal::Radio>::Error>;
209
210    async fn next_event(
211        &self,
212        on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>),
213    ) -> Result<(), Self::RunError> {
214        self.next_event(on_event).await
215    }
216
217    async fn add_peer(
218        &self,
219        key: PublicKey,
220    ) -> Result<PeerId, MacBackendError<Self::SendError, Self::CapacityError>> {
221        self.add_peer(key).await.map_err(|err| match err {
222            AddPeerError::Capacity => MacBackendError::Capacity(CapacityError),
223            AddPeerError::InvalidPublicKey => MacBackendError::InvalidPublicKey,
224        })
225    }
226
227    async fn remove_peer(&self, key: &PublicKey) -> bool {
228        self.remove_peer(key).await
229    }
230
231    async fn ensure_transient_peer(&self, key: &PublicKey) -> bool {
232        self.ensure_transient_peer(key).await
233    }
234
235    async fn add_private_channel(
236        &self,
237        key: ChannelKey,
238    ) -> Result<(), MacBackendError<Self::SendError, Self::CapacityError>> {
239        self.add_channel(key)
240            .await
241            .map_err(MacBackendError::Capacity)
242    }
243
244    async fn add_named_channel(
245        &self,
246        name: &str,
247    ) -> Result<(), MacBackendError<Self::SendError, Self::CapacityError>> {
248        self.add_named_channel(name).await.map_err(|err| match err {
249            umsh_mac::AddChannelError::Capacity => MacBackendError::Capacity(CapacityError),
250            umsh_mac::AddChannelError::InvalidName(reason) => {
251                MacBackendError::InvalidChannelName(reason)
252            }
253        })
254    }
255
256    async fn remove_channel(&self, key: &ChannelKey) -> bool {
257        self.remove_channel(key).await
258    }
259
260    async fn send_broadcast(
261        &self,
262        from: LocalIdentityId,
263        payload: &[u8],
264        options: &SendOptions,
265    ) -> Result<SendReceipt, MacBackendError<Self::SendError, Self::CapacityError>> {
266        self.send_broadcast(from, payload, options)
267            .await
268            .map_err(MacBackendError::Send)
269    }
270
271    async fn send_multicast(
272        &self,
273        from: LocalIdentityId,
274        channel: &ChannelId,
275        payload: &[u8],
276        options: &SendOptions,
277    ) -> Result<SendReceipt, MacBackendError<Self::SendError, Self::CapacityError>> {
278        self.send_multicast(from, channel, payload, options)
279            .await
280            .map_err(MacBackendError::Send)
281    }
282
283    async fn send_unicast(
284        &self,
285        from: LocalIdentityId,
286        dst: &PublicKey,
287        payload: &[u8],
288        options: &SendOptions,
289    ) -> Result<Option<SendReceipt>, MacBackendError<Self::SendError, Self::CapacityError>> {
290        self.send_unicast(from, dst, payload, options)
291            .await
292            .map_err(MacBackendError::Send)
293    }
294
295    async fn send_blind_unicast(
296        &self,
297        from: LocalIdentityId,
298        dst: &PublicKey,
299        channel: &ChannelId,
300        payload: &[u8],
301        options: &SendOptions,
302    ) -> Result<Option<SendReceipt>, MacBackendError<Self::SendError, Self::CapacityError>> {
303        self.send_blind_unicast(from, dst, channel, payload, options)
304            .await
305            .map_err(MacBackendError::Send)
306    }
307
308    async fn fill_random(&self, dest: &mut [u8]) {
309        self.fill_random(dest).await
310    }
311
312    async fn now_ms(&self) -> u64 {
313        self.now_ms().await
314    }
315
316    #[cfg(feature = "software-crypto")]
317    async fn register_ephemeral(
318        &self,
319        parent: LocalIdentityId,
320        identity: umsh_crypto::software::SoftwareIdentity,
321    ) -> Result<LocalIdentityId, MacBackendError<Self::SendError, Self::CapacityError>> {
322        self.register_ephemeral(parent, identity)
323            .await
324            .map_err(MacBackendError::Capacity)
325    }
326
327    #[cfg(feature = "software-crypto")]
328    async fn remove_ephemeral(&self, id: LocalIdentityId) -> bool {
329        self.remove_ephemeral(id).await
330    }
331
332    async fn frame_counter(&self, from: LocalIdentityId) -> Option<u32> {
333        self.frame_counter(from).await
334    }
335
336    async fn persisted_frame_counter(&self, from: LocalIdentityId) -> Option<u32> {
337        self.persisted_frame_counter(from).await
338    }
339
340    async fn for_each_peer(&self, f: &mut dyn FnMut(umsh_core::PublicKey)) {
341        self.for_each_peer(f).await
342    }
343
344    async fn for_each_peer_counter(
345        &self,
346        from: LocalIdentityId,
347        f: &mut dyn FnMut(umsh_core::PublicKey, u32, u32),
348    ) {
349        self.for_each_peer_counter(from, f).await
350    }
351
352    async fn for_each_transmitter_observation(
353        &self,
354        f: &mut dyn FnMut(umsh_mac::TransmitterObservation),
355    ) {
356        self.for_each_transmitter_observation(f).await
357    }
358
359    async fn peer_route(&self, peer: &PublicKey) -> Option<CachedRoute> {
360        self.peer_route(peer).await
361    }
362
363    async fn clear_peer_route(&self, peer: &PublicKey) -> bool {
364        self.clear_peer_route(peer).await
365    }
366
367    async fn restore_peer_route(&self, peer: &PublicKey, route: CachedRoute) -> bool {
368        self.restore_peer_route(peer, route).await
369    }
370}