protocol level capabilities
This commit is contained in:
@@ -170,6 +170,13 @@ impl BucketEntryInner {
|
||||
common_crypto_kinds(&self.validated_node_ids.kinds(), other)
|
||||
}
|
||||
|
||||
/// Capability check
|
||||
pub fn has_capabilities(&self, routing_domain: RoutingDomain, capabilities: &[Capability]) -> bool {
|
||||
let Some(ni) = self.node_info(routing_domain) else {
|
||||
return false;
|
||||
};
|
||||
ni.has_capabilities(capabilities)
|
||||
}
|
||||
|
||||
// Less is faster
|
||||
pub fn cmp_fastest(e1: &Self, e2: &Self) -> std::cmp::Ordering {
|
||||
|
||||
@@ -2,7 +2,11 @@ use super::*;
|
||||
|
||||
impl RoutingTable {
|
||||
/// Utility to find all closest nodes to a particular key, including possibly our own node and nodes further away from the key than our own, returning their peer info
|
||||
pub fn find_all_closest_peers(&self, key: TypedKey) -> NetworkResult<Vec<PeerInfo>> {
|
||||
pub fn find_all_closest_peers(
|
||||
&self,
|
||||
key: TypedKey,
|
||||
capabilities: &[Capability],
|
||||
) -> NetworkResult<Vec<PeerInfo>> {
|
||||
let Some(own_peer_info) = self.get_own_peer_info(RoutingDomain::PublicInternet) else {
|
||||
// Our own node info is not yet available, drop this request.
|
||||
return NetworkResult::service_unavailable("Not finding closest peers because our peer info is not yet available");
|
||||
@@ -12,11 +16,27 @@ impl RoutingTable {
|
||||
let filter = Box::new(
|
||||
move |rti: &RoutingTableInner, opt_entry: Option<Arc<BucketEntry>>| {
|
||||
// Ensure only things that are valid/signed in the PublicInternet domain are returned
|
||||
rti.filter_has_valid_signed_node_info(
|
||||
if !rti.filter_has_valid_signed_node_info(
|
||||
RoutingDomain::PublicInternet,
|
||||
true,
|
||||
opt_entry,
|
||||
)
|
||||
opt_entry.clone(),
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
// Ensure capabilities are met
|
||||
match opt_entry {
|
||||
Some(entry) => entry.with(rti, |_rti, e| {
|
||||
e.has_capabilities(RoutingDomain::PublicInternet, capabilities)
|
||||
}),
|
||||
None => rti
|
||||
.get_own_peer_info(RoutingDomain::PublicInternet)
|
||||
.map(|pi| {
|
||||
pi.signed_node_info()
|
||||
.node_info()
|
||||
.has_capabilities(capabilities)
|
||||
})
|
||||
.unwrap_or(false),
|
||||
}
|
||||
},
|
||||
) as RoutingTableEntryFilter;
|
||||
let filters = VecDeque::from([filter]);
|
||||
@@ -40,7 +60,12 @@ impl RoutingTable {
|
||||
}
|
||||
|
||||
/// Utility to find nodes that are closer to a key than our own node, returning their peer info
|
||||
pub fn find_peers_closer_to_key(&self, key: TypedKey) -> NetworkResult<Vec<PeerInfo>> {
|
||||
/// Can filter based on a particular set of capabiltiies
|
||||
pub fn find_peers_closer_to_key(
|
||||
&self,
|
||||
key: TypedKey,
|
||||
required_capabilities: Vec<Capability>,
|
||||
) -> NetworkResult<Vec<PeerInfo>> {
|
||||
// add node information for the requesting node to our routing table
|
||||
let crypto_kind = key.kind;
|
||||
let own_node_id = self.node_id(crypto_kind);
|
||||
@@ -59,24 +84,29 @@ impl RoutingTable {
|
||||
let Some(entry) = opt_entry else {
|
||||
return false;
|
||||
};
|
||||
// Ensure only things that are valid/signed in the PublicInternet domain are returned
|
||||
if !rti.filter_has_valid_signed_node_info(
|
||||
RoutingDomain::PublicInternet,
|
||||
true,
|
||||
Some(entry.clone()),
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
// Ensure things further from the key than our own node are not included
|
||||
let Some(entry_node_id) = entry.with(rti, |_rti, e| e.node_ids().get(crypto_kind)) else {
|
||||
return false;
|
||||
};
|
||||
let entry_distance = vcrypto.distance(&entry_node_id.value, &key.value);
|
||||
if entry_distance >= own_distance {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
// Ensure only things that have a minimum set of capabilities are returned
|
||||
entry.with(rti, |rti, e| {
|
||||
if !e.has_capabilities(RoutingDomain::PublicInternet, &required_capabilities) {
|
||||
return false;
|
||||
}
|
||||
// Ensure only things that are valid/signed in the PublicInternet domain are returned
|
||||
if !rti.filter_has_valid_signed_node_info(
|
||||
RoutingDomain::PublicInternet,
|
||||
true,
|
||||
Some(entry.clone()),
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
// Ensure things further from the key than our own node are not included
|
||||
let Some(entry_node_id) = e.node_ids().get(crypto_kind) else {
|
||||
return false;
|
||||
};
|
||||
let entry_distance = vcrypto.distance(&entry_node_id.value, &key.value);
|
||||
if entry_distance >= own_distance {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
})
|
||||
},
|
||||
) as RoutingTableEntryFilter;
|
||||
let filters = VecDeque::from([filter]);
|
||||
|
||||
@@ -1072,7 +1072,7 @@ impl RoutingTable {
|
||||
let res = network_result_try!(
|
||||
rpc_processor
|
||||
.clone()
|
||||
.rpc_call_find_node(Destination::direct(node_ref), node_id)
|
||||
.rpc_call_find_node(Destination::direct(node_ref), node_id, vec![])
|
||||
.await?
|
||||
);
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ impl RouteSpecStore {
|
||||
// Exclude nodes with no publicinternet nodeinfo, or incompatible nodeinfo or node status won't route
|
||||
entry.with_inner(|e| {
|
||||
e.signed_node_info(RoutingDomain::PublicInternet).map(|sni|
|
||||
sni.has_sequencing_matched_dial_info(sequencing) && sni.node_info().can_route()
|
||||
sni.has_sequencing_matched_dial_info(sequencing) && sni.node_info().has_capability(CAP_ROUTE)
|
||||
).unwrap_or(false)
|
||||
})
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@ const BACKGROUND_SAFETY_ROUTE_COUNT: usize = 2;
|
||||
impl RoutingTable {
|
||||
fn get_background_safety_route_count(&self) -> usize {
|
||||
let c = self.config.get();
|
||||
if c.capabilities.disable.contains(&CAP_WILL_ROUTE) {
|
||||
if c.capabilities.disable.contains(&CAP_ROUTE) {
|
||||
0
|
||||
} else {
|
||||
BACKGROUND_SAFETY_ROUTE_COUNT
|
||||
|
||||
@@ -100,6 +100,11 @@ impl RoutingTable {
|
||||
let can_serve_as_relay = e
|
||||
.node_info(RoutingDomain::PublicInternet)
|
||||
.map(|n| {
|
||||
if !(n.has_capability(CAP_RELAY) && n.is_signal_capable()) {
|
||||
// Needs to be able to signal and relay
|
||||
return false;
|
||||
}
|
||||
|
||||
let dids = n.all_filtered_dial_info_details(DialInfoDetail::NO_SORT, |did| {
|
||||
did.matches_filter(&outbound_dif)
|
||||
});
|
||||
@@ -145,26 +150,23 @@ impl RoutingTable {
|
||||
inner.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, entry| {
|
||||
let entry2 = entry.clone();
|
||||
entry.with(rti, |rti, e| {
|
||||
// Ensure we have the node's status
|
||||
if let Some(node_info) = e.node_info(routing_domain) {
|
||||
// Ensure the node will relay
|
||||
if node_info.can_inbound_relay() {
|
||||
// Compare against previous candidate
|
||||
if let Some(best_inbound_relay) = best_inbound_relay.as_mut() {
|
||||
// Less is faster
|
||||
let better = best_inbound_relay.with(rti, |_rti, best| {
|
||||
// choose low latency stability for relays
|
||||
BucketEntryInner::cmp_fastest_reliable(cur_ts, e, best)
|
||||
== std::cmp::Ordering::Less
|
||||
});
|
||||
// Now apply filter function and see if this node should be included
|
||||
if better && relay_node_filter(e) {
|
||||
*best_inbound_relay = entry2;
|
||||
}
|
||||
} else if relay_node_filter(e) {
|
||||
// Always store the first candidate
|
||||
best_inbound_relay = Some(entry2);
|
||||
// Filter this node
|
||||
if relay_node_filter(e) {
|
||||
// Compare against previous candidate
|
||||
if let Some(best_inbound_relay) = best_inbound_relay.as_mut() {
|
||||
// Less is faster
|
||||
let better = best_inbound_relay.with(rti, |_rti, best| {
|
||||
// choose low latency stability for relays
|
||||
BucketEntryInner::cmp_fastest_reliable(cur_ts, e, best)
|
||||
== std::cmp::Ordering::Less
|
||||
});
|
||||
// Now apply filter function and see if this node should be included
|
||||
if better {
|
||||
*best_inbound_relay = entry2;
|
||||
}
|
||||
} else {
|
||||
// Always store the first candidate
|
||||
best_inbound_relay = Some(entry2);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
use super::*;
|
||||
|
||||
pub type Capability = FourCC;
|
||||
pub const CAP_WILL_ROUTE: Capability = FourCC(*b"ROUT");
|
||||
pub const CAP_ROUTE: Capability = FourCC(*b"ROUT");
|
||||
#[cfg(feature = "unstable-tunnels")]
|
||||
pub const CAP_WILL_TUNNEL: Capability = FourCC(*b"TUNL");
|
||||
pub const CAP_WILL_SIGNAL: Capability = FourCC(*b"SGNL");
|
||||
pub const CAP_WILL_RELAY: Capability = FourCC(*b"RLAY");
|
||||
pub const CAP_WILL_VALIDATE_DIAL_INFO: Capability = FourCC(*b"DIAL");
|
||||
pub const CAP_WILL_DHT: Capability = FourCC(*b"DHTV");
|
||||
pub const CAP_WILL_APPMESSAGE: Capability = FourCC(*b"APPM");
|
||||
pub const CAP_TUNNEL: Capability = FourCC(*b"TUNL");
|
||||
pub const CAP_SIGNAL: Capability = FourCC(*b"SGNL");
|
||||
pub const CAP_RELAY: Capability = FourCC(*b"RLAY");
|
||||
pub const CAP_VALIDATE_DIAL_INFO: Capability = FourCC(*b"DIAL");
|
||||
pub const CAP_DHT: Capability = FourCC(*b"DHTV");
|
||||
pub const CAP_APPMESSAGE: Capability = FourCC(*b"APPM");
|
||||
#[cfg(feature = "unstable-blockstore")]
|
||||
pub const CAP_WILL_BLOCKSTORE: Capability = FourCC(*b"BLOC");
|
||||
pub const CAP_BLOCKSTORE: Capability = FourCC(*b"BLOC");
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(all(feature = "unstable-blockstore", feature="unstable-tunnels"))] {
|
||||
@@ -22,16 +22,16 @@ cfg_if! {
|
||||
}
|
||||
}
|
||||
pub const PUBLIC_INTERNET_CAPABILITIES: [Capability; PUBLIC_INTERNET_CAPABILITIES_LEN] = [
|
||||
CAP_WILL_ROUTE,
|
||||
CAP_ROUTE,
|
||||
#[cfg(feature = "unstable-tunnels")]
|
||||
CAP_WILL_TUNNEL,
|
||||
CAP_WILL_SIGNAL,
|
||||
CAP_WILL_RELAY,
|
||||
CAP_WILL_VALIDATE_DIAL_INFO,
|
||||
CAP_WILL_DHT,
|
||||
CAP_WILL_APPMESSAGE,
|
||||
CAP_TUNNEL,
|
||||
CAP_SIGNAL,
|
||||
CAP_RELAY,
|
||||
CAP_VALIDATE_DIAL_INFO,
|
||||
CAP_DHT,
|
||||
CAP_APPMESSAGE,
|
||||
#[cfg(feature = "unstable-blockstore")]
|
||||
CAP_WILL_BLOCKSTORE,
|
||||
CAP_BLOCKSTORE,
|
||||
];
|
||||
|
||||
#[cfg(feature = "unstable-blockstore")]
|
||||
@@ -40,11 +40,11 @@ const LOCAL_NETWORK_CAPABILITIES_LEN: usize = 4;
|
||||
const LOCAL_NETWORK_CAPABILITIES_LEN: usize = 3;
|
||||
|
||||
pub const LOCAL_NETWORK_CAPABILITIES: [Capability; LOCAL_NETWORK_CAPABILITIES_LEN] = [
|
||||
CAP_WILL_RELAY,
|
||||
CAP_WILL_DHT,
|
||||
CAP_WILL_APPMESSAGE,
|
||||
CAP_RELAY,
|
||||
CAP_DHT,
|
||||
CAP_APPMESSAGE,
|
||||
#[cfg(feature = "unstable-blockstore")]
|
||||
CAP_WILL_BLOCKSTORE,
|
||||
CAP_BLOCKSTORE,
|
||||
];
|
||||
|
||||
pub const MAX_CAPABILITIES: usize = 64;
|
||||
@@ -199,14 +199,24 @@ impl NodeInfo {
|
||||
false
|
||||
}
|
||||
|
||||
fn has_capability(&self, cap: Capability) -> bool {
|
||||
pub fn has_capability(&self, cap: Capability) -> bool {
|
||||
self.capabilities.contains(&cap)
|
||||
}
|
||||
pub fn has_capabilities(&self, capabilities: &[Capability]) -> bool {
|
||||
for cap in capabilities {
|
||||
if !self.has_capability(*cap) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Can this node assist with signalling? Yes but only if it doesn't require signalling, itself.
|
||||
pub fn can_signal(&self) -> bool {
|
||||
/// Also used to determine if nodes are capable of validation of dial info, as that operation
|
||||
/// has the same requirements, inbound capability and a dial info that requires no assistance
|
||||
pub fn is_signal_capable(&self) -> bool {
|
||||
// Has capability?
|
||||
if !self.has_capability(CAP_WILL_SIGNAL) {
|
||||
if !self.has_capability(CAP_SIGNAL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -222,47 +232,4 @@ impl NodeInfo {
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Can this node relay be an inbound relay?
|
||||
pub fn can_inbound_relay(&self) -> bool {
|
||||
// Has capability?
|
||||
if !self.has_capability(CAP_WILL_RELAY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For now this is the same
|
||||
self.can_signal()
|
||||
}
|
||||
|
||||
/// Is this node capable of validating dial info
|
||||
pub fn can_validate_dial_info(&self) -> bool {
|
||||
// Has capability?
|
||||
if !self.has_capability(CAP_WILL_VALIDATE_DIAL_INFO) {
|
||||
return false;
|
||||
}
|
||||
// For now this is the same
|
||||
self.can_signal()
|
||||
}
|
||||
/// Is this node capable of private routing
|
||||
pub fn can_route(&self) -> bool {
|
||||
self.has_capability(CAP_WILL_ROUTE)
|
||||
}
|
||||
/// Is this node capable of dht operations
|
||||
pub fn can_dht(&self) -> bool {
|
||||
self.has_capability(CAP_WILL_DHT)
|
||||
}
|
||||
/// Is this node capable of app_message and app_call
|
||||
pub fn can_appmessage(&self) -> bool {
|
||||
self.has_capability(CAP_WILL_APPMESSAGE)
|
||||
}
|
||||
/// Is this node capable of tunneling
|
||||
#[cfg(feature = "unstable-tunnels")]
|
||||
pub fn can_tunnel(&self) -> bool {
|
||||
self.has_capability(CAP_WILL_TUNNEL)
|
||||
}
|
||||
/// Is this node capable of block storage
|
||||
#[cfg(feature = "unstable-blockstore")]
|
||||
pub fn can_blockstore(&self) -> bool {
|
||||
self.has_capability(CAP_WILL_BLOCKSTORE)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user