1use heapless::{LinearMap, Vec};
2use umsh_core::{ChannelId, ChannelKey, NodeHint, PublicKey, RouterHint};
3use umsh_crypto::{DerivedChannelKeys, PairwiseKeys};
4
5use crate::{CapacityError, cache::ReplayWindow};
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
9pub struct PeerId(pub u8);
10
11#[derive(Clone, Debug, PartialEq, Eq)]
13pub enum CachedRoute {
14 Direct,
19 Source(Vec<RouterHint, 15>),
21 Flood { hops: u8, regions: Vec<[u8; 2], 8> },
23}
24
25impl CachedRoute {
26 pub const MAX_HINTS: usize = 15;
28 pub const MAX_REGIONS: usize = 8;
30
31 pub fn source(hints: &[RouterHint]) -> Option<Self> {
38 Vec::from_slice(hints).ok().map(Self::Source)
39 }
40
41 pub fn flood(hops: u8, regions: &[[u8; 2]]) -> Option<Self> {
44 Vec::from_slice(regions)
45 .ok()
46 .map(|regions| Self::Flood { hops, regions })
47 }
48}
49
50#[derive(Clone, Debug, PartialEq, Eq)]
52pub struct PeerInfo {
53 pub public_key: PublicKey,
55 pub pinned: bool,
57 pub route: Option<CachedRoute>,
59 pub last_seen_ms: u64,
61 pub initial_rx_counter: u32,
67}
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub struct AutoPeerUpdate {
72 pub peer_id: PeerId,
74 pub evicted_key: Option<PublicKey>,
76}
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq)]
80pub struct PeerRemoval {
81 pub removed_key: PublicKey,
83 pub moved: Option<(PeerId, PeerId)>,
87}
88
89#[derive(Clone, Debug)]
91pub struct PeerRegistry<const N: usize> {
92 peers: Vec<PeerInfo, N>,
93}
94
95impl<const N: usize> Default for PeerRegistry<N> {
96 fn default() -> Self {
97 Self::new()
98 }
99}
100
101impl<const N: usize> PeerRegistry<N> {
102 pub fn new() -> Self {
104 Self { peers: Vec::new() }
105 }
106
107 pub fn lookup_by_hint(&self, hint: &NodeHint) -> impl Iterator<Item = (PeerId, &PeerInfo)> {
109 self.peers
110 .iter()
111 .enumerate()
112 .filter(move |(_, peer)| peer.public_key.hint() == *hint)
113 .map(|(index, peer)| (PeerId(index as u8), peer))
114 }
115
116 pub fn lookup_by_key(&self, key: &PublicKey) -> Option<(PeerId, &PeerInfo)> {
118 self.peers
119 .iter()
120 .enumerate()
121 .find(|(_, peer)| peer.public_key == *key)
122 .map(|(index, peer)| (PeerId(index as u8), peer))
123 }
124
125 pub fn iter(&self) -> impl Iterator<Item = (PeerId, &PeerInfo)> {
127 self.peers
128 .iter()
129 .enumerate()
130 .map(|(index, peer)| (PeerId(index as u8), peer))
131 }
132
133 pub fn get(&self, id: PeerId) -> Option<&PeerInfo> {
135 self.peers.get(id.0 as usize)
136 }
137
138 pub fn get_mut(&mut self, id: PeerId) -> Option<&mut PeerInfo> {
140 self.peers.get_mut(id.0 as usize)
141 }
142
143 pub fn try_insert_or_update(&mut self, key: PublicKey) -> Result<PeerId, CapacityError> {
145 if let Some((id, peer)) = self
146 .peers
147 .iter_mut()
148 .enumerate()
149 .find(|(_, peer)| peer.public_key == key)
150 {
151 peer.public_key = key;
152 peer.pinned = true;
153 return Ok(PeerId(id as u8));
154 }
155
156 self.peers
157 .push(PeerInfo {
158 public_key: key,
159 pinned: true,
160 route: None,
161 last_seen_ms: 0,
162 initial_rx_counter: 0,
163 })
164 .map_err(|_| CapacityError)?;
165 Ok(PeerId((self.peers.len() - 1) as u8))
166 }
167
168 pub fn try_insert_or_update_auto(
173 &mut self,
174 key: PublicKey,
175 now_ms: u64,
176 ) -> Result<AutoPeerUpdate, CapacityError> {
177 if let Some((id, peer)) = self
178 .peers
179 .iter_mut()
180 .enumerate()
181 .find(|(_, peer)| peer.public_key == key)
182 {
183 peer.last_seen_ms = now_ms;
184 return Ok(AutoPeerUpdate {
185 peer_id: PeerId(id as u8),
186 evicted_key: None,
187 });
188 }
189
190 if self.peers.len() < N {
191 self.peers
192 .push(PeerInfo {
193 public_key: key,
194 pinned: false,
195 route: None,
196 last_seen_ms: now_ms,
197 initial_rx_counter: 0,
198 })
199 .map_err(|_| CapacityError)?;
200 return Ok(AutoPeerUpdate {
201 peer_id: PeerId((self.peers.len() - 1) as u8),
202 evicted_key: None,
203 });
204 }
205
206 let Some((index, oldest)) = self
207 .peers
208 .iter()
209 .enumerate()
210 .filter(|(_, peer)| !peer.pinned)
211 .min_by_key(|(_, peer)| peer.last_seen_ms)
212 else {
213 return Err(CapacityError);
214 };
215
216 let evicted_key = oldest.public_key;
217 self.peers[index] = PeerInfo {
218 public_key: key,
219 pinned: false,
220 route: None,
221 last_seen_ms: now_ms,
222 initial_rx_counter: 0,
223 };
224 Ok(AutoPeerUpdate {
225 peer_id: PeerId(index as u8),
226 evicted_key: Some(evicted_key),
227 })
228 }
229
230 pub fn remove(&mut self, id: PeerId) -> Option<PeerRemoval> {
237 let index = id.0 as usize;
238 if index >= self.peers.len() {
239 return None;
240 }
241 let last = self.peers.len() - 1;
242 let removed = self.peers.swap_remove(index);
243 let moved = (index != last).then_some((PeerId(last as u8), id));
244 Some(PeerRemoval {
245 removed_key: removed.public_key,
246 moved,
247 })
248 }
249
250 pub fn update_route(&mut self, id: PeerId, route: CachedRoute) {
252 if let Some(peer) = self.get_mut(id) {
253 peer.route = Some(route);
254 }
255 }
256
257 pub fn clear_route(&mut self, id: PeerId) -> bool {
262 self.get_mut(id)
263 .map(|peer| peer.route.take().is_some())
264 .unwrap_or(false)
265 }
266
267 pub fn touch(&mut self, id: PeerId, now_ms: u64) {
269 if let Some(peer) = self.get_mut(id) {
270 peer.last_seen_ms = now_ms;
271 }
272 }
273}
274
275#[derive(Clone)]
277pub struct PeerCryptoState {
278 pub pairwise_keys: PairwiseKeys,
280 pub replay_window: ReplayWindow,
282 pub persisted_rx_counter: u32,
285 pub needs_rx_persist: bool,
288}
289
290#[derive(Clone)]
292pub struct PeerCryptoMap<const N: usize> {
293 entries: LinearMap<PeerId, PeerCryptoState, N>,
294}
295
296impl<const N: usize> Default for PeerCryptoMap<N> {
297 fn default() -> Self {
298 Self::new()
299 }
300}
301
302impl<const N: usize> PeerCryptoMap<N> {
303 pub fn new() -> Self {
305 Self {
306 entries: LinearMap::new(),
307 }
308 }
309
310 pub fn get(&self, id: &PeerId) -> Option<&PeerCryptoState> {
312 self.entries.get(id)
313 }
314
315 pub fn get_mut(&mut self, id: &PeerId) -> Option<&mut PeerCryptoState> {
317 self.entries.get_mut(id)
318 }
319
320 pub fn insert(
322 &mut self,
323 id: PeerId,
324 state: PeerCryptoState,
325 ) -> Result<Option<PeerCryptoState>, CapacityError> {
326 self.entries.insert(id, state).map_err(|_| CapacityError)
327 }
328
329 pub fn remove(&mut self, id: &PeerId) -> Option<PeerCryptoState> {
331 self.entries.remove(id)
332 }
333
334 pub fn iter(&self) -> impl Iterator<Item = (&PeerId, &PeerCryptoState)> {
336 self.entries.iter()
337 }
338
339 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&PeerId, &mut PeerCryptoState)> {
341 self.entries.iter_mut()
342 }
343}
344
345#[derive(Clone)]
347pub struct HintReplayState {
348 pub window: ReplayWindow,
350 pub last_seen_ms: u64,
352}
353
354#[derive(Clone)]
356pub struct ChannelState<const RN: usize = 8, const HN: usize = 8> {
357 pub channel_key: ChannelKey,
359 pub derived: DerivedChannelKeys,
361 pub replay: LinearMap<PeerId, ReplayWindow, RN>,
363 pub hint_replay: LinearMap<NodeHint, HintReplayState, HN>,
365}
366
367impl<const RN: usize, const HN: usize> ChannelState<RN, HN> {
368 pub fn new(channel_key: ChannelKey, derived: DerivedChannelKeys) -> Self {
370 Self {
371 channel_key,
372 derived,
373 replay: LinearMap::new(),
374 hint_replay: LinearMap::new(),
375 }
376 }
377}
378
379#[derive(Clone)]
381pub struct ChannelTable<const N: usize, const RN: usize = 8, const HN: usize = 8> {
382 channels: Vec<ChannelState<RN, HN>, N>,
383}
384
385impl<const N: usize, const RN: usize, const HN: usize> Default for ChannelTable<N, RN, HN> {
386 fn default() -> Self {
387 Self::new()
388 }
389}
390
391impl<const N: usize, const RN: usize, const HN: usize> ChannelTable<N, RN, HN> {
392 pub fn new() -> Self {
394 Self {
395 channels: Vec::new(),
396 }
397 }
398
399 pub fn len(&self) -> usize {
401 self.channels.len()
402 }
403
404 pub fn is_empty(&self) -> bool {
406 self.channels.is_empty()
407 }
408
409 pub fn lookup_by_id(&self, id: &ChannelId) -> impl Iterator<Item = &ChannelState<RN, HN>> {
411 self.channels
412 .iter()
413 .filter(move |channel| channel.derived.channel_id == *id)
414 }
415
416 pub fn get_mut_by_id(&mut self, id: &ChannelId) -> Option<&mut ChannelState<RN, HN>> {
418 self.channels
419 .iter_mut()
420 .find(|channel| channel.derived.channel_id == *id)
421 }
422
423 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut ChannelState<RN, HN>> {
425 self.channels.iter_mut()
426 }
427
428 pub fn remove_by_key(&mut self, key: &ChannelKey) -> bool {
432 let Some(index) = self
433 .channels
434 .iter()
435 .position(|channel| channel.channel_key.0 == key.0)
436 else {
437 return false;
438 };
439 self.channels.swap_remove(index);
440 true
441 }
442
443 pub fn try_add(
445 &mut self,
446 key: ChannelKey,
447 derived: DerivedChannelKeys,
448 ) -> Result<(), CapacityError> {
449 if let Some(channel) = self.get_mut_by_id(&derived.channel_id) {
450 channel.channel_key = key;
451 channel.derived = derived;
452 return Ok(());
453 }
454
455 self.channels
456 .push(ChannelState::new(key, derived))
457 .map_err(|_| CapacityError)
458 }
459}