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
25#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct PeerInfo {
28 pub public_key: PublicKey,
30 pub pinned: bool,
32 pub route: Option<CachedRoute>,
34 pub last_seen_ms: u64,
36 pub initial_rx_counter: u32,
42}
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub struct AutoPeerUpdate {
47 pub peer_id: PeerId,
49 pub evicted_key: Option<PublicKey>,
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55pub struct PeerRemoval {
56 pub removed_key: PublicKey,
58 pub moved: Option<(PeerId, PeerId)>,
62}
63
64#[derive(Clone, Debug)]
66pub struct PeerRegistry<const N: usize> {
67 peers: Vec<PeerInfo, N>,
68}
69
70impl<const N: usize> Default for PeerRegistry<N> {
71 fn default() -> Self {
72 Self::new()
73 }
74}
75
76impl<const N: usize> PeerRegistry<N> {
77 pub fn new() -> Self {
79 Self { peers: Vec::new() }
80 }
81
82 pub fn lookup_by_hint(&self, hint: &NodeHint) -> impl Iterator<Item = (PeerId, &PeerInfo)> {
84 self.peers
85 .iter()
86 .enumerate()
87 .filter(move |(_, peer)| peer.public_key.hint() == *hint)
88 .map(|(index, peer)| (PeerId(index as u8), peer))
89 }
90
91 pub fn lookup_by_key(&self, key: &PublicKey) -> Option<(PeerId, &PeerInfo)> {
93 self.peers
94 .iter()
95 .enumerate()
96 .find(|(_, peer)| peer.public_key == *key)
97 .map(|(index, peer)| (PeerId(index as u8), peer))
98 }
99
100 pub fn iter(&self) -> impl Iterator<Item = (PeerId, &PeerInfo)> {
102 self.peers
103 .iter()
104 .enumerate()
105 .map(|(index, peer)| (PeerId(index as u8), peer))
106 }
107
108 pub fn get(&self, id: PeerId) -> Option<&PeerInfo> {
110 self.peers.get(id.0 as usize)
111 }
112
113 pub fn get_mut(&mut self, id: PeerId) -> Option<&mut PeerInfo> {
115 self.peers.get_mut(id.0 as usize)
116 }
117
118 pub fn try_insert_or_update(&mut self, key: PublicKey) -> Result<PeerId, CapacityError> {
120 if let Some((id, peer)) = self
121 .peers
122 .iter_mut()
123 .enumerate()
124 .find(|(_, peer)| peer.public_key == key)
125 {
126 peer.public_key = key;
127 peer.pinned = true;
128 return Ok(PeerId(id as u8));
129 }
130
131 self.peers
132 .push(PeerInfo {
133 public_key: key,
134 pinned: true,
135 route: None,
136 last_seen_ms: 0,
137 initial_rx_counter: 0,
138 })
139 .map_err(|_| CapacityError)?;
140 Ok(PeerId((self.peers.len() - 1) as u8))
141 }
142
143 pub fn try_insert_or_update_auto(
148 &mut self,
149 key: PublicKey,
150 now_ms: u64,
151 ) -> Result<AutoPeerUpdate, CapacityError> {
152 if let Some((id, peer)) = self
153 .peers
154 .iter_mut()
155 .enumerate()
156 .find(|(_, peer)| peer.public_key == key)
157 {
158 peer.last_seen_ms = now_ms;
159 return Ok(AutoPeerUpdate {
160 peer_id: PeerId(id as u8),
161 evicted_key: None,
162 });
163 }
164
165 if self.peers.len() < N {
166 self.peers
167 .push(PeerInfo {
168 public_key: key,
169 pinned: false,
170 route: None,
171 last_seen_ms: now_ms,
172 initial_rx_counter: 0,
173 })
174 .map_err(|_| CapacityError)?;
175 return Ok(AutoPeerUpdate {
176 peer_id: PeerId((self.peers.len() - 1) as u8),
177 evicted_key: None,
178 });
179 }
180
181 let Some((index, oldest)) = self
182 .peers
183 .iter()
184 .enumerate()
185 .filter(|(_, peer)| !peer.pinned)
186 .min_by_key(|(_, peer)| peer.last_seen_ms)
187 else {
188 return Err(CapacityError);
189 };
190
191 let evicted_key = oldest.public_key;
192 self.peers[index] = PeerInfo {
193 public_key: key,
194 pinned: false,
195 route: None,
196 last_seen_ms: now_ms,
197 initial_rx_counter: 0,
198 };
199 Ok(AutoPeerUpdate {
200 peer_id: PeerId(index as u8),
201 evicted_key: Some(evicted_key),
202 })
203 }
204
205 pub fn remove(&mut self, id: PeerId) -> Option<PeerRemoval> {
212 let index = id.0 as usize;
213 if index >= self.peers.len() {
214 return None;
215 }
216 let last = self.peers.len() - 1;
217 let removed = self.peers.swap_remove(index);
218 let moved = (index != last).then_some((PeerId(last as u8), id));
219 Some(PeerRemoval {
220 removed_key: removed.public_key,
221 moved,
222 })
223 }
224
225 pub fn update_route(&mut self, id: PeerId, route: CachedRoute) {
227 if let Some(peer) = self.get_mut(id) {
228 peer.route = Some(route);
229 }
230 }
231
232 pub fn clear_route(&mut self, id: PeerId) -> bool {
237 self.get_mut(id)
238 .map(|peer| peer.route.take().is_some())
239 .unwrap_or(false)
240 }
241
242 pub fn touch(&mut self, id: PeerId, now_ms: u64) {
244 if let Some(peer) = self.get_mut(id) {
245 peer.last_seen_ms = now_ms;
246 }
247 }
248}
249
250#[derive(Clone)]
252pub struct PeerCryptoState {
253 pub pairwise_keys: PairwiseKeys,
255 pub replay_window: ReplayWindow,
257 pub persisted_rx_counter: u32,
260 pub needs_rx_persist: bool,
263}
264
265#[derive(Clone)]
267pub struct PeerCryptoMap<const N: usize> {
268 entries: LinearMap<PeerId, PeerCryptoState, N>,
269}
270
271impl<const N: usize> Default for PeerCryptoMap<N> {
272 fn default() -> Self {
273 Self::new()
274 }
275}
276
277impl<const N: usize> PeerCryptoMap<N> {
278 pub fn new() -> Self {
280 Self {
281 entries: LinearMap::new(),
282 }
283 }
284
285 pub fn get(&self, id: &PeerId) -> Option<&PeerCryptoState> {
287 self.entries.get(id)
288 }
289
290 pub fn get_mut(&mut self, id: &PeerId) -> Option<&mut PeerCryptoState> {
292 self.entries.get_mut(id)
293 }
294
295 pub fn insert(
297 &mut self,
298 id: PeerId,
299 state: PeerCryptoState,
300 ) -> Result<Option<PeerCryptoState>, CapacityError> {
301 self.entries.insert(id, state).map_err(|_| CapacityError)
302 }
303
304 pub fn remove(&mut self, id: &PeerId) -> Option<PeerCryptoState> {
306 self.entries.remove(id)
307 }
308
309 pub fn iter(&self) -> impl Iterator<Item = (&PeerId, &PeerCryptoState)> {
311 self.entries.iter()
312 }
313}
314
315#[derive(Clone)]
317pub struct HintReplayState {
318 pub window: ReplayWindow,
320 pub last_seen_ms: u64,
322}
323
324#[derive(Clone)]
326pub struct ChannelState<const RN: usize = 8, const HN: usize = 8> {
327 pub channel_key: ChannelKey,
329 pub derived: DerivedChannelKeys,
331 pub replay: LinearMap<PeerId, ReplayWindow, RN>,
333 pub hint_replay: LinearMap<NodeHint, HintReplayState, HN>,
335}
336
337impl<const RN: usize, const HN: usize> ChannelState<RN, HN> {
338 pub fn new(channel_key: ChannelKey, derived: DerivedChannelKeys) -> Self {
340 Self {
341 channel_key,
342 derived,
343 replay: LinearMap::new(),
344 hint_replay: LinearMap::new(),
345 }
346 }
347}
348
349#[derive(Clone)]
351pub struct ChannelTable<const N: usize, const RN: usize = 8, const HN: usize = 8> {
352 channels: Vec<ChannelState<RN, HN>, N>,
353}
354
355impl<const N: usize, const RN: usize, const HN: usize> Default for ChannelTable<N, RN, HN> {
356 fn default() -> Self {
357 Self::new()
358 }
359}
360
361impl<const N: usize, const RN: usize, const HN: usize> ChannelTable<N, RN, HN> {
362 pub fn new() -> Self {
364 Self {
365 channels: Vec::new(),
366 }
367 }
368
369 pub fn len(&self) -> usize {
371 self.channels.len()
372 }
373
374 pub fn is_empty(&self) -> bool {
376 self.channels.is_empty()
377 }
378
379 pub fn lookup_by_id(&self, id: &ChannelId) -> impl Iterator<Item = &ChannelState<RN, HN>> {
381 self.channels
382 .iter()
383 .filter(move |channel| channel.derived.channel_id == *id)
384 }
385
386 pub fn get_mut_by_id(&mut self, id: &ChannelId) -> Option<&mut ChannelState<RN, HN>> {
388 self.channels
389 .iter_mut()
390 .find(|channel| channel.derived.channel_id == *id)
391 }
392
393 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut ChannelState<RN, HN>> {
395 self.channels.iter_mut()
396 }
397
398 pub fn remove_by_key(&mut self, key: &ChannelKey) -> bool {
402 let Some(index) = self
403 .channels
404 .iter()
405 .position(|channel| channel.channel_key.0 == key.0)
406 else {
407 return false;
408 };
409 self.channels.swap_remove(index);
410 true
411 }
412
413 pub fn try_add(
415 &mut self,
416 key: ChannelKey,
417 derived: DerivedChannelKeys,
418 ) -> Result<(), CapacityError> {
419 if let Some(channel) = self.get_mut_by_id(&derived.channel_id) {
420 channel.channel_key = key;
421 channel.derived = derived;
422 return Ok(());
423 }
424
425 self.channels
426 .push(ChannelState::new(key, derived))
427 .map_err(|_| CapacityError)
428 }
429}