use crate::address::is_lan_ip;
use crate::dht_lookup::LookupNode;
use crate::{Key, MultiAddr, PeerId};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AddressType {
Relay,
Direct,
Unverified,
#[serde(alias = "NATted")]
Lan,
}
impl AddressType {
pub const fn priority(self) -> u8 {
match self {
Self::Relay => 0,
Self::Direct => 1,
Self::Unverified => 2,
Self::Lan => 3,
}
}
pub(crate) fn for_advertised_address(addr: &MultiAddr, advertised: Self) -> Self {
if addr.ip().is_some_and(is_lan_ip) {
Self::Lan
} else {
advertised
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DHTNode {
pub peer_id: PeerId,
pub addresses: Vec<MultiAddr>,
#[serde(default)]
pub address_types: Vec<AddressType>,
pub distance: Option<Vec<u8>>,
pub reliability: f64,
#[serde(skip)]
pub address_authority: Option<crate::signed_address::AddressAuthority>,
}
impl LookupNode for DHTNode {
fn lookup_peer_id(&self) -> [u8; 32] {
*self.peer_id.as_bytes()
}
}
#[derive(Debug, Clone)]
pub struct WitnessedCloseGroup {
pub target: Key,
pub k: usize,
pub initial_closest: Vec<DHTNode>,
pub responder_views: Vec<ResponderView>,
}
#[derive(Debug, Clone)]
pub struct ResponderView {
pub responder: PeerId,
pub closest: Vec<DHTNode>,
}
impl DHTNode {
#[must_use]
pub fn publisher_address_set_unix_ns(&self) -> Option<u64> {
let seq = dht_node_publish_seq(self);
(seq != 0).then_some(seq)
}
#[must_use]
pub fn typed_addresses_by_priority(&self) -> Vec<(MultiAddr, AddressType)> {
let mut typed = self.typed_addresses();
typed.sort_by_key(|pair| pair.1.priority());
typed
}
#[must_use]
pub fn address_and_type_labels_by_priority(&self) -> Vec<(MultiAddr, &'static str)> {
self.typed_addresses_by_priority()
.into_iter()
.map(|(address, kind)| {
let label = match kind {
AddressType::Relay => "relay",
AddressType::Direct => "direct",
AddressType::Unverified => "unverified",
AddressType::Lan => "lan",
};
(address, label)
})
.collect()
}
#[must_use]
pub fn address_type_labels_by_priority(&self) -> Vec<&'static str> {
self.address_and_type_labels_by_priority()
.into_iter()
.map(|(_, label)| label)
.collect()
}
pub fn typed_addresses(&self) -> Vec<(MultiAddr, AddressType)> {
self.addresses
.iter()
.enumerate()
.map(|(i, addr)| {
let advertised = self
.address_types
.get(i)
.copied()
.unwrap_or(AddressType::Unverified);
let ty = AddressType::for_advertised_address(addr, advertised);
(addr.clone(), ty)
})
.collect()
}
pub fn addresses_by_priority(&self) -> Vec<MultiAddr> {
self.typed_addresses_by_priority()
.into_iter()
.map(|(address, _)| address)
.collect()
}
pub fn merge_from(&mut self, other: DHTNode) {
if self.peer_id != other.peer_id {
return;
}
let current_seq = dht_node_publish_seq(self);
let other_seq = dht_node_publish_seq(&other);
if current_seq != 0 || other_seq != 0 {
self.merge_owner_views(&other);
return;
}
while self.address_types.len() < self.addresses.len() {
self.address_types.push(AddressType::Unverified);
}
for (i, addr) in self.addresses.iter().enumerate() {
self.address_types[i] =
AddressType::for_advertised_address(addr, self.address_types[i]);
}
for (addr, ty) in other.typed_addresses() {
if let Some(pos) = self.addresses.iter().position(|a| a == &addr) {
if ty.priority() < self.address_types[pos].priority() {
self.address_types[pos] = ty;
}
} else {
self.addresses.push(addr);
self.address_types.push(ty);
}
}
let mut pairs: Vec<(MultiAddr, AddressType)> = self
.addresses
.drain(..)
.zip(self.address_types.drain(..))
.collect();
pairs.sort_by_key(|(_, ty)| ty.priority());
for (addr, ty) in pairs {
self.addresses.push(addr);
self.address_types.push(ty);
}
if other.reliability > self.reliability {
self.reliability = other.reliability;
}
let publish_seq = dht_node_publish_seq(self).max(dht_node_publish_seq(&other));
if publish_seq != 0 {
self.distance = encode_publish_seq_distance(publish_seq);
}
}
fn merge_owner_views(&mut self, other: &Self) {
use crate::signed_address::AddressAuthority;
let publication_sequence = |node: &Self| {
node.address_authority
.as_ref()
.and_then(AddressAuthority::publication)
.map_or(0, |proof| proof.sequence())
};
let current_v2 = publication_sequence(self);
let other_v2 = publication_sequence(other);
let replace = if current_v2 != 0 || other_v2 != 0 {
other_v2 > current_v2
} else {
dht_node_publish_seq(other) > dht_node_publish_seq(self)
};
if replace {
*self = other.clone();
}
}
}
const PUBLISH_SEQ_DISTANCE_MARKER: &[u8; 8] = b"PUBSEQ01";
pub(crate) fn encode_publish_seq_distance(seq: u64) -> Option<Vec<u8>> {
if seq == 0 {
return None;
}
let mut encoded = Vec::with_capacity(PUBLISH_SEQ_DISTANCE_MARKER.len() + 8);
encoded.extend_from_slice(PUBLISH_SEQ_DISTANCE_MARKER);
encoded.extend_from_slice(&seq.to_be_bytes());
Some(encoded)
}
pub(crate) fn dht_node_publish_seq(node: &DHTNode) -> u64 {
node.address_authority
.as_ref()
.map_or(0, |authority| authority.sequence())
}
#[cfg(feature = "native")]
pub(crate) fn advertised_publish_seq(node: &DHTNode) -> u64 {
let Some(distance) = node.distance.as_deref() else {
return 0;
};
if distance.len() != PUBLISH_SEQ_DISTANCE_MARKER.len() + 8
|| &distance[..PUBLISH_SEQ_DISTANCE_MARKER.len()] != PUBLISH_SEQ_DISTANCE_MARKER
{
return 0;
}
let mut bytes = [0u8; 8];
bytes.copy_from_slice(&distance[PUBLISH_SEQ_DISTANCE_MARKER.len()..]);
u64::from_be_bytes(bytes)
}
#[cfg(feature = "native")]
pub type SerializableDHTNode = DHTNode;