1use std::collections::HashMap;
4use std::fmt;
5use std::time::Duration;
6
7use super::{CoordEntry, ParentDeclaration, TreeCoordinate, TreeError};
8use crate::time::{Instant, instant_now};
9use crate::{Identity, NodeAddr};
10
11pub struct TreeState {
17 my_node_addr: NodeAddr,
19 my_declaration: ParentDeclaration,
21 pub(super) my_coords: TreeCoordinate,
23 pub(super) root: NodeAddr,
25 peer_declarations: HashMap<NodeAddr, ParentDeclaration>,
27 peer_ancestry: HashMap<NodeAddr, TreeCoordinate>,
29 parent_hysteresis: f64,
31 hold_down: Duration,
33 last_parent_switch: Option<Instant>,
35 flap_count: u32,
37 flap_window_start: Option<Instant>,
39 flap_dampening_until: Option<Instant>,
41 flap_threshold: u32,
43 flap_window: Duration,
45 flap_dampening_duration: Duration,
47}
48
49impl TreeState {
50 pub fn new(my_node_addr: NodeAddr) -> Self {
55 let timestamp = crate::time::now_secs();
56 let my_declaration = ParentDeclaration::self_root(my_node_addr, 1, timestamp);
57 let my_coords = TreeCoordinate::root_with_meta(my_node_addr, 1, timestamp);
58
59 Self {
60 my_node_addr,
61 my_declaration,
62 my_coords,
63 root: my_node_addr,
64 peer_declarations: HashMap::new(),
65 peer_ancestry: HashMap::new(),
66 parent_hysteresis: 0.0,
67 hold_down: Duration::ZERO,
68 last_parent_switch: None,
69 flap_count: 0,
70 flap_window_start: None,
71 flap_dampening_until: None,
72 flap_threshold: 4,
73 flap_window: Duration::from_secs(60),
74 flap_dampening_duration: Duration::from_secs(120),
75 }
76 }
77
78 pub fn my_node_addr(&self) -> &NodeAddr {
80 &self.my_node_addr
81 }
82
83 pub fn my_declaration(&self) -> &ParentDeclaration {
85 &self.my_declaration
86 }
87
88 pub fn my_coords(&self) -> &TreeCoordinate {
90 &self.my_coords
91 }
92
93 pub fn root(&self) -> &NodeAddr {
95 &self.root
96 }
97
98 pub fn is_root(&self) -> bool {
100 self.root == self.my_node_addr
101 }
102
103 pub fn peer_coords(&self, peer_id: &NodeAddr) -> Option<&TreeCoordinate> {
105 self.peer_ancestry.get(peer_id)
106 }
107
108 pub fn peer_declaration(&self, peer_id: &NodeAddr) -> Option<&ParentDeclaration> {
110 self.peer_declarations.get(peer_id)
111 }
112
113 pub fn peer_count(&self) -> usize {
115 self.peer_declarations.len()
116 }
117
118 pub fn peer_ids(&self) -> impl Iterator<Item = &NodeAddr> {
120 self.peer_declarations.keys()
121 }
122
123 pub fn update_peer(
127 &mut self,
128 declaration: ParentDeclaration,
129 ancestry: TreeCoordinate,
130 ) -> bool {
131 let peer_id = *declaration.node_addr();
132
133 if let Some(existing) = self.peer_declarations.get(&peer_id)
135 && !declaration.is_fresher_than(existing)
136 {
137 return false;
138 }
139
140 self.peer_declarations.insert(peer_id, declaration);
141 self.peer_ancestry.insert(peer_id, ancestry);
142 true
143 }
144
145 pub fn remove_peer(&mut self, peer_id: &NodeAddr) {
147 self.peer_declarations.remove(peer_id);
148 self.peer_ancestry.remove(peer_id);
149 }
150
151 pub fn set_parent(&mut self, parent_id: NodeAddr, sequence: u64, timestamp: u64) -> bool {
157 let parent_changed = self.is_root() || *self.my_declaration.parent_id() != parent_id;
158 self.my_declaration =
159 ParentDeclaration::new(self.my_node_addr, parent_id, sequence, timestamp);
160 self.last_parent_switch = Some(instant_now());
161 if parent_changed {
164 self.record_parent_switch()
165 } else {
166 false
167 }
168 }
169
170 pub fn recompute_coords(&mut self) {
178 if self.my_declaration.is_root() {
179 self.my_coords = TreeCoordinate::root_with_meta(
180 self.my_node_addr,
181 self.my_declaration.sequence(),
182 self.my_declaration.timestamp(),
183 );
184 self.root = self.my_node_addr;
185 return;
186 }
187
188 let parent_id = self.my_declaration.parent_id();
189 if let Some(parent_coords) = self.peer_ancestry.get(parent_id) {
190 let parent_root = *parent_coords.root_id();
191 if self.my_node_addr <= parent_root {
192 let seq = self.my_declaration.sequence();
196 let ts = self.my_declaration.timestamp();
197 self.my_declaration = ParentDeclaration::self_root(self.my_node_addr, seq, ts);
198 self.my_coords = TreeCoordinate::root_with_meta(self.my_node_addr, seq, ts);
199 self.root = self.my_node_addr;
200 return;
201 }
202 let self_entry = CoordEntry::new(
204 self.my_node_addr,
205 self.my_declaration.sequence(),
206 self.my_declaration.timestamp(),
207 );
208 let mut entries = vec![self_entry];
209 entries.extend_from_slice(parent_coords.entries());
210 self.my_coords = TreeCoordinate::new(entries).expect("non-empty path");
211 self.root = *self.my_coords.root_id();
212 }
213 }
214
215 pub fn smallest_visible_root(&self) -> Option<NodeAddr> {
217 self.peer_ancestry.values().map(|c| *c.root_id()).min()
218 }
219
220 pub fn should_be_root(&self) -> bool {
223 match self.smallest_visible_root() {
224 Some(sr) => self.my_node_addr <= sr,
225 None => true,
226 }
227 }
228
229 pub fn become_root(&mut self) {
233 let new_seq = self.my_declaration.sequence() + 1;
234 let timestamp = crate::time::now_secs();
235 self.my_declaration = ParentDeclaration::self_root(self.my_node_addr, new_seq, timestamp);
236 self.recompute_coords();
237 }
238
239 pub fn distance_to_peer(&self, peer_id: &NodeAddr) -> Option<usize> {
241 self.peer_ancestry
242 .get(peer_id)
243 .map(|coords| self.my_coords.distance_to(coords))
244 }
245
246 pub fn find_next_hop(&self, dest_coords: &TreeCoordinate) -> Option<NodeAddr> {
258 if self.my_coords.root_id() != dest_coords.root_id() {
259 return None;
260 }
261
262 let my_distance = self.my_coords.distance_to(dest_coords);
263
264 let mut best: Option<(NodeAddr, usize)> = None;
265
266 for (peer_id, peer_coords) in &self.peer_ancestry {
267 let distance = peer_coords.distance_to(dest_coords);
268
269 let dominated = match &best {
270 None => true,
271 Some((best_id, best_dist)) => {
272 distance < *best_dist || (distance == *best_dist && peer_id < best_id)
273 }
274 };
275
276 if dominated {
277 best = Some((*peer_id, distance));
278 }
279 }
280
281 match best {
282 Some((peer_id, distance)) if distance < my_distance => Some(peer_id),
283 _ => None,
284 }
285 }
286
287 pub fn set_parent_hysteresis(&mut self, hysteresis: f64) {
289 self.parent_hysteresis = hysteresis.clamp(0.0, 1.0);
290 }
291
292 pub fn set_hold_down(&mut self, secs: u64) {
294 self.hold_down = Duration::from_secs(secs);
295 }
296
297 pub fn set_flap_dampening(&mut self, threshold: u32, window_secs: u64, dampening_secs: u64) {
299 self.flap_threshold = threshold;
300 self.flap_window = Duration::from_secs(window_secs);
301 self.flap_dampening_duration = Duration::from_secs(dampening_secs);
302 }
303
304 pub fn record_parent_switch(&mut self) -> bool {
307 let now = instant_now();
308
309 match self.flap_window_start {
311 Some(start) if now.duration_since(start) < self.flap_window => {
312 self.flap_count += 1;
313 }
314 _ => {
315 self.flap_window_start = Some(now);
316 self.flap_count = 1;
317 }
318 }
319
320 if self.flap_count >= self.flap_threshold && self.flap_dampening_until.is_none() {
322 self.flap_dampening_until = Some(now + self.flap_dampening_duration);
323 return true;
324 }
325 false
326 }
327
328 pub fn is_flap_dampened(&self) -> bool {
330 match self.flap_dampening_until {
331 Some(until) => instant_now() < until,
332 None => false,
333 }
334 }
335
336 pub fn evaluate_parent(&self, peer_costs: &HashMap<NodeAddr, f64>) -> Option<NodeAddr> {
345 if self.peer_ancestry.is_empty() {
346 return None;
347 }
348
349 let mut smallest_root: Option<NodeAddr> = None;
351 for coords in self.peer_ancestry.values() {
352 let peer_root = coords.root_id();
353 smallest_root = Some(match smallest_root {
354 None => *peer_root,
355 Some(current) => {
356 if *peer_root < current {
357 *peer_root
358 } else {
359 current
360 }
361 }
362 });
363 }
364
365 let smallest_root = smallest_root?;
366
367 if self.my_node_addr <= smallest_root {
375 return None;
376 }
377
378 let mut best_peer: Option<(NodeAddr, f64)> = None; for (peer_id, coords) in &self.peer_ancestry {
382 if *coords.root_id() != smallest_root {
383 continue;
384 }
385 if coords.contains(&self.my_node_addr) {
387 continue;
388 }
389 let cost = match peer_costs.get(peer_id) {
394 Some(&c) => c,
395 None if peer_costs.is_empty() => 1.0,
396 None => continue,
397 };
398 let eff_depth = coords.depth() as f64 + cost;
399 match &best_peer {
400 None => best_peer = Some((*peer_id, eff_depth)),
401 Some((best_id, best_eff)) => {
402 if eff_depth < *best_eff || (eff_depth == *best_eff && peer_id < best_id) {
403 best_peer = Some((*peer_id, eff_depth));
404 }
405 }
406 }
407 }
408
409 let (best_peer_id, best_eff_depth) = best_peer?;
410
411 if *self.my_declaration.parent_id() == best_peer_id && !self.is_root() {
413 return None;
414 }
415
416 if !self.is_root()
420 && !self
421 .peer_ancestry
422 .contains_key(self.my_declaration.parent_id())
423 {
424 return Some(best_peer_id);
425 }
426
427 if smallest_root < self.root || (self.is_root() && smallest_root < self.my_node_addr) {
429 return Some(best_peer_id);
430 }
431
432 if self.is_root() {
434 return Some(best_peer_id);
435 }
436
437 if !self.hold_down.is_zero()
440 && self
441 .last_parent_switch
442 .is_some_and(|last| last.elapsed() < self.hold_down)
443 {
444 return None;
445 }
446
447 if self.is_flap_dampened() {
450 return None;
451 }
452
453 let current_parent_cost = peer_costs
460 .get(self.my_declaration.parent_id())
461 .copied()
462 .unwrap_or(if peer_costs.is_empty() {
463 1.0
464 } else {
465 f64::INFINITY
466 });
467 let current_parent_coords = self.peer_ancestry.get(self.my_declaration.parent_id());
468 let current_parent_eff = match current_parent_coords {
469 Some(coords) => coords.depth() as f64 + current_parent_cost,
470 None => return Some(best_peer_id), };
472
473 if best_eff_depth < current_parent_eff * (1.0 - self.parent_hysteresis) {
475 return Some(best_peer_id);
476 }
477
478 None
479 }
480
481 pub fn handle_parent_lost(&mut self, peer_costs: &HashMap<NodeAddr, f64>) -> bool {
488 if let Some(new_parent) = self.evaluate_parent(peer_costs) {
490 let timestamp = crate::time::now_secs();
491 let new_seq = self.my_declaration.sequence() + 1;
492 self.set_parent(new_parent, new_seq, timestamp);
493 self.recompute_coords();
494 return true;
495 }
496
497 let timestamp = crate::time::now_secs();
499 let new_seq = self.my_declaration.sequence() + 1;
500 self.my_declaration = ParentDeclaration::self_root(self.my_node_addr, new_seq, timestamp);
501 self.recompute_coords();
502 true
503 }
504
505 pub fn sign_declaration(&mut self, identity: &Identity) -> Result<(), TreeError> {
509 self.my_declaration.sign(identity)
510 }
511
512 pub fn is_declaration_signed(&self) -> bool {
514 self.my_declaration.is_signed()
515 }
516}
517
518impl fmt::Debug for TreeState {
519 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
520 f.debug_struct("TreeState")
521 .field("my_node_addr", &self.my_node_addr)
522 .field("root", &self.root)
523 .field("is_root", &self.is_root())
524 .field("depth", &self.my_coords.depth())
525 .field("peers", &self.peer_count())
526 .finish()
527 }
528}