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    /// Return the route the MAC currently has cached for `peer`.
144    async fn peer_route(&self, peer: &PublicKey) -> Option<CachedRoute> {
145        let _ = peer;
146        None
147    }
148
149    /// Forget the route cached for `peer`, returning whether one was held.
150    async fn clear_peer_route(&self, peer: &PublicKey) -> bool {
151        let _ = peer;
152        false
153    }
154}
155
156/// Normalized wrapper around MAC-backend failures.
157#[derive(Clone, Debug, PartialEq, Eq)]
158pub enum MacBackendError<S, C> {
159    /// Send-oriented MAC failure.
160    Send(S),
161    /// Capacity-related MAC failure.
162    Capacity(C),
163    /// A supplied public key did not decode to a valid Ed25519 point on the curve.
164    InvalidPublicKey,
165    /// A channel name failed canonicalization (non-ASCII or too long).
166    InvalidChannelName(umsh_crypto::ChannelNameError),
167}
168
169impl<
170    'a,
171    P: Platform,
172    const IDENTITIES: usize,
173    const PEERS: usize,
174    const CHANNELS: usize,
175    const ACKS: usize,
176    const TX: usize,
177    const FRAME: usize,
178    const DUP: usize,
179    const RN: usize,
180    const HN: usize,
181> MacBackend for MacHandle<'a, P, IDENTITIES, PEERS, CHANNELS, ACKS, TX, FRAME, DUP, RN, HN>
182{
183    type SendError = SendError;
184    type CapacityError = CapacityError;
185    type RunError = MacError<<P::Radio as umsh_hal::Radio>::Error>;
186
187    async fn next_event(
188        &self,
189        on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>),
190    ) -> Result<(), Self::RunError> {
191        self.next_event(on_event).await
192    }
193
194    async fn add_peer(
195        &self,
196        key: PublicKey,
197    ) -> Result<PeerId, MacBackendError<Self::SendError, Self::CapacityError>> {
198        self.add_peer(key).await.map_err(|err| match err {
199            AddPeerError::Capacity => MacBackendError::Capacity(CapacityError),
200            AddPeerError::InvalidPublicKey => MacBackendError::InvalidPublicKey,
201        })
202    }
203
204    async fn remove_peer(&self, key: &PublicKey) -> bool {
205        self.remove_peer(key).await
206    }
207
208    async fn ensure_transient_peer(&self, key: &PublicKey) -> bool {
209        self.ensure_transient_peer(key).await
210    }
211
212    async fn add_private_channel(
213        &self,
214        key: ChannelKey,
215    ) -> Result<(), MacBackendError<Self::SendError, Self::CapacityError>> {
216        self.add_channel(key)
217            .await
218            .map_err(MacBackendError::Capacity)
219    }
220
221    async fn add_named_channel(
222        &self,
223        name: &str,
224    ) -> Result<(), MacBackendError<Self::SendError, Self::CapacityError>> {
225        self.add_named_channel(name).await.map_err(|err| match err {
226            umsh_mac::AddChannelError::Capacity => MacBackendError::Capacity(CapacityError),
227            umsh_mac::AddChannelError::InvalidName(reason) => {
228                MacBackendError::InvalidChannelName(reason)
229            }
230        })
231    }
232
233    async fn remove_channel(&self, key: &ChannelKey) -> bool {
234        self.remove_channel(key).await
235    }
236
237    async fn send_broadcast(
238        &self,
239        from: LocalIdentityId,
240        payload: &[u8],
241        options: &SendOptions,
242    ) -> Result<SendReceipt, MacBackendError<Self::SendError, Self::CapacityError>> {
243        self.send_broadcast(from, payload, options)
244            .await
245            .map_err(MacBackendError::Send)
246    }
247
248    async fn send_multicast(
249        &self,
250        from: LocalIdentityId,
251        channel: &ChannelId,
252        payload: &[u8],
253        options: &SendOptions,
254    ) -> Result<SendReceipt, MacBackendError<Self::SendError, Self::CapacityError>> {
255        self.send_multicast(from, channel, payload, options)
256            .await
257            .map_err(MacBackendError::Send)
258    }
259
260    async fn send_unicast(
261        &self,
262        from: LocalIdentityId,
263        dst: &PublicKey,
264        payload: &[u8],
265        options: &SendOptions,
266    ) -> Result<Option<SendReceipt>, MacBackendError<Self::SendError, Self::CapacityError>> {
267        self.send_unicast(from, dst, payload, options)
268            .await
269            .map_err(MacBackendError::Send)
270    }
271
272    async fn send_blind_unicast(
273        &self,
274        from: LocalIdentityId,
275        dst: &PublicKey,
276        channel: &ChannelId,
277        payload: &[u8],
278        options: &SendOptions,
279    ) -> Result<Option<SendReceipt>, MacBackendError<Self::SendError, Self::CapacityError>> {
280        self.send_blind_unicast(from, dst, channel, payload, options)
281            .await
282            .map_err(MacBackendError::Send)
283    }
284
285    async fn fill_random(&self, dest: &mut [u8]) {
286        self.fill_random(dest).await
287    }
288
289    async fn now_ms(&self) -> u64 {
290        self.now_ms().await
291    }
292
293    #[cfg(feature = "software-crypto")]
294    async fn register_ephemeral(
295        &self,
296        parent: LocalIdentityId,
297        identity: umsh_crypto::software::SoftwareIdentity,
298    ) -> Result<LocalIdentityId, MacBackendError<Self::SendError, Self::CapacityError>> {
299        self.register_ephemeral(parent, identity)
300            .await
301            .map_err(MacBackendError::Capacity)
302    }
303
304    #[cfg(feature = "software-crypto")]
305    async fn remove_ephemeral(&self, id: LocalIdentityId) -> bool {
306        self.remove_ephemeral(id).await
307    }
308
309    async fn frame_counter(&self, from: LocalIdentityId) -> Option<u32> {
310        self.frame_counter(from).await
311    }
312
313    async fn persisted_frame_counter(&self, from: LocalIdentityId) -> Option<u32> {
314        self.persisted_frame_counter(from).await
315    }
316
317    async fn for_each_peer(&self, f: &mut dyn FnMut(umsh_core::PublicKey)) {
318        self.for_each_peer(f).await
319    }
320
321    async fn for_each_peer_counter(
322        &self,
323        from: LocalIdentityId,
324        f: &mut dyn FnMut(umsh_core::PublicKey, u32, u32),
325    ) {
326        self.for_each_peer_counter(from, f).await
327    }
328
329    async fn peer_route(&self, peer: &PublicKey) -> Option<CachedRoute> {
330        self.peer_route(peer).await
331    }
332
333    async fn clear_peer_route(&self, peer: &PublicKey) -> bool {
334        self.clear_peer_route(peer).await
335    }
336}