From 907075411d05ceac98778b2a34e6dfccc9be6484 Mon Sep 17 00:00:00 2001 From: John Smith Date: Tue, 4 Jul 2023 12:35:48 -0400 Subject: [PATCH] protocol level capabilities --- veilid-core/proto/veilid.capnp | 3 +- veilid-core/src/routing_table/bucket_entry.rs | 7 ++ veilid-core/src/routing_table/find_peers.rs | 76 +++++++++----- veilid-core/src/routing_table/mod.rs | 2 +- .../route_spec_store/route_spec_store.rs | 2 +- .../tasks/private_route_management.rs | 2 +- .../routing_table/tasks/relay_management.rs | 40 ++++---- .../src/routing_table/types/node_info.rs | 99 +++++++------------ .../coders/operations/operation_find_node.rs | 45 ++++++++- veilid-core/src/rpc_processor/mod.rs | 9 +- veilid-core/src/rpc_processor/rpc_app_call.rs | 6 +- .../src/rpc_processor/rpc_app_message.rs | 6 +- .../src/rpc_processor/rpc_cancel_tunnel.rs | 6 +- .../src/rpc_processor/rpc_complete_tunnel.rs | 6 +- .../src/rpc_processor/rpc_find_node.rs | 17 +++- .../src/rpc_processor/rpc_get_value.rs | 4 +- veilid-core/src/rpc_processor/rpc_route.rs | 2 +- .../src/rpc_processor/rpc_set_value.rs | 4 +- veilid-core/src/rpc_processor/rpc_signal.rs | 2 +- .../src/rpc_processor/rpc_start_tunnel.rs | 6 +- .../rpc_processor/rpc_validate_dial_info.rs | 7 +- .../src/rpc_processor/rpc_value_changed.rs | 2 +- .../src/rpc_processor/rpc_watch_value.rs | 2 +- veilid-python/veilid/types.py | 4 +- 24 files changed, 219 insertions(+), 140 deletions(-) diff --git a/veilid-core/proto/veilid.capnp b/veilid-core/proto/veilid.capnp index 9aef1568..8fabe0f7 100644 --- a/veilid-core/proto/veilid.capnp +++ b/veilid-core/proto/veilid.capnp @@ -273,7 +273,8 @@ struct OperationReturnReceipt @0xeb0fb5b5a9160eeb { } struct OperationFindNodeQ @0xfdef788fe9623bcd { - nodeId @0 :TypedKey; # node id to locate + nodeId @0 :TypedKey; # node id to locate + capabilities @1 :List(Capability); # required capabilities returned peers must have } struct OperationFindNodeA @0xa84cf2fb40c77089 { diff --git a/veilid-core/src/routing_table/bucket_entry.rs b/veilid-core/src/routing_table/bucket_entry.rs index a4d197d2..f0f6df94 100644 --- a/veilid-core/src/routing_table/bucket_entry.rs +++ b/veilid-core/src/routing_table/bucket_entry.rs @@ -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 { diff --git a/veilid-core/src/routing_table/find_peers.rs b/veilid-core/src/routing_table/find_peers.rs index e7f289ea..52787196 100644 --- a/veilid-core/src/routing_table/find_peers.rs +++ b/veilid-core/src/routing_table/find_peers.rs @@ -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> { + pub fn find_all_closest_peers( + &self, + key: TypedKey, + capabilities: &[Capability], + ) -> NetworkResult> { 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>| { // 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> { + /// Can filter based on a particular set of capabiltiies + pub fn find_peers_closer_to_key( + &self, + key: TypedKey, + required_capabilities: Vec, + ) -> NetworkResult> { // 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]); diff --git a/veilid-core/src/routing_table/mod.rs b/veilid-core/src/routing_table/mod.rs index 618faa77..2d6c29d9 100644 --- a/veilid-core/src/routing_table/mod.rs +++ b/veilid-core/src/routing_table/mod.rs @@ -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? ); diff --git a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs index 0ebeb351..f1ae27c4 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs @@ -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) }) }, diff --git a/veilid-core/src/routing_table/tasks/private_route_management.rs b/veilid-core/src/routing_table/tasks/private_route_management.rs index 1abdd51b..715c25aa 100644 --- a/veilid-core/src/routing_table/tasks/private_route_management.rs +++ b/veilid-core/src/routing_table/tasks/private_route_management.rs @@ -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 diff --git a/veilid-core/src/routing_table/tasks/relay_management.rs b/veilid-core/src/routing_table/tasks/relay_management.rs index 543fdffd..960c5bc9 100644 --- a/veilid-core/src/routing_table/tasks/relay_management.rs +++ b/veilid-core/src/routing_table/tasks/relay_management.rs @@ -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); } } }); diff --git a/veilid-core/src/routing_table/types/node_info.rs b/veilid-core/src/routing_table/types/node_info.rs index 067d2fed..96c40637 100644 --- a/veilid-core/src/routing_table/types/node_info.rs +++ b/veilid-core/src/routing_table/types/node_info.rs @@ -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) - } } diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs b/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs index 607dacbc..10848648 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_find_node.rs @@ -5,11 +5,15 @@ const MAX_FIND_NODE_A_PEERS_LEN: usize = 20; #[derive(Debug, Clone)] pub struct RPCOperationFindNodeQ { node_id: TypedKey, + capabilities: Vec, } impl RPCOperationFindNodeQ { - pub fn new(node_id: TypedKey) -> Self { - Self { node_id } + pub fn new(node_id: TypedKey, capabilities: Vec) -> Self { + Self { + node_id, + capabilities, + } } pub fn validate(&mut self, _validate_context: &RPCValidateContext) -> Result<(), RPCError> { Ok(()) @@ -18,15 +22,33 @@ impl RPCOperationFindNodeQ { // pub fn node_id(&self) -> &TypedKey { // &self.node_id // } + // pub fn capabilities(&self) -> &[Capability] { + // &self.capabilities + // } - pub fn destructure(self) -> TypedKey { - self.node_id + pub fn destructure(self) -> (TypedKey, Vec) { + (self.node_id, self.capabilities) } pub fn decode(reader: &veilid_capnp::operation_find_node_q::Reader) -> Result { let ni_reader = reader.get_node_id().map_err(RPCError::protocol)?; let node_id = decode_typed_key(&ni_reader)?; - Ok(Self { node_id }) + let cap_reader = reader + .reborrow() + .get_capabilities() + .map_err(RPCError::protocol)?; + if cap_reader.len() as usize > MAX_CAPABILITIES { + return Err(RPCError::protocol("too many capabilities")); + } + let capabilities = cap_reader + .as_slice() + .map(|s| s.iter().map(|x| FourCC::from(x.to_be_bytes())).collect()) + .unwrap_or_default(); + + Ok(Self { + node_id, + capabilities, + }) } pub fn encode( &self, @@ -34,6 +56,19 @@ impl RPCOperationFindNodeQ { ) -> Result<(), RPCError> { let mut ni_builder = builder.reborrow().init_node_id(); encode_typed_key(&self.node_id, &mut ni_builder); + + let mut cap_builder = builder + .reborrow() + .init_capabilities(self.capabilities.len() as u32); + if let Some(s) = cap_builder.as_slice() { + let capvec: Vec = self + .capabilities + .iter() + .map(|x| u32::from_be_bytes(x.0)) + .collect(); + + s.clone_from_slice(&capvec); + } Ok(()) } } diff --git a/veilid-core/src/rpc_processor/mod.rs b/veilid-core/src/rpc_processor/mod.rs index f9c9a504..06931555 100644 --- a/veilid-core/src/rpc_processor/mod.rs +++ b/veilid-core/src/rpc_processor/mod.rs @@ -442,9 +442,11 @@ impl RPCProcessor { &self, routing_domain: RoutingDomain, signed_node_info: &SignedNodeInfo, + capabilities: &[Capability], ) -> bool { let routing_table = self.routing_table(); routing_table.signed_node_info_is_valid_in_routing_domain(routing_domain, &signed_node_info) + && signed_node_info.node_info().has_capabilities(capabilities) } ////////////////////////////////////////////////////////////////////// @@ -470,6 +472,7 @@ impl RPCProcessor { .rpc_call_find_node( Destination::direct(next_node).with_safety(safety_selection), node_id, + vec![], ) .await { @@ -1474,7 +1477,11 @@ impl RPCProcessor { // Ensure the sender peer info is for the actual sender specified in the envelope // Sender PeerInfo was specified, update our routing table with it - if !self.verify_node_info(routing_domain, sender_peer_info.signed_node_info()) { + if !self.verify_node_info( + routing_domain, + sender_peer_info.signed_node_info(), + &[], + ) { return Ok(NetworkResult::invalid_message( "sender peerinfo has invalid peer scope", )); diff --git a/veilid-core/src/rpc_processor/rpc_app_call.rs b/veilid-core/src/rpc_processor/rpc_app_call.rs index f73762fb..4b54452a 100644 --- a/veilid-core/src/rpc_processor/rpc_app_call.rs +++ b/veilid-core/src/rpc_processor/rpc_app_call.rs @@ -57,7 +57,11 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_appmessage() { + if !opi + .signed_node_info() + .node_info() + .has_capability(CAP_APPMESSAGE) + { return Ok(NetworkResult::service_unavailable( "app call is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_app_message.rs b/veilid-core/src/rpc_processor/rpc_app_message.rs index 891d8a2e..56800099 100644 --- a/veilid-core/src/rpc_processor/rpc_app_message.rs +++ b/veilid-core/src/rpc_processor/rpc_app_message.rs @@ -28,7 +28,11 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_appmessage() { + if !opi + .signed_node_info() + .node_info() + .has_capability(CAP_APPMESSAGE) + { return Ok(NetworkResult::service_unavailable( "app message is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs b/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs index 6001eb49..2e761488 100644 --- a/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs @@ -12,7 +12,11 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_tunnel() { + if !opi + .signed_node_info() + .node_info() + .has_capability(CAP_TUNNEL) + { return Ok(NetworkResult::service_unavailable( "tunnel is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs b/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs index 1b442894..c13088c1 100644 --- a/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs @@ -12,7 +12,11 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_tunnel() { + if !opi + .signed_node_info() + .node_info() + .has_capability(CAP_TUNNEL) + { return Ok(NetworkResult::service_unavailable( "tunnel is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_find_node.rs b/veilid-core/src/rpc_processor/rpc_find_node.rs index afd05e18..6bbcf5fa 100644 --- a/veilid-core/src/rpc_processor/rpc_find_node.rs +++ b/veilid-core/src/rpc_processor/rpc_find_node.rs @@ -15,6 +15,7 @@ impl RPCProcessor { self, dest: Destination, node_id: TypedKey, + capabilities: Vec, ) -> Result>>, RPCError> { // Ensure destination never has a private route if matches!( @@ -29,7 +30,8 @@ impl RPCProcessor { )); } - let find_node_q_detail = RPCQuestionDetail::FindNodeQ(RPCOperationFindNodeQ::new(node_id)); + let find_node_q_detail = + RPCQuestionDetail::FindNodeQ(RPCOperationFindNodeQ::new(node_id, capabilities.clone())); let find_node_q = RPCQuestion::new( network_result_try!(self.get_destination_respond_to(&dest)?), find_node_q_detail, @@ -60,9 +62,13 @@ impl RPCProcessor { let peers = find_node_a.destructure(); for peer_info in &peers { - if !self.verify_node_info(RoutingDomain::PublicInternet, peer_info.signed_node_info()) { + if !self.verify_node_info( + RoutingDomain::PublicInternet, + peer_info.signed_node_info(), + &capabilities, + ) { return Ok(NetworkResult::invalid_message( - "find_node response has invalid peer scope", + "find_node response does not meet peer criteria", )); } } @@ -94,11 +100,12 @@ impl RPCProcessor { }, _ => panic!("not a question"), }; - let node_id = find_node_q.destructure(); + let (node_id, capabilities) = find_node_q.destructure(); // Get a chunk of the routing table near the requested node id let routing_table = self.routing_table(); - let closest_nodes = network_result_try!(routing_table.find_all_closest_peers(node_id)); + let closest_nodes = + network_result_try!(routing_table.find_all_closest_peers(node_id, &capabilities)); // Make FindNode answer let find_node_a = RPCOperationFindNodeA::new(closest_nodes)?; diff --git a/veilid-core/src/rpc_processor/rpc_get_value.rs b/veilid-core/src/rpc_processor/rpc_get_value.rs index bab14d2d..085bb0bc 100644 --- a/veilid-core/src/rpc_processor/rpc_get_value.rs +++ b/veilid-core/src/rpc_processor/rpc_get_value.rs @@ -177,7 +177,7 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_dht() { + if !opi.signed_node_info().node_info().has_capability(CAP_DHT) { return Ok(NetworkResult::service_unavailable( "dht is not available", )); @@ -199,7 +199,7 @@ impl RPCProcessor { // Get the nodes that we know about that are closer to the the key than our own node let routing_table = self.routing_table(); - let closer_to_key_peers = network_result_try!(routing_table.find_peers_closer_to_key(key)); + let closer_to_key_peers = network_result_try!(routing_table.find_peers_closer_to_key(key, vec![CAP_DHT])); let debug_string = format!( "IN <=== GetValueQ({} #{}{}) <== {}", diff --git a/veilid-core/src/rpc_processor/rpc_route.rs b/veilid-core/src/rpc_processor/rpc_route.rs index ec2337fd..0c1b14be 100644 --- a/veilid-core/src/rpc_processor/rpc_route.rs +++ b/veilid-core/src/rpc_processor/rpc_route.rs @@ -369,7 +369,7 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_route() { + if !opi.signed_node_info().node_info().has_capability(CAP_ROUTE) { return Ok(NetworkResult::service_unavailable( "route is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_set_value.rs b/veilid-core/src/rpc_processor/rpc_set_value.rs index b83c03c1..9973b5ef 100644 --- a/veilid-core/src/rpc_processor/rpc_set_value.rs +++ b/veilid-core/src/rpc_processor/rpc_set_value.rs @@ -179,7 +179,7 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_dht() { + if !opi.signed_node_info().node_info().has_capability(CAP_DHT) { return Ok(NetworkResult::service_unavailable( "dht is not available", )); @@ -211,7 +211,7 @@ impl RPCProcessor { // Get the nodes that we know about that are closer to the the key than our own node let routing_table = self.routing_table(); - let closer_to_key_peers = network_result_try!(routing_table.find_peers_closer_to_key(key)); + let closer_to_key_peers = network_result_try!(routing_table.find_peers_closer_to_key(key, vec![CAP_DHT])); let debug_string = format!( "IN <=== SetValueQ({} #{} len={} seq={} writer={}{}) <== {}", diff --git a/veilid-core/src/rpc_processor/rpc_signal.rs b/veilid-core/src/rpc_processor/rpc_signal.rs index 542649ac..4e32952a 100644 --- a/veilid-core/src/rpc_processor/rpc_signal.rs +++ b/veilid-core/src/rpc_processor/rpc_signal.rs @@ -41,7 +41,7 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_signal() { + if !opi.signed_node_info().node_info().is_signal_capable() { return Ok(NetworkResult::service_unavailable( "signal is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_start_tunnel.rs b/veilid-core/src/rpc_processor/rpc_start_tunnel.rs index d4608cbe..972ba67b 100644 --- a/veilid-core/src/rpc_processor/rpc_start_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_start_tunnel.rs @@ -12,7 +12,11 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_tunnel() { + if !opi + .signed_node_info() + .node_info() + .has_capability(CAP_TUNNEL) + { return Ok(NetworkResult::service_unavailable( "tunnel is not available", )); diff --git a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs index 9a4a9f64..16598b31 100644 --- a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs +++ b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs @@ -71,7 +71,8 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(detail.routing_domain) { - if !opi.signed_node_info().node_info().can_validate_dial_info() { + let ni = opi.signed_node_info().node_info(); + if !ni.has_capability(CAP_VALIDATE_DIAL_INFO) || !ni.is_signal_capable() { return Ok(NetworkResult::service_unavailable( "validate dial info is not available", )); @@ -116,7 +117,9 @@ impl RPCProcessor { let entry = v.unwrap(); entry.with(rti, move |_rti, e| { e.node_info(routing_domain) - .map(|ni| ni.can_validate_dial_info()) + .map(|ni| { + ni.has_capability(CAP_VALIDATE_DIAL_INFO) && ni.is_signal_capable() + }) .unwrap_or(false) }) }, diff --git a/veilid-core/src/rpc_processor/rpc_value_changed.rs b/veilid-core/src/rpc_processor/rpc_value_changed.rs index 94a0bfb4..fd5b1910 100644 --- a/veilid-core/src/rpc_processor/rpc_value_changed.rs +++ b/veilid-core/src/rpc_processor/rpc_value_changed.rs @@ -10,7 +10,7 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_dht() { + if !opi.signed_node_info().node_info().has_capability(CAP_DHT) { return Ok(NetworkResult::service_unavailable("dht is not available")); } } diff --git a/veilid-core/src/rpc_processor/rpc_watch_value.rs b/veilid-core/src/rpc_processor/rpc_watch_value.rs index 53179744..e6f28474 100644 --- a/veilid-core/src/rpc_processor/rpc_watch_value.rs +++ b/veilid-core/src/rpc_processor/rpc_watch_value.rs @@ -10,7 +10,7 @@ impl RPCProcessor { let routing_table = self.routing_table(); { if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) { - if !opi.signed_node_info().node_info().can_dht() { + if !opi.signed_node_info().node_info().has_capability(CAP_DHT) { return Ok(NetworkResult::service_unavailable("dht is not available")); } } diff --git a/veilid-python/veilid/types.py b/veilid-python/veilid/types.py index 5d7bc6a0..eef8cd1d 100644 --- a/veilid-python/veilid/types.py +++ b/veilid-python/veilid/types.py @@ -54,13 +54,13 @@ class CryptoKind(StrEnum): class Capability(StrEnum): CAP_WILL_ROUTE = "ROUT" - CAP_WILL_TUNNEL = "TUNL" + CAP_TUNNEL = "TUNL" CAP_WILL_SIGNAL = "SGNL" CAP_WILL_RELAY = "RLAY" CAP_WILL_VALIDATE_DIAL_INFO = "DIAL" CAP_WILL_DHT = "DHTV" CAP_WILL_APPMESSAGE = "APPM" - CAP_WILL_BLOCKSTORE = "BLOC" + CAP_BLOCKSTORE = "BLOC" class Stability(StrEnum):