decdn_node/dht/
dial_addrs.rs1use std::collections::HashMap;
18use std::net::SocketAddr;
19use std::sync::{Arc, RwLock};
20
21use iroh::address_lookup::MemoryLookup;
22use iroh::{EndpointAddr, PublicKey};
23use tracing::debug;
24
25use crate::dht::chain_projection::{with_read, with_write};
26use crate::dht::routing::NodeId;
27
28const PROVENANCE: &str = "capacity_bond_registry";
31
32const LABEL: &str = "chain dial-address directory";
34
35#[derive(Debug, Clone)]
38pub struct DialAddrDirectory {
39 addrs: Arc<RwLock<HashMap<NodeId, Vec<SocketAddr>>>>,
40 lookup: MemoryLookup,
41}
42
43impl Default for DialAddrDirectory {
44 fn default() -> Self {
45 Self {
46 addrs: Arc::default(),
47 lookup: MemoryLookup::with_provenance(PROVENANCE),
48 }
49 }
50}
51
52impl DialAddrDirectory {
53 #[must_use]
56 pub fn lookup(&self) -> MemoryLookup {
57 self.lookup.clone()
58 }
59
60 #[must_use]
62 pub fn get(&self, node: &NodeId) -> Option<Vec<SocketAddr>> {
63 with_read(&self.addrs, LABEL, |m| m.get(node).cloned())
64 }
65
66 pub(crate) fn set(&self, node: NodeId, addrs: Vec<SocketAddr>) {
68 with_write(&self.addrs, LABEL, |m| {
69 if addrs.is_empty() {
70 m.remove(&node);
71 self.lookup_remove(&node);
72 } else {
73 self.lookup_set(&node, &addrs);
74 m.insert(node, addrs);
75 }
76 });
77 }
78
79 pub(crate) fn remove(&self, node: &NodeId) {
81 with_write(&self.addrs, LABEL, |m| {
82 m.remove(node);
83 self.lookup_remove(node);
84 });
85 }
86
87 pub(crate) fn replace_all(&self, mut next: HashMap<NodeId, Vec<SocketAddr>>) {
90 next.retain(|_, addrs| !addrs.is_empty());
91 with_write(&self.addrs, LABEL, |m| {
92 for gone in m.keys().filter(|id| !next.contains_key(id)) {
93 self.lookup_remove(gone);
94 }
95 for (id, addrs) in &next {
96 self.lookup_set(id, addrs);
97 }
98 *m = next;
99 });
100 }
101
102 fn lookup_set(&self, node: &NodeId, addrs: &[SocketAddr]) {
103 let Some(pk) = public_key(node) else {
104 return;
105 };
106 let target = addrs
107 .iter()
108 .fold(EndpointAddr::new(pk), |t, sock| t.with_ip_addr(*sock));
109 self.lookup.set_endpoint_info(target);
110 }
111
112 fn lookup_remove(&self, node: &NodeId) {
113 if let Some(pk) = public_key(node) {
114 self.lookup.remove_endpoint_info(pk);
115 }
116 }
117}
118
119fn public_key(node: &NodeId) -> Option<PublicKey> {
122 PublicKey::from_bytes(node.as_bytes())
123 .inspect_err(|e| debug!(%node, error = %e, "registry node id is not a valid public key"))
124 .ok()
125}
126
127#[cfg(test)]
128#[allow(clippy::unwrap_used, clippy::expect_used)]
129mod tests;