initial import of main veilid core

This commit is contained in:
John Smith
2021-11-22 11:28:30 -05:00
parent c4cd54e020
commit 9e94a6a96f
218 changed files with 34880 additions and 1 deletions

View File

@@ -0,0 +1,142 @@
use super::*;
#[derive(Clone)]
pub struct Bucket {
routing_table: RoutingTable,
entries: BTreeMap<DHTKey, BucketEntry>,
newest_entry: Option<DHTKey>,
}
pub(super) type EntriesIterMut<'a> =
alloc::collections::btree_map::IterMut<'a, DHTKey, BucketEntry>;
pub(super) type EntriesIter<'a> = alloc::collections::btree_map::Iter<'a, DHTKey, BucketEntry>;
fn state_ordering(state: BucketEntryState) -> usize {
match state {
BucketEntryState::Dead => 0,
BucketEntryState::Unreliable => 1,
BucketEntryState::Reliable => 2,
}
}
impl Bucket {
pub fn new(routing_table: RoutingTable) -> Self {
Self {
routing_table: routing_table,
entries: BTreeMap::new(),
newest_entry: None,
}
}
pub(super) fn add_entry(&mut self, node_id: DHTKey) -> NodeRef {
info!("Node added: {}", node_id.encode());
// Add new entry
self.entries.insert(node_id, BucketEntry::new());
// This is now the newest bucket entry
self.newest_entry = Some(node_id);
// Get a node ref to return
let entry_ref = self.entries.get_mut(&node_id).unwrap();
NodeRef::new(self.routing_table.clone(), node_id, entry_ref)
}
pub(super) fn remove_entry(&mut self, node_id: &DHTKey) {
info!("Node removed: {}", node_id);
// Remove the entry
self.entries.remove(node_id);
// newest_entry is updated by kick_bucket()
}
pub(super) fn roll_transfers(&mut self, last_ts: u64, cur_ts: u64) {
// Called every ROLLING_TRANSFERS_INTERVAL_SECS
for entry in &mut self.entries {
entry.1.roll_transfers(last_ts, cur_ts);
}
}
pub(super) fn entry_mut(&mut self, key: &DHTKey) -> Option<&mut BucketEntry> {
self.entries.get_mut(key)
}
pub(super) fn entries(&self) -> EntriesIter {
self.entries.iter()
}
pub(super) fn entries_mut(&mut self) -> EntriesIterMut {
self.entries.iter_mut()
}
pub(super) fn kick(&mut self, bucket_depth: usize) -> Option<BTreeSet<DHTKey>> {
// Get number of entries to attempt to purge from bucket
let bucket_len = self.entries.len();
if bucket_len <= bucket_depth {
return None;
}
// Try to purge the newest entries that overflow the bucket
let mut dead_node_ids: BTreeSet<DHTKey> = BTreeSet::new();
let mut extra_entries = bucket_len - bucket_depth;
// Get the sorted list of entries by their kick order
let mut sorted_entries: Vec<(&_, &_)> = self.entries.iter().collect();
let cur_ts = get_timestamp();
sorted_entries.sort_by(
|a: &(&DHTKey, &BucketEntry), b: &(&DHTKey, &BucketEntry)| -> core::cmp::Ordering {
let ea = a.1;
let eb = b.1;
let astate = state_ordering(ea.state(cur_ts));
let bstate = state_ordering(eb.state(cur_ts));
// first kick dead nodes, then unreliable nodes
if astate < bstate {
return core::cmp::Ordering::Less;
}
if astate > bstate {
return core::cmp::Ordering::Greater;
}
// then kick by time added, most recent nodes are kicked first
let ata = ea.peer_stats().time_added;
let bta = eb.peer_stats().time_added;
bta.cmp(&ata)
},
);
self.newest_entry = None;
for i in 0..sorted_entries.len() {
// If we're not evicting more entries, exit, noting this may be the newest entry
if extra_entries == 0 {
// The first 'live' entry we find is our newest entry
if self.newest_entry.is_none() {
self.newest_entry = Some(sorted_entries[i].0.clone());
}
break;
}
extra_entries -= 1;
// if this entry has references we can't drop it yet
if sorted_entries[i].1.ref_count > 0 {
// The first 'live' entry we fine is our newest entry
if self.newest_entry.is_none() {
self.newest_entry = Some(sorted_entries[i].0.clone());
}
continue;
}
// if no references, lets evict it
dead_node_ids.insert(sorted_entries[i].0.clone());
}
// Now purge the dead node ids
for id in &dead_node_ids {
// Remove the entry
self.remove_entry(id);
}
if dead_node_ids.len() > 0 {
Some(dead_node_ids)
} else {
None
}
}
}

View File

@@ -0,0 +1,446 @@
use super::*;
// Latency entry is per round-trip packet (ping or data)
// - Size is number of entries
const ROLLING_LATENCIES_SIZE: usize = 10;
// Transfers entries are in bytes total for the interval
// - Size is number of entries
// - Interval is number of seconds in each entry
const ROLLING_TRANSFERS_SIZE: usize = 10;
pub const ROLLING_TRANSFERS_INTERVAL_SECS: u32 = 10;
// Reliable pings are done with increased spacing between pings
// - Start secs is the number of seconds between the first two pings
// - Max secs is the maximum number of seconds between consecutive pings
// - Multiplier changes the number of seconds between pings over time
// making it longer as the node becomes more reliable
const RELIABLE_PING_INTERVAL_START_SECS: u32 = 10;
const RELIABLE_PING_INTERVAL_MAX_SECS: u32 = 10 * 60;
const RELIABLE_PING_INTERVAL_MULTIPLIER: f64 = 2.0;
// Unreliable pings are done for a fixed amount of time while the
// node is given a chance to come back online before it is made dead
// If a node misses a single ping, it is marked unreliable and must
// return reliable pings for the duration of the span before being
// marked reliable again
// - Span is the number of seconds total to attempt to validate the node
// - Interval is the number of seconds between each ping
const UNRELIABLE_PING_SPAN_SECS: u32 = 60;
const UNRELIABLE_PING_INTERVAL_SECS: u32 = 5;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BucketEntryState {
Reliable,
Unreliable,
Dead,
}
#[derive(Debug, Clone)]
pub struct BucketEntry {
pub(super) ref_count: u32,
min_max_version: Option<(u8, u8)>,
last_connection: Option<(ConnectionDescriptor, u64)>,
dial_info_entries: VecDeque<DialInfoEntry>,
rolling_latencies: VecDeque<u64>,
rolling_transfers: VecDeque<(u64, u64)>,
current_transfer: (u64, u64),
peer_stats: PeerStats,
}
impl BucketEntry {
pub(super) fn new() -> Self {
Self {
ref_count: 0,
min_max_version: None,
last_connection: None,
dial_info_entries: VecDeque::new(),
rolling_latencies: VecDeque::new(),
rolling_transfers: VecDeque::new(),
current_transfer: (0, 0),
peer_stats: PeerStats {
time_added: get_timestamp(),
last_seen: None,
ping_stats: PingStats::default(),
latency: None,
transfer: (TransferStats::default(), TransferStats::default()),
node_info: None,
},
}
}
pub fn add_dial_info(&mut self, dial_info: DialInfo) -> Result<(), String> {
let mut idx: Option<usize> = None;
for i in 0..self.dial_info_entries.len() {
if self.dial_info_entries[i].dial_info() == &dial_info {
idx = Some(i);
break;
}
}
match idx {
None => {
self.dial_info_entries
.push_front(DialInfoEntry::try_new(dial_info)?);
}
Some(idx) => {
let die = self.dial_info_entries.remove(idx).unwrap();
self.dial_info_entries.push_front(die);
}
}
Ok(())
}
pub fn best_dial_info(&self) -> Option<DialInfo> {
self.dial_info_entries
.front()
.map(|die| die.dial_info().clone())
}
pub fn filtered_dial_info<F>(&self, filter: F) -> Option<DialInfo>
where
F: Fn(&DialInfoEntry) -> bool,
{
for die in &self.dial_info_entries {
if filter(die) {
return Some(die.dial_info().clone());
}
}
None
}
pub fn dial_info_entries_as_ref(&self) -> &VecDeque<DialInfoEntry> {
&self.dial_info_entries
}
pub fn dial_info(&self) -> Vec<DialInfo> {
self.dial_info_entries
.iter()
.map(|e| e.dial_info().clone())
.collect()
}
pub fn public_dial_info(&self) -> Vec<DialInfo> {
self.dial_info_entries
.iter()
.filter_map(|e| {
if e.is_public() {
Some(e.dial_info().clone())
} else {
None
}
})
.collect()
}
pub fn public_dial_info_for_protocol(&self, protocol_type: ProtocolType) -> Vec<DialInfo> {
self.dial_info_entries
.iter()
.filter_map(|e| {
if e.dial_info().protocol_type() != protocol_type {
None
} else if e.is_public() {
Some(e.dial_info().clone())
} else {
None
}
})
.collect()
}
pub fn private_dial_info(&self) -> Vec<DialInfo> {
self.dial_info_entries
.iter()
.filter_map(|e| {
if e.is_private() {
Some(e.dial_info().clone())
} else {
None
}
})
.collect()
}
pub fn private_dial_info_for_protocol(&mut self, protocol_type: ProtocolType) -> Vec<DialInfo> {
self.dial_info_entries
.iter_mut()
.filter_map(|e| {
if e.dial_info().protocol_type() != protocol_type {
None
} else if e.is_private() {
Some(e.dial_info().clone())
} else {
None
}
})
.collect()
}
pub fn get_peer_info(&self, key: DHTKey, scope: PeerScope) -> PeerInfo {
PeerInfo {
node_id: NodeId::new(key),
dial_infos: match scope {
PeerScope::All => self.dial_info(),
PeerScope::Public => self.public_dial_info(),
PeerScope::Private => self.private_dial_info(),
},
}
}
pub fn set_last_connection(&mut self, last_connection: ConnectionDescriptor, timestamp: u64) {
self.last_connection = Some((last_connection, timestamp));
// sort the dialinfoentries by the last peer address if we have a match
// if one particular peer address is being used and matches a dialinfoentry
// then we should prefer it
for i in 0..self.dial_info_entries.len() {
let die = &mut self.dial_info_entries[i];
// see if we have a matching address
if RoutingTable::dial_info_peer_address_match(
die.dial_info(),
&self.last_connection.as_ref().unwrap().0.remote,
) {
drop(die);
// push the most recent dialinfo to the front
let dies = &mut self.dial_info_entries;
let die = dies.remove(i).unwrap();
dies.push_front(die);
break;
}
}
}
pub fn last_connection(&self) -> Option<ConnectionDescriptor> {
match self.last_connection.as_ref() {
Some(x) => Some(x.0.clone()),
None => None,
}
}
pub fn set_min_max_version(&mut self, min_max_version: (u8, u8)) {
self.min_max_version = Some(min_max_version);
}
pub fn min_max_version(&self) -> Option<(u8, u8)> {
self.min_max_version
}
pub fn state(&self, cur_ts: u64) -> BucketEntryState {
if self.check_reliable(cur_ts) {
BucketEntryState::Reliable
} else if self.check_dead(cur_ts) {
BucketEntryState::Dead
} else {
BucketEntryState::Unreliable
}
}
pub fn peer_stats(&self) -> &PeerStats {
&self.peer_stats
}
pub fn update_node_info(&mut self, node_info: NodeInfo) {
self.peer_stats.node_info = Some(node_info);
}
///// stats methods
// called every ROLLING_TRANSFERS_INTERVAL_SECS seconds
pub(super) fn roll_transfers(&mut self, last_ts: u64, cur_ts: u64) {
let dur_ms = (cur_ts - last_ts) / 1000u64;
while self.rolling_transfers.len() >= ROLLING_TRANSFERS_SIZE {
self.rolling_transfers.pop_front();
}
self.rolling_transfers.push_back(self.current_transfer);
self.current_transfer = (0, 0);
let xd = &mut self.peer_stats.transfer.0;
let xu = &mut self.peer_stats.transfer.1;
xd.maximum = 0;
xu.maximum = 0;
xd.minimum = u64::MAX;
xu.minimum = u64::MAX;
xd.average = 0;
xu.average = 0;
for (rtd, rtu) in &self.rolling_transfers {
let bpsd = rtd * 1000u64 / dur_ms;
let bpsu = rtu * 1000u64 / dur_ms;
if bpsd > xd.maximum {
xd.maximum = bpsd;
}
if bpsu > xu.maximum {
xu.maximum = bpsu;
}
if bpsd < xd.minimum {
xd.minimum = bpsd;
}
if bpsu < xu.minimum {
xu.minimum = bpsu;
}
xd.average += bpsd;
xu.average += bpsu;
}
let len = self.rolling_transfers.len() as u64;
xd.average /= len;
xu.average /= len;
// total remains unchanged
}
// Called for every round trip packet we receive
fn record_latency(&mut self, latency: u64) {
while self.rolling_latencies.len() >= ROLLING_LATENCIES_SIZE {
self.rolling_latencies.pop_front();
}
self.rolling_latencies.push_back(latency);
let mut ls = LatencyStats {
fastest: 0,
average: 0,
slowest: 0,
};
for rl in &self.rolling_latencies {
if *rl < ls.fastest {
ls.fastest = *rl;
}
if *rl > ls.slowest {
ls.slowest = *rl;
}
ls.average += *rl;
}
let len = self.rolling_latencies.len() as u64;
ls.average /= len;
self.peer_stats.latency = Some(ls);
}
///// state machine handling
pub(super) fn check_reliable(&self, cur_ts: u64) -> bool {
// if we have had consecutive ping replies for longer that UNRELIABLE_PING_SPAN_SECS
match self.peer_stats.ping_stats.first_consecutive_pong_time {
None => false,
Some(ts) => (cur_ts - ts) >= (UNRELIABLE_PING_SPAN_SECS as u64 * 1000000u64),
}
}
pub(super) fn check_dead(&self, cur_ts: u64) -> bool {
// if we have not heard from the node at all for the duration of the unreliable ping span
match self.peer_stats.last_seen {
None => true,
Some(ts) => (cur_ts - ts) >= (UNRELIABLE_PING_SPAN_SECS as u64 * 1000000u64),
}
}
pub(super) fn needs_ping(&self, cur_ts: u64) -> bool {
// if we need a ping right now to validate us
match self.state(cur_ts) {
BucketEntryState::Reliable => {
// If we are in a reliable state, we need a ping on an exponential scale
match self.peer_stats.ping_stats.last_pinged {
None => true,
Some(last_pinged) => {
let first_consecutive_pong_time = self
.peer_stats
.ping_stats
.first_consecutive_pong_time
.unwrap();
let start_of_reliable_time = first_consecutive_pong_time
+ (UNRELIABLE_PING_SPAN_SECS as u64 * 1000000u64);
let reliable_cur = cur_ts - start_of_reliable_time;
let reliable_last = last_pinged - start_of_reliable_time;
retry_falloff_log(
reliable_last,
reliable_cur,
RELIABLE_PING_INTERVAL_START_SECS as u64 * 1000000u64,
RELIABLE_PING_INTERVAL_MAX_SECS as u64 * 1000000u64,
RELIABLE_PING_INTERVAL_MULTIPLIER,
)
}
}
}
BucketEntryState::Unreliable => {
// If we are in an unreliable state, we need a ping every UNRELIABLE_PING_INTERVAL_SECS seconds
match self.peer_stats.ping_stats.last_pinged {
None => true,
Some(last_pinged) => {
(cur_ts - last_pinged)
>= (UNRELIABLE_PING_INTERVAL_SECS as u64 * 1000000u64)
}
}
}
BucketEntryState::Dead => false,
}
}
pub(super) fn touch_last_seen(&mut self, ts: u64) {
// If we've heard from the node at all, we can always restart our lost ping count
self.peer_stats.ping_stats.recent_lost_pings = 0;
// Mark the node as seen
self.peer_stats.last_seen = Some(ts);
}
////////////////////////////////////////////////////////////////
/// Called by RPC processor as events happen
pub fn ping_sent(&mut self, ts: u64, bytes: u64) {
self.peer_stats.ping_stats.total_sent += 1;
self.current_transfer.1 += bytes;
self.peer_stats.ping_stats.in_flight += 1;
self.peer_stats.ping_stats.last_pinged = Some(ts);
}
pub fn ping_rcvd(&mut self, ts: u64, bytes: u64) {
self.current_transfer.0 += bytes;
self.touch_last_seen(ts);
}
pub fn pong_sent(&mut self, _ts: u64, bytes: u64) {
self.current_transfer.1 += bytes;
}
pub fn pong_rcvd(&mut self, send_ts: u64, recv_ts: u64, bytes: u64) {
self.current_transfer.0 += bytes;
self.peer_stats.ping_stats.in_flight -= 1;
self.peer_stats.ping_stats.total_returned += 1;
self.peer_stats.ping_stats.consecutive_pongs += 1;
if self
.peer_stats
.ping_stats
.first_consecutive_pong_time
.is_none()
{
self.peer_stats.ping_stats.first_consecutive_pong_time = Some(recv_ts);
}
self.record_latency(recv_ts - send_ts);
self.touch_last_seen(recv_ts);
}
pub fn ping_lost(&mut self, _ts: u64) {
self.peer_stats.ping_stats.in_flight -= 1;
self.peer_stats.ping_stats.recent_lost_pings += 1;
self.peer_stats.ping_stats.consecutive_pongs = 0;
self.peer_stats.ping_stats.first_consecutive_pong_time = None;
}
pub fn question_sent(&mut self, _ts: u64, bytes: u64) {
self.current_transfer.1 += bytes;
}
pub fn question_rcvd(&mut self, ts: u64, bytes: u64) {
self.current_transfer.0 += bytes;
self.touch_last_seen(ts);
}
pub fn answer_sent(&mut self, _ts: u64, bytes: u64) {
self.current_transfer.1 += bytes;
}
pub fn answer_rcvd(&mut self, send_ts: u64, recv_ts: u64, bytes: u64) {
self.current_transfer.0 += bytes;
self.record_latency(recv_ts - send_ts);
self.touch_last_seen(recv_ts);
}
pub fn question_lost(&mut self, _ts: u64) {
self.peer_stats.ping_stats.consecutive_pongs = 0;
self.peer_stats.ping_stats.first_consecutive_pong_time = None;
}
}
impl Drop for BucketEntry {
fn drop(&mut self) {
assert_eq!(self.ref_count, 0);
}
}

View File

@@ -0,0 +1,61 @@
use super::*;
#[derive(Debug, Clone)]
pub struct DialInfoEntry {
dial_info: DialInfo,
resolved_address: IpAddr,
}
impl DialInfoEntry {
pub fn try_new(dial_info: DialInfo) -> Result<Self, String> {
let addr = match dial_info.resolve() {
Ok(a) => a,
Err(_) => return Err("failed to resolve address".to_owned()),
};
Ok(Self {
dial_info: dial_info,
resolved_address: addr,
})
}
pub fn dial_info(&self) -> &DialInfo {
&self.dial_info
}
pub fn address(&self) -> IpAddr {
self.resolved_address
}
pub fn resolve(&mut self) -> Result<IpAddr, String> {
let addr = match self.dial_info.resolve() {
Ok(a) => a,
Err(_) => return Err("failed to resolve address".to_owned()),
};
self.resolved_address = addr;
Ok(addr)
}
pub fn matches_peer_scope(&self, scope: PeerScope) -> bool {
match scope {
PeerScope::All => true,
PeerScope::Public => self.is_public(),
PeerScope::Private => self.is_private(),
}
}
pub fn is_public(&self) -> bool {
ipaddr_is_global(&self.resolved_address)
}
pub fn is_private(&self) -> bool {
match self.resolved_address {
IpAddr::V4(a) => ipv4addr_is_private(&a),
IpAddr::V6(a) => ipv6addr_is_unicast_site_local(&a),
}
}
pub fn is_valid(&self) -> bool {
self.is_public() || self.is_private()
}
pub fn is_loopback(&self) -> bool {
ipaddr_is_loopback(&self.resolved_address)
}
}

View File

@@ -0,0 +1,280 @@
use super::*;
use crate::dht::*;
use crate::intf::*;
use crate::xx::*;
use crate::*;
impl RoutingTable {
// Retrieve the fastest nodes in the routing table with a particular kind of protocol address type
// Returns noderefs are are scoped to that address type only
pub fn get_fast_nodes_of_type(
&self,
protocol_address_type: ProtocolAddressType,
) -> Vec<NodeRef> {
self.find_fastest_nodes(
// filter
Some(Box::new(
move |params: &(&DHTKey, Option<&mut BucketEntry>)| {
// Only interested in nodes with node info
if let Some(node_info) = &params.1.as_ref().unwrap().peer_stats().node_info {
// Will the node validate dial info?
// and does it have a UDPv4, public scope, dial info?
if node_info.will_validate_dial_info
&& params
.1
.as_ref()
.unwrap()
.dial_info_entries_as_ref()
.iter()
.find_map(|die| {
if die.matches_peer_scope(PeerScope::Public)
&& die.dial_info().protocol_address_type()
== protocol_address_type
{
Some(())
} else {
None
}
})
.is_some()
{
// If so return true and include this node
return true;
}
}
false
},
)),
// transform
|e| {
NodeRef::new_filtered(
self.clone(),
*e.0,
e.1.as_mut().unwrap(),
protocol_address_type,
)
},
)
}
pub fn get_own_peer_info(&self, scope: PeerScope) -> PeerInfo {
let dial_infos = match scope {
PeerScope::All => {
let mut divec = self.public_dial_info();
divec.append(&mut self.local_dial_info());
divec.dedup();
divec
}
PeerScope::Public => self.public_dial_info(),
PeerScope::Private => self.local_dial_info(),
};
PeerInfo {
node_id: NodeId::new(self.node_id()),
dial_infos: dial_infos.iter().map(|x| x.dial_info.clone()).collect(),
}
}
pub fn transform_to_peer_info(
kv: &mut (&DHTKey, Option<&mut BucketEntry>),
scope: PeerScope,
own_peer_info: &PeerInfo,
) -> PeerInfo {
match &kv.1 {
None => own_peer_info.clone(),
Some(entry) => entry.get_peer_info(*kv.0, scope),
}
}
pub fn find_peers_with_sort_and_filter<F, C, T, O>(
&self,
node_count: usize,
cur_ts: u64,
filter: F,
compare: C,
transform: T,
) -> Vec<O>
where
F: Fn(&(&DHTKey, Option<&mut BucketEntry>)) -> bool,
C: Fn(
&(&DHTKey, Option<&mut BucketEntry>),
&(&DHTKey, Option<&mut BucketEntry>),
) -> core::cmp::Ordering,
T: Fn(&mut (&DHTKey, Option<&mut BucketEntry>)) -> O,
{
let mut inner = self.inner.lock();
// collect all the nodes for sorting
let mut nodes =
Vec::<(&DHTKey, Option<&mut BucketEntry>)>::with_capacity(inner.bucket_entry_count + 1);
// add our own node (only one of there with the None entry)
let self_node_id = inner.node_id.clone();
let selfkv = (&self_node_id, None);
if filter(&selfkv) {
nodes.push(selfkv);
}
// add all nodes from buckets
for b in &mut inner.buckets {
for (k, v) in b.entries_mut() {
// Don't bother with dead nodes
if !v.check_dead(cur_ts) {
// Apply filter
let kv = (k, Some(v));
if filter(&kv) {
nodes.push(kv);
}
}
}
}
// sort by preference for returning nodes
nodes.sort_by(compare);
// return transformed vector for filtered+sorted nodes
let cnt = usize::min(node_count, nodes.len());
let mut out = Vec::<O>::with_capacity(cnt);
for i in 0..cnt {
let val = transform(&mut nodes[i]);
out.push(val);
}
out
}
pub fn find_fastest_nodes<T, O>(
&self,
filter: Option<Box<dyn Fn(&(&DHTKey, Option<&mut BucketEntry>)) -> bool>>,
transform: T,
) -> Vec<O>
where
T: Fn(&mut (&DHTKey, Option<&mut BucketEntry>)) -> O,
{
let cur_ts = get_timestamp();
let node_count = {
let c = self.config.get();
c.network.dht.max_find_node_count as usize
};
let out = self.find_peers_with_sort_and_filter(
node_count,
cur_ts,
// filter
|kv| {
if kv.1.is_none() {
// filter out self peer, as it is irrelevant to the 'fastest nodes' search
false
} else if filter.is_some() && !filter.as_ref().unwrap()(kv) {
false
} else {
true
}
},
// sort
|(a_key, a_entry), (b_key, b_entry)| {
// same nodes are always the same
if a_key == b_key {
return core::cmp::Ordering::Equal;
}
// our own node always comes last (should not happen, here for completeness)
if a_entry.is_none() {
return core::cmp::Ordering::Greater;
}
if b_entry.is_none() {
return core::cmp::Ordering::Less;
}
// reliable nodes come first
let ae = a_entry.as_ref().unwrap();
let be = b_entry.as_ref().unwrap();
let ra = ae.check_reliable(cur_ts);
let rb = be.check_reliable(cur_ts);
if ra != rb {
if ra {
return core::cmp::Ordering::Less;
} else {
return core::cmp::Ordering::Greater;
}
}
// latency is the next metric, closer nodes first
let a_latency = match ae.peer_stats().latency.as_ref() {
None => {
// treat unknown latency as slow
return core::cmp::Ordering::Greater;
}
Some(l) => l,
};
let b_latency = match be.peer_stats().latency.as_ref() {
None => {
// treat unknown latency as slow
return core::cmp::Ordering::Less;
}
Some(l) => l,
};
// Sort by average latency
a_latency.average.cmp(&b_latency.average)
},
// transform,
transform,
);
trace!(">> find_fastest_nodes: node count = {}", out.len());
out
}
pub fn find_closest_nodes<T, O>(
&self,
node_id: DHTKey,
filter: Option<Box<dyn Fn(&(&DHTKey, Option<&mut BucketEntry>)) -> bool>>,
transform: T,
) -> Vec<O>
where
T: Fn(&mut (&DHTKey, Option<&mut BucketEntry>)) -> O,
{
let cur_ts = get_timestamp();
let node_count = {
let c = self.config.get();
c.network.dht.max_find_node_count as usize
};
let out = self.find_peers_with_sort_and_filter(
node_count,
cur_ts,
// filter
|kv| {
if kv.1.is_none() {
// include self peer, as it is relevant to the 'closest nodes' search
true
} else if filter.is_some() && !filter.as_ref().unwrap()(kv) {
false
} else {
true
}
},
// sort
|(a_key, a_entry), (b_key, b_entry)| {
// same nodes are always the same
if a_key == b_key {
return core::cmp::Ordering::Equal;
}
// reliable nodes come first, pessimistically treating our own node as unreliable
let ra = a_entry.as_ref().map_or(false, |x| x.check_reliable(cur_ts));
let rb = b_entry.as_ref().map_or(false, |x| x.check_reliable(cur_ts));
if ra != rb {
if ra {
return core::cmp::Ordering::Less;
} else {
return core::cmp::Ordering::Greater;
}
}
// distance is the next metric, closer nodes first
let da = distance(a_key, &node_id);
let db = distance(b_key, &node_id);
da.cmp(&db)
},
// transform,
transform,
);
trace!(">> find_closest_nodes: node count = {}", out.len());
out
}
}

View File

@@ -0,0 +1,731 @@
mod bucket;
mod bucket_entry;
mod dial_info_entry;
mod find_nodes;
mod node_ref;
use bucket::*;
pub use bucket_entry::*;
pub use dial_info_entry::*;
pub use find_nodes::*;
pub use node_ref::*;
use crate::dht::*;
use crate::intf::*;
use crate::network_manager::*;
use crate::rpc_processor::*;
use crate::xx::*;
use crate::*;
use alloc::collections::VecDeque;
use alloc::str::FromStr;
use futures_util::stream::{FuturesUnordered, StreamExt};
//////////////////////////////////////////////////////////////////////////
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
pub enum DialInfoOrigin {
Static,
Discovered,
Mapped,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
pub struct DialInfoDetail {
pub dial_info: DialInfo,
pub origin: DialInfoOrigin,
pub network_class: Option<NetworkClass>,
pub timestamp: u64,
}
struct RoutingTableInner {
network_manager: NetworkManager,
node_id: DHTKey,
node_id_secret: DHTKeySecret,
buckets: Vec<Bucket>,
//recent_nodes: VecDeque<DHTKey>,
//closest_reliable_nodes: Vec<DHTKey>,
//fastest_reliable_nodes: Vec<DHTKey>,
//closest_nodes: Vec<DHTKey>,
//fastest_nodes: Vec<DHTKey>,
local_dial_info: Vec<DialInfoDetail>,
public_dial_info: Vec<DialInfoDetail>,
bucket_entry_count: usize,
// Waiters
eventual_changed_dial_info: Eventual,
}
struct RoutingTableUnlockedInner {
// Background processes
rolling_transfers_task: TickTask,
bootstrap_task: TickTask,
peer_minimum_refresh_task: TickTask,
ping_validator_task: TickTask,
}
#[derive(Clone)]
pub struct RoutingTable {
config: VeilidConfig,
inner: Arc<Mutex<RoutingTableInner>>,
unlocked_inner: Arc<RoutingTableUnlockedInner>,
}
impl RoutingTable {
fn new_inner(network_manager: NetworkManager) -> RoutingTableInner {
RoutingTableInner {
network_manager: network_manager,
node_id: DHTKey::default(),
node_id_secret: DHTKeySecret::default(),
buckets: Vec::new(),
//recent_nodes: VecDeque::new(),
//closest_reliable_nodes: Vec::new(),
//fastest_reliable_nodes: Vec::new(),
//closest_nodes: Vec::new(),
//fastest_nodes: Vec::new(),
local_dial_info: Vec::new(),
public_dial_info: Vec::new(),
bucket_entry_count: 0,
eventual_changed_dial_info: Eventual::new(),
}
}
fn new_unlocked_inner(config: VeilidConfig) -> RoutingTableUnlockedInner {
let c = config.get();
RoutingTableUnlockedInner {
rolling_transfers_task: TickTask::new(bucket_entry::ROLLING_TRANSFERS_INTERVAL_SECS),
bootstrap_task: TickTask::new(1),
peer_minimum_refresh_task: TickTask::new_us(c.network.dht.min_peer_refresh_time),
ping_validator_task: TickTask::new(1),
}
}
pub fn new(network_manager: NetworkManager) -> Self {
let config = network_manager.config();
let this = Self {
config: config.clone(),
inner: Arc::new(Mutex::new(Self::new_inner(network_manager))),
unlocked_inner: Arc::new(Self::new_unlocked_inner(config)),
};
// Set rolling transfers tick task
{
let this2 = this.clone();
this.unlocked_inner
.rolling_transfers_task
.set_routine(move |l, t| {
Box::pin(this2.clone().rolling_transfers_task_routine(l, t))
});
}
// Set bootstrap tick task
{
let this2 = this.clone();
this.unlocked_inner
.bootstrap_task
.set_routine(move |_l, _t| Box::pin(this2.clone().bootstrap_task_routine()));
}
// Set peer minimum refresh tick task
{
let this2 = this.clone();
this.unlocked_inner
.peer_minimum_refresh_task
.set_routine(move |_l, _t| {
Box::pin(this2.clone().peer_minimum_refresh_task_routine())
});
}
// Set ping validator tick task
{
let this2 = this.clone();
this.unlocked_inner
.ping_validator_task
.set_routine(move |l, t| Box::pin(this2.clone().ping_validator_task_routine(l, t)));
}
this
}
pub fn network_manager(&self) -> NetworkManager {
self.inner.lock().network_manager.clone()
}
pub fn rpc_processor(&self) -> RPCProcessor {
self.network_manager().rpc_processor()
}
pub fn node_id(&self) -> DHTKey {
self.inner.lock().node_id
}
pub fn node_id_secret(&self) -> DHTKeySecret {
self.inner.lock().node_id_secret
}
pub fn has_local_dial_info(&self) -> bool {
let inner = self.inner.lock();
inner.local_dial_info.len() > 0
}
pub fn local_dial_info(&self) -> Vec<DialInfoDetail> {
let inner = self.inner.lock();
inner.local_dial_info.clone()
}
pub fn local_dial_info_for_protocol(&self, protocol_type: ProtocolType) -> Vec<DialInfoDetail> {
let inner = self.inner.lock();
inner
.local_dial_info
.iter()
.filter_map(|di| {
if di.dial_info.protocol_type() != protocol_type {
None
} else {
Some(di.clone())
}
})
.collect()
}
pub fn local_dial_info_for_protocol_address_type(
&self,
protocol_address_type: ProtocolAddressType,
) -> Vec<DialInfoDetail> {
let inner = self.inner.lock();
inner
.local_dial_info
.iter()
.filter_map(|di| {
if di.dial_info.protocol_address_type() != protocol_address_type {
None
} else {
Some(di.clone())
}
})
.collect()
}
pub fn register_local_dial_info(&self, dial_info: DialInfo, origin: DialInfoOrigin) {
let ts = get_timestamp();
let mut inner = self.inner.lock();
inner.local_dial_info.push(DialInfoDetail {
dial_info: dial_info.clone(),
origin: origin,
network_class: None,
timestamp: ts,
});
info!(
"Local Dial Info: {} ({:?})",
NodeDialInfoSingle {
node_id: NodeId::new(inner.node_id),
dial_info: dial_info.clone()
}
.to_string(),
origin,
);
}
pub fn clear_local_dial_info(&self) {
self.inner.lock().local_dial_info.clear();
}
pub fn has_public_dial_info(&self) -> bool {
let inner = self.inner.lock();
inner.public_dial_info.len() > 0
}
pub fn public_dial_info(&self) -> Vec<DialInfoDetail> {
let inner = self.inner.lock();
inner.public_dial_info.clone()
}
pub fn public_dial_info_for_protocol(
&self,
protocol_type: ProtocolType,
) -> Vec<DialInfoDetail> {
let inner = self.inner.lock();
inner
.public_dial_info
.iter()
.filter_map(|di| {
if di.dial_info.protocol_type() != protocol_type {
None
} else {
Some(di.clone())
}
})
.collect()
}
pub fn public_dial_info_for_protocol_address_type(
&self,
protocol_address_type: ProtocolAddressType,
) -> Vec<DialInfoDetail> {
let inner = self.inner.lock();
inner
.public_dial_info
.iter()
.filter_map(|di| {
if di.dial_info.protocol_address_type() != protocol_address_type {
None
} else {
Some(di.clone())
}
})
.collect()
}
pub fn register_public_dial_info(
&self,
dial_info: DialInfo,
network_class: Option<NetworkClass>,
origin: DialInfoOrigin,
) {
let ts = get_timestamp();
let mut inner = self.inner.lock();
inner.public_dial_info.push(DialInfoDetail {
dial_info: dial_info.clone(),
origin: origin,
network_class: network_class,
timestamp: ts,
});
info!(
"Public Dial Info: {} ({:?}#{:?})",
NodeDialInfoSingle {
node_id: NodeId::new(inner.node_id),
dial_info: dial_info.clone()
}
.to_string(),
origin,
network_class,
);
}
pub fn clear_public_dial_info(&self) {
self.inner.lock().public_dial_info.clear();
}
pub async fn wait_changed_dial_info(&self) {
let inst = self
.inner
.lock()
.eventual_changed_dial_info
.instance_empty();
inst.await;
}
pub async fn trigger_changed_dial_info(&self) {
let eventual = {
let mut inner = self.inner.lock();
let mut new_eventual = Eventual::new();
core::mem::swap(&mut inner.eventual_changed_dial_info, &mut new_eventual);
new_eventual
};
eventual.resolve().await;
}
fn bucket_depth(index: usize) -> usize {
match index {
0 => 256,
1 => 128,
2 => 64,
3 => 32,
4 => 16,
5 => 8,
6 => 4,
7 => 4,
8 => 4,
9 => 4,
_ => 4,
}
}
pub async fn init(&self) -> Result<(), String> {
let mut inner = self.inner.lock();
// Size the buckets (one per bit)
inner.buckets.reserve(DHT_KEY_LENGTH * 8);
for _ in 0..DHT_KEY_LENGTH * 8 {
let bucket = Bucket::new(self.clone());
inner.buckets.push(bucket);
}
// make local copy of node id for easy access
let c = self.config.get();
inner.node_id = c.network.node_id;
inner.node_id_secret = c.network.node_id_secret;
Ok(())
}
pub async fn terminate(&self) {
*self.inner.lock() = Self::new_inner(self.network_manager());
}
// Just match address and port to help sort dialinfoentries for buckets
// because inbound connections will not have dialinfo associated with them
// but should have ip addresses if they have changed
fn dial_info_peer_address_match(dial_info: &DialInfo, peer_addr: &PeerAddress) -> bool {
match dial_info {
DialInfo::UDP(_) => {
peer_addr.protocol_type == ProtocolType::UDP
&& peer_addr.port == dial_info.port()
&& peer_addr.address.address_string() == dial_info.address_string()
}
DialInfo::TCP(_) => {
peer_addr.protocol_type == ProtocolType::TCP
&& peer_addr.port == dial_info.port()
&& peer_addr.address.address_string() == dial_info.address_string()
}
DialInfo::WS(_) => {
peer_addr.protocol_type == ProtocolType::WS
&& peer_addr.port == dial_info.port()
&& peer_addr.address.address_string() == dial_info.address_string()
}
DialInfo::WSS(_) => {
peer_addr.protocol_type == ProtocolType::WSS
&& peer_addr.port == dial_info.port()
&& peer_addr.address.address_string() == dial_info.address_string()
}
}
}
// Attempt to settle buckets and remove entries down to the desired number
// which may not be possible due extant NodeRefs
fn kick_bucket(inner: &mut RoutingTableInner, idx: usize) {
let bucket = &mut inner.buckets[idx];
let bucket_depth = Self::bucket_depth(idx);
if let Some(dead_node_ids) = bucket.kick(bucket_depth) {
// Remove counts
inner.bucket_entry_count -= dead_node_ids.len();
debug!("Routing table now has {} nodes", inner.bucket_entry_count);
// Now purge the routing table inner vectors
//let filter = |k: &DHTKey| dead_node_ids.contains(k);
//inner.closest_reliable_nodes.retain(filter);
//inner.fastest_reliable_nodes.retain(filter);
//inner.closest_nodes.retain(filter);
//inner.fastest_nodes.retain(filter);
}
}
fn find_bucket_index(inner: &RoutingTableInner, node_id: DHTKey) -> usize {
distance(&node_id, &inner.node_id)
.first_nonzero_bit()
.unwrap()
}
fn drop_node_ref(&self, node_id: DHTKey) {
// Reduce ref count on entry
let mut inner = self.inner.lock();
let idx = Self::find_bucket_index(&*inner, node_id);
let new_ref_count = {
let bucket = &mut inner.buckets[idx];
let entry = bucket.entry_mut(&node_id).unwrap();
entry.ref_count -= 1;
entry.ref_count
};
// If this entry could possibly go away, kick the bucket
if new_ref_count == 0 {
// it important to do this in the same inner lock as the ref count decrease
Self::kick_bucket(&mut *inner, idx);
}
}
pub fn create_node_ref(&self, node_id: DHTKey) -> Result<NodeRef, String> {
// Ensure someone isn't trying register this node itself
if node_id == self.node_id() {
return Err("can't register own node".to_owned());
}
// Insert into bucket, possibly evicting the newest bucket member
let noderef = match self.lookup_node_ref(node_id) {
None => {
// Make new entry
let mut inner = self.inner.lock();
let idx = Self::find_bucket_index(&*inner, node_id);
let nr = {
// Get the bucket for the entry
let bucket = &mut inner.buckets[idx];
// Add new entry
let nr = bucket.add_entry(node_id);
// Update count
inner.bucket_entry_count += 1;
debug!("Routing table now has {} nodes", inner.bucket_entry_count);
nr
};
// Kick the bucket
// It is important to do this in the same inner lock as the add_entry
Self::kick_bucket(&mut *inner, idx);
nr
}
Some(nr) => nr,
};
Ok(noderef)
}
pub fn lookup_node_ref(&self, node_id: DHTKey) -> Option<NodeRef> {
let mut inner = self.inner.lock();
let idx = Self::find_bucket_index(&*inner, node_id);
let bucket = &mut inner.buckets[idx];
match bucket.entry_mut(&node_id) {
None => None,
Some(e) => Some(NodeRef::new(self.clone(), node_id, e)),
}
}
// Shortcut function to add a node to our routing table if it doesn't exist
// and add the dial info we have for it, since that's pretty common
pub fn register_node_with_dial_info(
&self,
node_id: DHTKey,
dial_infos: &[DialInfo],
) -> Result<NodeRef, String> {
let nr = match self.create_node_ref(node_id) {
Err(e) => {
return Err(format!("Couldn't create node reference: {}", e));
}
Ok(v) => v,
};
nr.operate(move |e| -> Result<(), String> {
for di in dial_infos {
e.add_dial_info(di.clone())?;
}
Ok(())
})?;
Ok(nr)
}
// Shortcut function to add a node to our routing table if it doesn't exist
// and add the last peer address we have for it, since that's pretty common
pub fn register_node_with_existing_connection(
&self,
node_id: DHTKey,
descriptor: ConnectionDescriptor,
timestamp: u64,
) -> Result<NodeRef, String> {
let nr = match self.create_node_ref(node_id) {
Err(e) => {
return Err(format!("Couldn't create node reference: {}", e));
}
Ok(v) => v,
};
nr.operate(move |e| {
// set the most recent node address for connection finding and udp replies
e.set_last_connection(descriptor, timestamp);
});
Ok(nr)
}
fn operate_on_bucket_entry<T, F>(&self, node_id: DHTKey, f: F) -> T
where
F: FnOnce(&mut BucketEntry) -> T,
{
let mut inner = self.inner.lock();
let idx = Self::find_bucket_index(&*inner, node_id);
let bucket = &mut inner.buckets[idx];
let entry = bucket.entry_mut(&node_id).unwrap();
f(entry)
}
pub async fn find_self(&self, node_ref: NodeRef) -> Result<Vec<NodeRef>, String> {
let node_id = self.node_id();
let rpc_processor = self.rpc_processor();
let res = match rpc_processor
.rpc_call_find_node(
Destination::Direct(node_ref.clone()),
node_id,
None,
RespondTo::Sender,
)
.await
{
Ok(v) => v,
Err(e) => {
return Err(format!("couldn't contact node at {:?}: {}", &node_ref, e));
}
};
trace!(
"find_self for at {:?} answered in {}ms",
&node_ref,
timestamp_to_secs(res.latency) * 1000.0f64
);
// register nodes we'd found
let mut out = Vec::<NodeRef>::with_capacity(res.peers.len());
for p in res.peers {
// if our own node if is in the list then ignore it, as we don't add ourselves to our own routing table
if p.node_id.key == node_id {
// however, it is useful to note when
continue;
}
// register the node if it's new
let nr = match self.register_node_with_dial_info(p.node_id.key, &p.dial_infos) {
Ok(v) => v,
Err(e) => {
return Err(format!(
"couldn't register node {} at {:?}: {}",
p.node_id.key, &p.dial_infos, e
));
}
};
out.push(nr);
}
Ok(out)
}
pub async fn reverse_find_node(&self, node_ref: NodeRef, wide: bool) {
// Ask bootstrap node to 'find' our own node so we can get some more nodes near ourselves
// and then contact those nodes to inform -them- that we exist
// Ask bootstrap server for nodes closest to our own node
let closest_nodes = match self.find_self(node_ref.clone()).await {
Err(e) => {
error!(
"reverse_find_node: find_self failed for {:?}: {}",
&node_ref, e
);
return;
}
Ok(v) => v,
};
// Ask each node near us to find us as well
if wide {
for closest_nr in closest_nodes {
match self.find_self(closest_nr.clone()).await {
Err(e) => {
error!(
"reverse_find_node: closest node find_self failed for {:?}: {}",
&closest_nr, e
);
return;
}
Ok(v) => v,
};
}
}
}
async fn bootstrap_task_routine(self) -> Result<(), String> {
let bootstrap = {
let c = self.config.get();
c.network.bootstrap.clone()
};
// Map all bootstrap entries to a single key with multiple dialinfo
let mut bsmap: BTreeMap<DHTKey, Vec<DialInfo>> = BTreeMap::new();
for b in bootstrap {
let ndis = match NodeDialInfoSingle::from_str(b.as_str()) {
Err(_) => {
return Err(format!("Invalid dial info in bootstrap entry: {}", b));
}
Ok(v) => v,
};
let node_id = ndis.node_id.key;
bsmap
.entry(node_id)
.or_insert(Vec::new())
.push(ndis.dial_info);
}
// Run all bootstrap operations concurrently
let mut unord = FuturesUnordered::new();
for (k, v) in bsmap {
let nr = match self.register_node_with_dial_info(k, &v) {
Ok(nr) => nr,
Err(e) => {
return Err(format!("Couldn't add bootstrap node: {}", e));
}
};
info!("Bootstrapping {} with {:?}", k.encode(), &v);
unord.push(self.reverse_find_node(nr, true));
}
while unord.next().await.is_some() {}
Ok(())
}
///////////////////////////////////////////////////////////
/// Peer ping validation
// Ask our remaining peers to give us more peers before we go
// back to the bootstrap servers to keep us from bothering them too much
async fn peer_minimum_refresh_task_routine(self) -> Result<(), String> {
// get list of all peers we know about, even the unreliable ones, and ask them to bootstrap too
let noderefs = {
let mut inner = self.inner.lock();
let mut noderefs = Vec::<NodeRef>::with_capacity(inner.bucket_entry_count);
for b in &mut inner.buckets {
for (k, entry) in b.entries_mut() {
noderefs.push(NodeRef::new(self.clone(), *k, entry))
}
}
noderefs
};
// do peer minimum search concurrently
let mut unord = FuturesUnordered::new();
for nr in noderefs {
debug!("Peer minimum search with {:?}", nr);
unord.push(self.reverse_find_node(nr, false));
}
while unord.next().await.is_some() {}
Ok(())
}
// Ping each node in the routing table if they need to be pinged
// to determine their reliability
async fn ping_validator_task_routine(self, _last_ts: u64, cur_ts: u64) -> Result<(), String> {
let rpc = self.rpc_processor();
let mut inner = self.inner.lock();
for b in &mut inner.buckets {
for (k, entry) in b.entries_mut() {
if entry.needs_ping(cur_ts) {
let nr = NodeRef::new(self.clone(), *k, entry);
intf::spawn_local(rpc.clone().rpc_call_info(nr)).detach();
}
}
}
Ok(())
}
// Compute transfer statistics to determine how 'fast' a node is
async fn rolling_transfers_task_routine(self, last_ts: u64, cur_ts: u64) -> Result<(), String> {
let mut inner = self.inner.lock();
for b in &mut inner.buckets {
b.roll_transfers(last_ts, cur_ts);
}
Ok(())
}
// Ticks about once per second
// to run tick tasks which may run at slower tick rates as configured
pub async fn tick(&self) -> Result<(), String> {
// Do rolling transfers every ROLLING_TRANSFERS_INTERVAL_SECS secs
self.unlocked_inner.rolling_transfers_task.tick().await?;
// If routing table is empty, then add the bootstrap nodes to it
if self.inner.lock().bucket_entry_count == 0 {
self.unlocked_inner.bootstrap_task.tick().await?;
}
// If we still don't have enough peers, find nodes until we do
let min_peer_count = {
let c = self.config.get();
c.network.dht.min_peer_count as usize
};
if self.inner.lock().bucket_entry_count < min_peer_count {
self.unlocked_inner.peer_minimum_refresh_task.tick().await?;
}
// Ping validate some nodes to groom the table
self.unlocked_inner.ping_validator_task.tick().await?;
Ok(())
}
}

View File

@@ -0,0 +1,108 @@
use super::*;
use crate::dht::*;
use alloc::fmt;
pub struct NodeRef {
routing_table: RoutingTable,
node_id: DHTKey,
protocol_address_type: Option<ProtocolAddressType>,
}
impl NodeRef {
pub fn new(routing_table: RoutingTable, key: DHTKey, entry: &mut BucketEntry) -> Self {
entry.ref_count += 1;
Self {
routing_table: routing_table,
node_id: key,
protocol_address_type: None,
}
}
pub fn new_filtered(
routing_table: RoutingTable,
key: DHTKey,
entry: &mut BucketEntry,
protocol_address_type: ProtocolAddressType,
) -> Self {
entry.ref_count += 1;
Self {
routing_table: routing_table,
node_id: key,
protocol_address_type: Some(protocol_address_type),
}
}
pub fn node_id(&self) -> DHTKey {
self.node_id
}
pub fn protocol_address_type(&self) -> Option<ProtocolAddressType> {
self.protocol_address_type
}
pub fn set_protocol_address_type(
&mut self,
protocol_address_type: Option<ProtocolAddressType>,
) {
self.protocol_address_type = protocol_address_type;
}
pub fn operate<T, F>(&self, f: F) -> T
where
F: FnOnce(&mut BucketEntry) -> T,
{
self.routing_table.operate_on_bucket_entry(self.node_id, f)
}
pub fn dial_info(&self) -> Option<DialInfo> {
match self.protocol_address_type {
None => self.operate(|e| e.best_dial_info()),
Some(pat) => self.operate(|e| {
e.filtered_dial_info(|die| die.dial_info().protocol_address_type() == pat)
}),
}
}
pub fn last_connection(&self) -> Option<ConnectionDescriptor> {
match self.operate(|e| e.last_connection()) {
None => None,
Some(c) => {
if let Some(protocol_address_type) = self.protocol_address_type {
if c.remote.protocol_address_type() == protocol_address_type {
Some(c)
} else {
None
}
} else {
Some(c)
}
}
}
}
}
impl Clone for NodeRef {
fn clone(&self) -> Self {
self.operate(move |e| {
e.ref_count += 1;
});
Self {
routing_table: self.routing_table.clone(),
node_id: self.node_id.clone(),
protocol_address_type: self.protocol_address_type,
}
}
}
impl fmt::Debug for NodeRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.protocol_address_type {
None => write!(f, "{}", self.node_id.encode()),
Some(pat) => write!(f, "{}#{:?}", self.node_id.encode(), pat),
}
}
}
impl Drop for NodeRef {
fn drop(&mut self) {
self.routing_table.drop_node_ref(self.node_id);
}
}