2021-11-22 16:28:30 +00:00
|
|
|
use super::*;
|
|
|
|
use crate::dht::*;
|
|
|
|
use alloc::fmt;
|
|
|
|
|
2022-04-17 23:10:10 +00:00
|
|
|
// Connectionless protocols like UDP are dependent on a NAT translation timeout
|
|
|
|
// We should ping them with some frequency and 30 seconds is typical timeout
|
|
|
|
const CONNECTIONLESS_TIMEOUT_SECS: u32 = 29;
|
|
|
|
|
2021-11-22 16:28:30 +00:00
|
|
|
pub struct NodeRef {
|
|
|
|
routing_table: RoutingTable,
|
|
|
|
node_id: DHTKey,
|
2022-06-25 14:57:33 +00:00
|
|
|
entry: Arc<BucketEntry>,
|
2022-04-19 15:23:44 +00:00
|
|
|
filter: Option<DialInfoFilter>,
|
2022-05-25 15:12:19 +00:00
|
|
|
#[cfg(feature = "tracking")]
|
|
|
|
track_id: usize,
|
2021-11-22 16:28:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl NodeRef {
|
2022-04-19 15:23:44 +00:00
|
|
|
pub fn new(
|
|
|
|
routing_table: RoutingTable,
|
2022-06-25 14:57:33 +00:00
|
|
|
node_id: DHTKey,
|
|
|
|
entry: Arc<BucketEntry>,
|
2022-04-19 15:23:44 +00:00
|
|
|
filter: Option<DialInfoFilter>,
|
|
|
|
) -> Self {
|
2022-06-25 14:57:33 +00:00
|
|
|
entry.ref_count.fetch_add(1u32, Ordering::Relaxed);
|
2022-05-25 15:12:19 +00:00
|
|
|
|
2021-11-22 16:28:30 +00:00
|
|
|
Self {
|
2021-11-26 15:39:43 +00:00
|
|
|
routing_table,
|
2022-06-25 14:57:33 +00:00
|
|
|
node_id,
|
|
|
|
entry,
|
2022-04-19 15:23:44 +00:00
|
|
|
filter,
|
2022-05-25 15:12:19 +00:00
|
|
|
#[cfg(feature = "tracking")]
|
|
|
|
track_id: entry.track(),
|
2021-11-22 16:28:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn node_id(&self) -> DHTKey {
|
|
|
|
self.node_id
|
|
|
|
}
|
|
|
|
|
2022-04-21 00:49:16 +00:00
|
|
|
pub fn filter_ref(&self) -> Option<&DialInfoFilter> {
|
|
|
|
self.filter.as_ref()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn take_filter(&mut self) -> Option<DialInfoFilter> {
|
|
|
|
self.filter.take()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_filter(&mut self, filter: Option<DialInfoFilter>) {
|
|
|
|
self.filter = filter
|
|
|
|
}
|
|
|
|
|
2022-08-02 01:06:31 +00:00
|
|
|
pub fn merge_filter(&mut self, filter: DialInfoFilter) {
|
|
|
|
if let Some(self_filter) = self.filter.take() {
|
|
|
|
self.filter = Some(self_filter.filtered(filter));
|
|
|
|
} else {
|
|
|
|
self.filter = Some(filter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn filtered_clone(&self, filter: DialInfoFilter) -> Self {
|
|
|
|
let mut out = self.clone();
|
|
|
|
out.merge_filter(filter);
|
|
|
|
out
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_filter_dead(&self) -> bool {
|
|
|
|
if let Some(filter) = &self.filter {
|
|
|
|
filter.is_dead()
|
|
|
|
} else {
|
|
|
|
false
|
2022-04-21 00:49:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-02 01:06:31 +00:00
|
|
|
// Returns true if some protocols can still pass the filter and false if no protocols remain
|
|
|
|
// pub fn filter_protocols(&mut self, protocol_set: ProtocolSet) -> bool {
|
|
|
|
// if protocol_set != ProtocolSet::all() {
|
|
|
|
// let mut dif = self.filter.clone().unwrap_or_default();
|
|
|
|
// dif.protocol_set &= protocol_set;
|
|
|
|
// self.filter = Some(dif);
|
|
|
|
// }
|
|
|
|
// self.filter
|
|
|
|
// .as_ref()
|
|
|
|
// .map(|f| !f.protocol_set.is_empty())
|
|
|
|
// .unwrap_or(true)
|
|
|
|
// }
|
|
|
|
|
2022-08-31 01:21:16 +00:00
|
|
|
pub(super) fn operate<T, F>(&self, f: F) -> T
|
2021-11-22 16:28:30 +00:00
|
|
|
where
|
2022-08-31 01:21:16 +00:00
|
|
|
F: FnOnce(&RoutingTableInner, &BucketEntryInner) -> T,
|
2021-11-22 16:28:30 +00:00
|
|
|
{
|
2022-08-31 01:21:16 +00:00
|
|
|
let inner = &*self.routing_table.inner.read();
|
|
|
|
self.entry.with(|e| f(inner, e))
|
2022-06-25 14:57:33 +00:00
|
|
|
}
|
|
|
|
|
2022-08-31 01:21:16 +00:00
|
|
|
pub(super) fn operate_mut<T, F>(&self, f: F) -> T
|
2022-06-25 14:57:33 +00:00
|
|
|
where
|
2022-08-31 01:21:16 +00:00
|
|
|
F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner) -> T,
|
2022-06-25 14:57:33 +00:00
|
|
|
{
|
2022-08-31 01:21:16 +00:00
|
|
|
let inner = &mut *self.routing_table.inner.write();
|
|
|
|
self.entry.with_mut(|e| f(inner, e))
|
2021-11-22 16:28:30 +00:00
|
|
|
}
|
|
|
|
|
2022-08-31 01:21:16 +00:00
|
|
|
pub fn peer_info(&self, routing_domain: RoutingDomain) -> Option<PeerInfo> {
|
|
|
|
self.operate(|_rti, e| e.peer_info(self.node_id(), routing_domain))
|
2022-04-16 15:18:54 +00:00
|
|
|
}
|
2022-08-31 01:21:16 +00:00
|
|
|
pub fn has_valid_signed_node_info(&self, opt_routing_domain: Option<RoutingDomain>) -> bool {
|
|
|
|
self.operate(|_rti, e| e.has_valid_signed_node_info(opt_routing_domain))
|
2022-03-25 02:07:55 +00:00
|
|
|
}
|
2022-08-31 01:21:16 +00:00
|
|
|
pub fn has_seen_our_node_info(&self, routing_domain: RoutingDomain) -> bool {
|
|
|
|
self.operate(|_rti, e| e.has_seen_our_node_info(routing_domain))
|
|
|
|
}
|
|
|
|
pub fn set_seen_our_node_info(&self, routing_domain: RoutingDomain) {
|
|
|
|
self.operate_mut(|_rti, e| e.set_seen_our_node_info(routing_domain, true));
|
2021-11-22 16:28:30 +00:00
|
|
|
}
|
2022-08-27 02:52:08 +00:00
|
|
|
pub fn has_updated_since_last_network_change(&self) -> bool {
|
2022-08-31 01:21:16 +00:00
|
|
|
self.operate(|_rti, e| e.has_updated_since_last_network_change())
|
2022-08-27 02:52:08 +00:00
|
|
|
}
|
|
|
|
pub fn set_updated_since_last_network_change(&self) {
|
2022-08-31 01:21:16 +00:00
|
|
|
self.operate_mut(|_rti, e| e.set_updated_since_last_network_change(true));
|
2022-08-27 02:52:08 +00:00
|
|
|
}
|
2022-08-31 01:21:16 +00:00
|
|
|
|
|
|
|
pub fn update_node_status(&self, node_status: NodeStatus) {
|
|
|
|
self.operate_mut(|_rti, e| {
|
|
|
|
e.update_node_status(node_status);
|
|
|
|
});
|
2022-04-21 00:49:16 +00:00
|
|
|
}
|
2022-08-31 01:21:16 +00:00
|
|
|
|
|
|
|
pub fn min_max_version(&self) -> Option<(u8, u8)> {
|
|
|
|
self.operate(|_rti, e| e.min_max_version())
|
2022-04-21 00:49:16 +00:00
|
|
|
}
|
2022-08-31 01:21:16 +00:00
|
|
|
|
|
|
|
pub fn set_min_max_version(&self, min_max_version: (u8, u8)) {
|
|
|
|
self.operate_mut(|_rti, e| e.set_min_max_version(min_max_version))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn state(&self, cur_ts: u64) -> BucketEntryState {
|
|
|
|
self.operate(|_rti, e| e.state(cur_ts))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn network_class(&self, routing_domain: RoutingDomain) -> Option<NetworkClass> {
|
|
|
|
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.network_class))
|
2022-08-02 01:06:31 +00:00
|
|
|
}
|
2022-08-31 01:21:16 +00:00
|
|
|
pub fn outbound_protocols(&self, routing_domain: RoutingDomain) -> Option<ProtocolTypeSet> {
|
|
|
|
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.outbound_protocols))
|
|
|
|
}
|
|
|
|
pub fn address_types(&self, routing_domain: RoutingDomain) -> Option<AddressTypeSet> {
|
|
|
|
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.address_types))
|
|
|
|
}
|
|
|
|
pub fn node_info_outbound_filter(&self, routing_domain: RoutingDomain) -> DialInfoFilter {
|
2022-08-02 01:06:31 +00:00
|
|
|
let mut dif = DialInfoFilter::all();
|
2022-08-31 01:21:16 +00:00
|
|
|
if let Some(outbound_protocols) = self.outbound_protocols(routing_domain) {
|
2022-08-02 01:06:31 +00:00
|
|
|
dif = dif.with_protocol_type_set(outbound_protocols);
|
|
|
|
}
|
2022-08-31 01:21:16 +00:00
|
|
|
if let Some(address_types) = self.address_types(routing_domain) {
|
2022-08-02 01:06:31 +00:00
|
|
|
dif = dif.with_address_type_set(address_types);
|
|
|
|
}
|
|
|
|
dif
|
|
|
|
}
|
|
|
|
|
2022-08-31 01:21:16 +00:00
|
|
|
pub fn relay(&self, routing_domain: RoutingDomain) -> Option<NodeRef> {
|
|
|
|
let target_rpi =
|
|
|
|
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.relay_peer_info))?;
|
2022-05-11 01:49:42 +00:00
|
|
|
target_rpi.and_then(|t| {
|
2022-05-11 13:37:54 +00:00
|
|
|
// If relay is ourselves, then return None, because we can't relay through ourselves
|
|
|
|
// and to contact this node we should have had an existing inbound connection
|
|
|
|
if t.node_id.key == self.routing_table.node_id() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register relay node and return noderef
|
2022-05-11 01:49:42 +00:00
|
|
|
self.routing_table
|
2022-08-31 01:21:16 +00:00
|
|
|
.register_node_with_signed_node_info(
|
|
|
|
routing_domain,
|
|
|
|
t.node_id.key,
|
|
|
|
t.signed_node_info,
|
|
|
|
false,
|
|
|
|
)
|
2022-05-11 01:49:42 +00:00
|
|
|
.map(|mut nr| {
|
|
|
|
nr.set_filter(self.filter_ref().cloned());
|
|
|
|
nr
|
|
|
|
})
|
|
|
|
})
|
2022-04-21 00:49:16 +00:00
|
|
|
}
|
2022-04-24 02:08:02 +00:00
|
|
|
pub fn first_filtered_dial_info_detail(
|
|
|
|
&self,
|
|
|
|
routing_domain: Option<RoutingDomain>,
|
|
|
|
) -> Option<DialInfoDetail> {
|
2022-08-31 01:21:16 +00:00
|
|
|
self.operate(|_rt, e| {
|
2022-04-25 15:29:02 +00:00
|
|
|
// Prefer local dial info first unless it is filtered out
|
2022-08-02 01:06:31 +00:00
|
|
|
if routing_domain == None || routing_domain == Some(RoutingDomain::LocalNetwork) {
|
2022-08-31 01:21:16 +00:00
|
|
|
e.node_info(RoutingDomain::LocalNetwork).and_then(|l| {
|
|
|
|
l.first_filtered_dial_info_detail(|did| {
|
2022-04-25 15:29:02 +00:00
|
|
|
if let Some(filter) = self.filter.as_ref() {
|
2022-08-31 01:21:16 +00:00
|
|
|
did.matches_filter(filter)
|
2022-04-24 02:08:02 +00:00
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
})
|
2022-05-11 01:49:42 +00:00
|
|
|
})
|
2022-04-19 15:23:44 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
.or_else(|| {
|
2022-08-02 01:06:31 +00:00
|
|
|
if routing_domain == None || routing_domain == Some(RoutingDomain::PublicInternet) {
|
2022-08-31 01:21:16 +00:00
|
|
|
e.node_info(RoutingDomain::PublicInternet).and_then(|n| {
|
2022-05-11 01:49:42 +00:00
|
|
|
n.first_filtered_dial_info_detail(|did| {
|
|
|
|
if let Some(filter) = self.filter.as_ref() {
|
|
|
|
did.matches_filter(filter)
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
})
|
2022-04-19 15:23:44 +00:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-04-24 02:08:02 +00:00
|
|
|
pub fn all_filtered_dial_info_details<F>(
|
|
|
|
&self,
|
|
|
|
routing_domain: Option<RoutingDomain>,
|
|
|
|
) -> Vec<DialInfoDetail> {
|
2022-04-19 15:23:44 +00:00
|
|
|
let mut out = Vec::new();
|
2022-08-31 01:21:16 +00:00
|
|
|
self.operate(|_rt, e| {
|
2022-04-25 15:29:02 +00:00
|
|
|
// Prefer local dial info first unless it is filtered out
|
2022-08-02 01:06:31 +00:00
|
|
|
if routing_domain == None || routing_domain == Some(RoutingDomain::LocalNetwork) {
|
2022-08-31 01:21:16 +00:00
|
|
|
if let Some(ni) = e.node_info(RoutingDomain::LocalNetwork) {
|
|
|
|
out.append(&mut ni.all_filtered_dial_info_details(|did| {
|
2022-05-11 01:49:42 +00:00
|
|
|
if let Some(filter) = self.filter.as_ref() {
|
2022-08-31 01:21:16 +00:00
|
|
|
did.matches_filter(filter)
|
2022-05-11 01:49:42 +00:00
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
2022-08-31 01:21:16 +00:00
|
|
|
}))
|
2022-04-24 02:08:02 +00:00
|
|
|
}
|
2022-04-19 15:23:44 +00:00
|
|
|
}
|
2022-08-02 01:06:31 +00:00
|
|
|
if routing_domain == None || routing_domain == Some(RoutingDomain::PublicInternet) {
|
2022-08-31 01:21:16 +00:00
|
|
|
if let Some(ni) = e.node_info(RoutingDomain::PublicInternet) {
|
2022-05-11 01:49:42 +00:00
|
|
|
out.append(&mut ni.all_filtered_dial_info_details(|did| {
|
|
|
|
if let Some(filter) = self.filter.as_ref() {
|
|
|
|
did.matches_filter(filter)
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
}
|
2022-04-19 15:23:44 +00:00
|
|
|
}
|
|
|
|
});
|
2022-04-24 02:08:02 +00:00
|
|
|
out.remove_duplicates();
|
2022-04-19 15:23:44 +00:00
|
|
|
out
|
|
|
|
}
|
|
|
|
|
2022-04-17 23:10:10 +00:00
|
|
|
pub async fn last_connection(&self) -> Option<ConnectionDescriptor> {
|
|
|
|
// Get the last connection and the last time we saw anything with this connection
|
2022-08-09 00:42:27 +00:00
|
|
|
let (last_connection, last_seen) =
|
2022-08-31 01:21:16 +00:00
|
|
|
self.operate(|_rti, e| e.last_connection(self.filter.clone()))?;
|
2022-05-28 14:07:57 +00:00
|
|
|
|
2022-04-17 23:10:10 +00:00
|
|
|
// Should we check the connection table?
|
|
|
|
if last_connection.protocol_type().is_connection_oriented() {
|
|
|
|
// Look the connection up in the connection manager and see if it's still there
|
|
|
|
let connection_manager = self.routing_table.network_manager().connection_manager();
|
|
|
|
connection_manager.get_connection(last_connection).await?;
|
|
|
|
} else {
|
|
|
|
// If this is not connection oriented, then we check our last seen time
|
|
|
|
// to see if this mapping has expired (beyond our timeout)
|
|
|
|
let cur_ts = intf::get_timestamp();
|
|
|
|
if (last_seen + (CONNECTIONLESS_TIMEOUT_SECS as u64 * 1_000_000u64)) < cur_ts {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(last_connection)
|
2021-11-22 16:28:30 +00:00
|
|
|
}
|
2022-04-17 23:10:10 +00:00
|
|
|
|
2022-08-09 00:42:27 +00:00
|
|
|
pub fn clear_last_connections(&self) {
|
2022-08-31 01:21:16 +00:00
|
|
|
self.operate_mut(|_rti, e| e.clear_last_connections())
|
2022-08-06 14:23:26 +00:00
|
|
|
}
|
|
|
|
|
2022-08-06 16:36:07 +00:00
|
|
|
pub fn set_last_connection(&self, connection_descriptor: ConnectionDescriptor, ts: u64) {
|
2022-08-31 01:21:16 +00:00
|
|
|
self.operate_mut(|_rti, e| e.set_last_connection(connection_descriptor, ts))
|
2022-08-06 16:36:07 +00:00
|
|
|
}
|
|
|
|
|
2022-04-17 17:28:39 +00:00
|
|
|
pub fn has_any_dial_info(&self) -> bool {
|
2022-08-31 01:21:16 +00:00
|
|
|
self.operate(|_rti, e| {
|
|
|
|
for rtd in RoutingDomain::all() {
|
|
|
|
if let Some(ni) = e.node_info(rtd) {
|
|
|
|
if ni.has_any_dial_info() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn stats_question_sent(&self, ts: u64, bytes: u64, expects_answer: bool) {
|
|
|
|
self.operate_mut(|rti, e| {
|
|
|
|
rti.self_transfer_stats_accounting.add_up(bytes);
|
|
|
|
e.question_sent(ts, bytes, expects_answer);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
pub fn stats_question_rcvd(&self, ts: u64, bytes: u64) {
|
|
|
|
self.operate_mut(|rti, e| {
|
|
|
|
rti.self_transfer_stats_accounting.add_down(bytes);
|
|
|
|
e.question_rcvd(ts, bytes);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
pub fn stats_answer_sent(&self, bytes: u64) {
|
|
|
|
self.operate_mut(|rti, e| {
|
|
|
|
rti.self_transfer_stats_accounting.add_up(bytes);
|
|
|
|
e.answer_sent(bytes);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
pub fn stats_answer_rcvd(&self, send_ts: u64, recv_ts: u64, bytes: u64) {
|
|
|
|
self.operate_mut(|rti, e| {
|
|
|
|
rti.self_transfer_stats_accounting.add_down(bytes);
|
|
|
|
rti.self_latency_stats_accounting
|
|
|
|
.record_latency(recv_ts - send_ts);
|
|
|
|
e.answer_rcvd(send_ts, recv_ts, bytes);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
pub fn stats_question_lost(&self) {
|
|
|
|
self.operate_mut(|_rti, e| {
|
|
|
|
e.question_lost();
|
|
|
|
})
|
|
|
|
}
|
|
|
|
pub fn stats_failed_to_send(&self, ts: u64, expects_answer: bool) {
|
|
|
|
self.operate_mut(|_rti, e| {
|
|
|
|
e.failed_to_send(ts, expects_answer);
|
2022-05-11 01:49:42 +00:00
|
|
|
})
|
2022-04-17 17:28:39 +00:00
|
|
|
}
|
2021-11-22 16:28:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Clone for NodeRef {
|
|
|
|
fn clone(&self) -> Self {
|
2022-06-25 14:57:33 +00:00
|
|
|
self.entry.ref_count.fetch_add(1u32, Ordering::Relaxed);
|
2022-05-25 15:12:19 +00:00
|
|
|
|
2022-06-25 14:57:33 +00:00
|
|
|
Self {
|
|
|
|
routing_table: self.routing_table.clone(),
|
|
|
|
node_id: self.node_id,
|
|
|
|
entry: self.entry.clone(),
|
|
|
|
filter: self.filter.clone(),
|
|
|
|
#[cfg(feature = "tracking")]
|
|
|
|
track_id: e.track(),
|
|
|
|
}
|
2021-11-22 16:28:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-17 17:28:39 +00:00
|
|
|
impl PartialEq for NodeRef {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
self.node_id == other.node_id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Eq for NodeRef {}
|
|
|
|
|
2022-05-25 15:12:19 +00:00
|
|
|
impl fmt::Display for NodeRef {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(f, "{}", self.node_id.encode())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-22 16:28:30 +00:00
|
|
|
impl fmt::Debug for NodeRef {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2022-05-25 15:12:19 +00:00
|
|
|
f.debug_struct("NodeRef")
|
|
|
|
.field("node_id", &self.node_id)
|
|
|
|
.field("filter", &self.filter)
|
|
|
|
.finish()
|
2021-11-22 16:28:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for NodeRef {
|
|
|
|
fn drop(&mut self) {
|
2022-05-25 15:12:19 +00:00
|
|
|
#[cfg(feature = "tracking")]
|
|
|
|
self.operate(|e| e.untrack(self.track_id));
|
2022-06-25 14:57:33 +00:00
|
|
|
|
|
|
|
// drop the noderef and queue a bucket kick if it was the last one
|
|
|
|
let new_ref_count = self.entry.ref_count.fetch_sub(1u32, Ordering::Relaxed) - 1;
|
|
|
|
if new_ref_count == 0 {
|
|
|
|
self.routing_table.queue_bucket_kick(self.node_id);
|
|
|
|
}
|
2021-11-22 16:28:30 +00:00
|
|
|
}
|
|
|
|
}
|