Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_o3db_sync/src/kademlia/contact.rs

2.7 KiB, 23 runs

created by r1870400018:11186, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! Peer contact records stored in the routing table.
2//!
3//! A [`Contact`] is the minimum information needed to route to a peer: its
4//! identifier, one or more network addresses, a liveness timestamp, a smoothed
5//! round-trip time, and an opaque capability bitfield. Public keys and key
6//! rotation live one layer up -- in fe2o3_crypto and the distributed Ozone
7//! layer on top of this crate -- because routing itself does not authenticate
8//! anything.
9//!
10//! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\
11//! Anthropic Claude
12
13use super::id::NodeId;
14
15use std::{
16 net::SocketAddr,
17 time::Duration,
18};
19
20
21/// Opaque peer capability flags.
22///
23/// Concrete bit assignments are made by the layer above (distributed Ozone,
24/// Oxegen peer type). This crate treats capabilities as an opaque bitfield
25/// with set, test and clear operations.
26#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
27pub struct Capabilities(pub u64);
28
29impl Capabilities {
30 pub const NONE: Self = Self(0);
31
32 /// Are all of the mask's bits set?
33 pub fn has(&self, mask: Capabilities) -> bool {
34 self.0 & mask.0 == mask.0
35 }
36
37 pub fn set(&mut self, mask: Capabilities) {
38 self.0 |= mask.0;
39 }
40
41 pub fn clear(&mut self, mask: Capabilities) {
42 self.0 &= !mask.0;
43 }
44}
45
46
47/// A routing-table entry for a single peer.
48///
49/// Cheap to clone -- `addresses` is a short `Vec` and the other fields are
50/// `Copy`. The routing table keeps `Contact`s directly rather than behind an
51/// `Arc` because they are small and mutated only through the table's own API.
52#[derive(Clone, Debug)]
53pub struct Contact {
54 pub node_id: NodeId,
55 pub addresses: Vec<SocketAddr>, // the first is the primary
56 pub last_seen: u64, // caller's own epoch, opaque here
57 pub rtt: Option<Duration>, // None until a probe succeeds
58 pub capabilities: Capabilities,
59}
60
61impl Contact {
62 /// `last_seen` starts at 0, with no RTT sample and no capabilities. The
63 /// caller is expected to set `last_seen` at once, through
64 /// [`Contact::touch`] or the routing table's insert path.
65 pub fn new(node_id: NodeId, addresses: Vec<SocketAddr>) -> Self {
66 Self {
67 node_id,
68 addresses,
69 last_seen: 0,
70 rtt: None,
71 capabilities: Capabilities::NONE,
72 }
73 }
74
75 pub fn touch(&mut self, now: u64) {
76 self.last_seen = now;
77 }
78
79 /// An exponentially weighted moving average with α = 1/8, the
80 /// TCP Jacobson smoothing constant. The first sample becomes the initial
81 /// estimate unchanged.
82 pub fn record_rtt(&mut self, sample: Duration) {
83 self.rtt = Some(match self.rtt {
84 None => sample,
85 Some(prev) => {
86 let prev_ns = prev.as_nanos() as u64;
87 let samp_ns = sample.as_nanos() as u64;
88 let next_ns = prev_ns - (prev_ns / 8) + (samp_ns / 8);
89 Duration::from_nanos(next_ns)
90 },
91 });
92 }
93}