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