more capability work

This commit is contained in:
John Smith 2023-07-04 00:24:55 -04:00
parent e674eaf496
commit 8f721c428b
33 changed files with 456 additions and 474 deletions

View File

@ -190,19 +190,8 @@ struct DialInfoDetail @0x96423aa1d67b74d8 {
class @1 :DialInfoClass; class @1 :DialInfoClass;
} }
struct PublicInternetNodeStatus @0x9c9d7f1f12eb088f {
capabilities @0 :List(Capability); # List of Capability FOURCC codes that this node is advertising it is capable of in the publicinternet routing domain
}
struct LocalNetworkNodeStatus @0x957f5bfed2d0b5a5 {
capabilities @0 :List(Capability); # List of Capability FOURCC codes that this node is advertising it is capable of in the localnetwork routing domain
}
struct NodeStatus @0xd36b9e7a3bf3330d { struct NodeStatus @0xd36b9e7a3bf3330d {
union { # Reserved for non-nodeinfo status
publicInternet @0 :PublicInternetNodeStatus;
localNetwork @1 :LocalNetworkNodeStatus;
}
} }
struct ProtocolTypeSet @0x82f12f55a1b73326 { struct ProtocolTypeSet @0x82f12f55a1b73326 {
@ -227,7 +216,8 @@ struct NodeInfo @0xe125d847e3f9f419 {
addressTypes @2 :AddressTypeSet; # address types supported addressTypes @2 :AddressTypeSet; # address types supported
envelopeSupport @3 :List(UInt8); # supported rpc envelope/receipt versions envelopeSupport @3 :List(UInt8); # supported rpc envelope/receipt versions
cryptoSupport @4 :List(CryptoKind); # cryptography systems supported cryptoSupport @4 :List(CryptoKind); # cryptography systems supported
dialInfoDetailList @5 :List(DialInfoDetail); # inbound dial info details for this node capabilities @5 :List(Capability); # capabilities supported by the node
dialInfoDetailList @6 :List(DialInfoDetail); # inbound dial info details for this node
} }
struct SignedDirectNodeInfo @0xe0e7ea3e893a3dd7 { struct SignedDirectNodeInfo @0xe0e7ea3e893a3dd7 {
@ -283,7 +273,7 @@ struct OperationReturnReceipt @0xeb0fb5b5a9160eeb {
} }
struct OperationFindNodeQ @0xfdef788fe9623bcd { struct OperationFindNodeQ @0xfdef788fe9623bcd {
nodeId @0 :TypedKey; # node id to locate nodeId @0 :TypedKey; # node id to locate
} }
struct OperationFindNodeA @0xa84cf2fb40c77089 { struct OperationFindNodeA @0xa84cf2fb40c77089 {

View File

@ -5,31 +5,31 @@ mod native;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
mod wasm; mod wasm;
mod direct_boot;
mod send_data;
mod connection_handle;
mod address_filter; mod address_filter;
mod connection_handle;
mod connection_manager; mod connection_manager;
mod connection_table; mod connection_table;
mod direct_boot;
mod network_connection; mod network_connection;
mod send_data;
mod stats;
mod tasks; mod tasks;
mod types; mod types;
mod stats;
pub mod tests; pub mod tests;
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
pub use connection_manager::*; pub use connection_manager::*;
pub use network_connection::*;
pub use types::*;
pub use send_data::*;
pub use direct_boot::*; pub use direct_boot::*;
pub use network_connection::*;
pub use send_data::*;
pub use stats::*; pub use stats::*;
pub use types::*;
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
use connection_handle::*;
use address_filter::*; use address_filter::*;
use connection_handle::*;
use crypto::*; use crypto::*;
use futures_util::stream::FuturesUnordered; use futures_util::stream::FuturesUnordered;
use hashlink::LruCache; use hashlink::LruCache;
@ -47,13 +47,16 @@ use wasm::*;
pub const MAX_MESSAGE_SIZE: usize = MAX_ENVELOPE_SIZE; pub const MAX_MESSAGE_SIZE: usize = MAX_ENVELOPE_SIZE;
pub const IPADDR_TABLE_SIZE: usize = 1024; pub const IPADDR_TABLE_SIZE: usize = 1024;
pub const IPADDR_MAX_INACTIVE_DURATION_US: TimestampDuration = TimestampDuration::new(300_000_000u64); // 5 minutes pub const IPADDR_MAX_INACTIVE_DURATION_US: TimestampDuration =
TimestampDuration::new(300_000_000u64); // 5 minutes
pub const NODE_CONTACT_METHOD_CACHE_SIZE: usize = 1024; pub const NODE_CONTACT_METHOD_CACHE_SIZE: usize = 1024;
pub const PUBLIC_ADDRESS_CHANGE_DETECTION_COUNT: usize = 3; pub const PUBLIC_ADDRESS_CHANGE_DETECTION_COUNT: usize = 3;
pub const PUBLIC_ADDRESS_CHECK_CACHE_SIZE: usize = 8; pub const PUBLIC_ADDRESS_CHECK_CACHE_SIZE: usize = 8;
pub const PUBLIC_ADDRESS_CHECK_TASK_INTERVAL_SECS: u32 = 60; pub const PUBLIC_ADDRESS_CHECK_TASK_INTERVAL_SECS: u32 = 60;
pub const PUBLIC_ADDRESS_INCONSISTENCY_TIMEOUT_US: TimestampDuration = TimestampDuration::new(300_000_000u64); // 5 minutes pub const PUBLIC_ADDRESS_INCONSISTENCY_TIMEOUT_US: TimestampDuration =
pub const PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US: TimestampDuration = TimestampDuration::new(3600_000_000u64); // 60 minutes TimestampDuration::new(300_000_000u64); // 5 minutes
pub const PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US: TimestampDuration =
TimestampDuration::new(3600_000_000u64); // 60 minutes
pub const ADDRESS_FILTER_TASK_INTERVAL_SECS: u32 = 60; pub const ADDRESS_FILTER_TASK_INTERVAL_SECS: u32 = 60;
pub const BOOT_MAGIC: &[u8; 4] = b"BOOT"; pub const BOOT_MAGIC: &[u8; 4] = b"BOOT";
@ -75,7 +78,6 @@ struct NetworkComponents {
receipt_manager: ReceiptManager, receipt_manager: ReceiptManager,
} }
#[derive(Debug)] #[derive(Debug)]
struct ClientWhitelistEntry { struct ClientWhitelistEntry {
last_seen_ts: Timestamp, last_seen_ts: Timestamp,
@ -134,7 +136,7 @@ struct NetworkManagerUnlockedInner {
storage_manager: StorageManager, storage_manager: StorageManager,
protected_store: ProtectedStore, protected_store: ProtectedStore,
table_store: TableStore, table_store: TableStore,
#[cfg(feature="unstable-blockstore")] #[cfg(feature = "unstable-blockstore")]
block_store: BlockStore, block_store: BlockStore,
crypto: Crypto, crypto: Crypto,
address_filter: AddressFilter, address_filter: AddressFilter,
@ -171,8 +173,7 @@ impl NetworkManager {
storage_manager: StorageManager, storage_manager: StorageManager,
protected_store: ProtectedStore, protected_store: ProtectedStore,
table_store: TableStore, table_store: TableStore,
#[cfg(feature="unstable-blockstore")] #[cfg(feature = "unstable-blockstore")] block_store: BlockStore,
block_store: BlockStore,
crypto: Crypto, crypto: Crypto,
network_key: Option<SharedSecret>, network_key: Option<SharedSecret>,
) -> NetworkManagerUnlockedInner { ) -> NetworkManagerUnlockedInner {
@ -181,7 +182,7 @@ impl NetworkManager {
storage_manager, storage_manager,
protected_store, protected_store,
table_store, table_store,
#[cfg(feature="unstable-blockstore")] #[cfg(feature = "unstable-blockstore")]
block_store, block_store,
crypto, crypto,
address_filter: AddressFilter::new(config), address_filter: AddressFilter::new(config),
@ -200,18 +201,20 @@ impl NetworkManager {
storage_manager: StorageManager, storage_manager: StorageManager,
protected_store: ProtectedStore, protected_store: ProtectedStore,
table_store: TableStore, table_store: TableStore,
#[cfg(feature="unstable-blockstore")] #[cfg(feature = "unstable-blockstore")] block_store: BlockStore,
block_store: BlockStore,
crypto: Crypto, crypto: Crypto,
) -> Self { ) -> Self {
// Make the network key // Make the network key
let network_key = { let network_key = {
let c = config.get(); let c = config.get();
let network_key_password = if let Some(nkp) = c.network.network_key_password.clone() { let network_key_password = if let Some(nkp) = c.network.network_key_password.clone() {
Some(nkp) Some(nkp)
} else { } else {
if c.network.routing_table.bootstrap.contains(&"bootstrap.veilid.net".to_owned()) { if c.network
.routing_table
.bootstrap
.contains(&"bootstrap.veilid.net".to_owned())
{
None None
} else { } else {
Some(c.network.routing_table.bootstrap.join(",")) Some(c.network.routing_table.bootstrap.join(","))
@ -224,7 +227,13 @@ impl NetworkManager {
let bcs = crypto.best(); let bcs = crypto.best();
// Yes the use of the salt this way is generally bad, but this just needs to be hashed // Yes the use of the salt this way is generally bad, but this just needs to be hashed
Some(bcs.derive_shared_secret(network_key_password.as_bytes(), network_key_password.as_bytes()).expect("failed to derive network key")) Some(
bcs.derive_shared_secret(
network_key_password.as_bytes(),
network_key_password.as_bytes(),
)
.expect("failed to derive network key"),
)
} else { } else {
None None
} }
@ -242,7 +251,7 @@ impl NetworkManager {
storage_manager, storage_manager,
protected_store, protected_store,
table_store, table_store,
#[cfg(feature="unstable-blockstore")] #[cfg(feature = "unstable-blockstore")]
block_store, block_store,
crypto, crypto,
network_key, network_key,
@ -271,7 +280,7 @@ impl NetworkManager {
pub fn table_store(&self) -> TableStore { pub fn table_store(&self) -> TableStore {
self.unlocked_inner.table_store.clone() self.unlocked_inner.table_store.clone()
} }
#[cfg(feature="unstable-blockstore")] #[cfg(feature = "unstable-blockstore")]
pub fn block_store(&self) -> BlockStore { pub fn block_store(&self) -> BlockStore {
self.unlocked_inner.block_store.clone() self.unlocked_inner.block_store.clone()
} }
@ -443,7 +452,7 @@ impl NetworkManager {
pub fn update_client_whitelist(&self, client: TypedKey) { pub fn update_client_whitelist(&self, client: TypedKey) {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
match inner.client_whitelist.entry(client, |_k,_v| { match inner.client_whitelist.entry(client, |_k, _v| {
// do nothing on LRU evict // do nothing on LRU evict
}) { }) {
hashlink::lru_cache::Entry::Occupied(mut entry) => { hashlink::lru_cache::Entry::Occupied(mut entry) => {
@ -461,7 +470,7 @@ impl NetworkManager {
pub fn check_client_whitelist(&self, client: TypedKey) -> bool { pub fn check_client_whitelist(&self, client: TypedKey) -> bool {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
match inner.client_whitelist.entry(client, |_k,_v| { match inner.client_whitelist.entry(client, |_k, _v| {
// do nothing on LRU evict // do nothing on LRU evict
}) { }) {
hashlink::lru_cache::Entry::Occupied(mut entry) => { hashlink::lru_cache::Entry::Occupied(mut entry) => {
@ -475,7 +484,8 @@ impl NetworkManager {
pub fn purge_client_whitelist(&self) { pub fn purge_client_whitelist(&self) {
let timeout_ms = self.with_config(|c| c.network.client_whitelist_timeout_ms); let timeout_ms = self.with_config(|c| c.network.client_whitelist_timeout_ms);
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
let cutoff_timestamp = get_aligned_timestamp() - TimestampDuration::new((timeout_ms as u64) * 1000u64); let cutoff_timestamp =
get_aligned_timestamp() - TimestampDuration::new((timeout_ms as u64) * 1000u64);
// Remove clients from the whitelist that haven't been since since our whitelist timeout // Remove clients from the whitelist that haven't been since since our whitelist timeout
while inner while inner
.client_whitelist .client_whitelist
@ -493,95 +503,8 @@ impl NetworkManager {
net.needs_restart() net.needs_restart()
} }
/// Get our node's capabilities in the PublicInternet routing domain pub fn generate_node_status(&self, _routing_domain: RoutingDomain) -> NodeStatus {
fn generate_public_internet_node_status(&self) -> PublicInternetNodeStatus { NodeStatus {}
let Some(own_peer_info) = self
.routing_table()
.get_own_peer_info(RoutingDomain::PublicInternet) else {
return PublicInternetNodeStatus::default();
};
let own_node_info = own_peer_info.signed_node_info().node_info();
let config = self.config();
let c = config.get();
let will_route = own_node_info.can_inbound_relay(); // xxx: eventually this may have more criteria added
let will_tunnel = own_node_info.can_inbound_relay(); // xxx: we may want to restrict by battery life and network bandwidth at some point
let will_signal = own_node_info.can_signal();
let will_relay = own_node_info.can_inbound_relay();
let will_validate_dial_info = own_node_info.can_validate_dial_info();
let mut capabilities = Vec::new();
if will_route && !c.capabilities.disable.contains(&CAP_WILL_ROUTE) {
capabilities.push(CAP_WILL_ROUTE);
}
if will_tunnel && !c.capabilities.disable.contains(&CAP_WILL_TUNNEL) {
capabilities.push(CAP_WILL_TUNNEL);
}
if will_signal && !c.capabilities.disable.contains(&CAP_WILL_SIGNAL) {
capabilities.push(CAP_WILL_SIGNAL);
}
if will_relay && !c.capabilities.disable.contains(&CAP_WILL_RELAY){
capabilities.push(CAP_WILL_RELAY);
}
if will_validate_dial_info && !c.capabilities.disable.contains(&CAP_WILL_VALIDATE_DIAL_INFO) {
capabilities.push(CAP_WILL_VALIDATE_DIAL_INFO);
}
if !c.capabilities.disable.contains(&CAP_WILL_DHT) {
capabilities.push(CAP_WILL_DHT);
}
if !c.capabilities.disable.contains(&CAP_WILL_APPMESSAGE) {
capabilities.push(CAP_WILL_APPMESSAGE);
}
PublicInternetNodeStatus {
capabilities
}
}
/// Get our node's capabilities in the LocalNetwork routing domain
fn generate_local_network_node_status(&self) -> LocalNetworkNodeStatus {
let Some(own_peer_info) = self
.routing_table()
.get_own_peer_info(RoutingDomain::LocalNetwork) else {
return LocalNetworkNodeStatus::default();
};
let own_node_info = own_peer_info.signed_node_info().node_info();
let config = self.config();
let c = config.get();
let will_relay = own_node_info.can_inbound_relay();
let will_validate_dial_info = own_node_info.can_validate_dial_info();
let mut capabilities = Vec::new();
if will_relay && !c.capabilities.disable.contains(&CAP_WILL_RELAY) {
capabilities.push(CAP_WILL_RELAY);
}
if will_validate_dial_info && !c.capabilities.disable.contains(&CAP_WILL_VALIDATE_DIAL_INFO) {
capabilities.push(CAP_WILL_VALIDATE_DIAL_INFO);
}
if !c.capabilities.disable.contains(&CAP_WILL_DHT) {
capabilities.push(CAP_WILL_DHT);
}
if !c.capabilities.disable.contains(&CAP_WILL_APPMESSAGE) {
capabilities.push(CAP_WILL_APPMESSAGE);
}
LocalNetworkNodeStatus {
capabilities
}
}
pub fn generate_node_status(&self, routing_domain: RoutingDomain) -> NodeStatus {
match routing_domain {
RoutingDomain::PublicInternet => {
NodeStatus::PublicInternet(self.generate_public_internet_node_status())
}
RoutingDomain::LocalNetwork => {
NodeStatus::LocalNetwork(self.generate_local_network_node_status())
}
}
} }
/// Generates a multi-shot/normal receipt /// Generates a multi-shot/normal receipt
@ -598,12 +521,18 @@ impl NetworkManager {
// Generate receipt and serialized form to return // Generate receipt and serialized form to return
let vcrypto = self.crypto().best(); let vcrypto = self.crypto().best();
let nonce = vcrypto.random_nonce(); let nonce = vcrypto.random_nonce();
let node_id = routing_table.node_id(vcrypto.kind()); let node_id = routing_table.node_id(vcrypto.kind());
let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind()); let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind());
let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.value, extra_data)?; let receipt = Receipt::try_new(
best_envelope_version(),
node_id.kind,
nonce,
node_id.value,
extra_data,
)?;
let out = receipt let out = receipt
.to_signed_data(self.crypto(), &node_id_secret) .to_signed_data(self.crypto(), &node_id_secret)
.wrap_err("failed to generate signed receipt")?; .wrap_err("failed to generate signed receipt")?;
@ -627,12 +556,18 @@ impl NetworkManager {
// Generate receipt and serialized form to return // Generate receipt and serialized form to return
let vcrypto = self.crypto().best(); let vcrypto = self.crypto().best();
let nonce = vcrypto.random_nonce(); let nonce = vcrypto.random_nonce();
let node_id = routing_table.node_id(vcrypto.kind()); let node_id = routing_table.node_id(vcrypto.kind());
let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind()); let node_id_secret = routing_table.node_id_secret_key(vcrypto.kind());
let receipt = Receipt::try_new(best_envelope_version(), node_id.kind, nonce, node_id.value, extra_data)?; let receipt = Receipt::try_new(
best_envelope_version(),
node_id.kind,
nonce,
node_id.value,
extra_data,
)?;
let out = receipt let out = receipt
.to_signed_data(self.crypto(), &node_id_secret) .to_signed_data(self.crypto(), &node_id_secret)
.wrap_err("failed to generate signed receipt")?; .wrap_err("failed to generate signed receipt")?;
@ -744,9 +679,10 @@ impl NetworkManager {
) { ) {
Ok(nr) => nr, Ok(nr) => nr,
Err(e) => { Err(e) => {
return Ok(NetworkResult::invalid_message( return Ok(NetworkResult::invalid_message(format!(
format!("unable to register reverse connect peerinfo: {}", e) "unable to register reverse connect peerinfo: {}",
)); e
)));
} }
}; };
@ -767,9 +703,10 @@ impl NetworkManager {
) { ) {
Ok(nr) => nr, Ok(nr) => nr,
Err(e) => { Err(e) => {
return Ok(NetworkResult::invalid_message( return Ok(NetworkResult::invalid_message(format!(
format!("unable to register hole punch connect peerinfo: {}", e) "unable to register hole punch connect peerinfo: {}",
)); e
)));
} }
}; };
@ -813,7 +750,10 @@ impl NetworkManager {
} }
/// Builds an envelope for sending over the network /// Builds an envelope for sending over the network
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, body), err))] #[cfg_attr(
feature = "verbose-tracing",
instrument(level = "trace", skip(self, body), err)
)]
fn build_envelope<B: AsRef<[u8]>>( fn build_envelope<B: AsRef<[u8]>>(
&self, &self,
dest_node_id: TypedKey, dest_node_id: TypedKey,
@ -834,9 +774,21 @@ impl NetworkManager {
let nonce = vcrypto.random_nonce(); let nonce = vcrypto.random_nonce();
// Encode envelope // Encode envelope
let envelope = Envelope::new(version, node_id.kind, ts, nonce, node_id.value, dest_node_id.value); let envelope = Envelope::new(
version,
node_id.kind,
ts,
nonce,
node_id.value,
dest_node_id.value,
);
envelope envelope
.to_encrypted_data(self.crypto(), body.as_ref(), &node_id_secret, &self.unlocked_inner.network_key) .to_encrypted_data(
self.crypto(),
body.as_ref(),
&node_id_secret,
&self.unlocked_inner.network_key,
)
.wrap_err("envelope failed to encode") .wrap_err("envelope failed to encode")
} }
@ -844,18 +796,20 @@ impl NetworkManager {
/// node_ref is the direct destination to which the envelope will be sent /// node_ref is the direct destination to which the envelope will be sent
/// If 'destination_node_ref' is specified, it can be different than the node_ref being sent to /// If 'destination_node_ref' is specified, it can be different than the node_ref being sent to
/// which will cause the envelope to be relayed /// which will cause the envelope to be relayed
#[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, body), ret, err))] #[cfg_attr(
feature = "verbose-tracing",
instrument(level = "trace", skip(self, body), ret, err)
)]
pub async fn send_envelope<B: AsRef<[u8]>>( pub async fn send_envelope<B: AsRef<[u8]>>(
&self, &self,
node_ref: NodeRef, node_ref: NodeRef,
destination_node_ref: Option<NodeRef>, destination_node_ref: Option<NodeRef>,
body: B, body: B,
) -> EyreResult<NetworkResult<SendDataKind>> { ) -> EyreResult<NetworkResult<SendDataKind>> {
let destination_node_ref = destination_node_ref.as_ref().unwrap_or(&node_ref).clone(); let destination_node_ref = destination_node_ref.as_ref().unwrap_or(&node_ref).clone();
if !node_ref.same_entry(&destination_node_ref) { if !node_ref.same_entry(&destination_node_ref) {
log_net!( log_net!(
"sending envelope to {:?} via {:?}", "sending envelope to {:?} via {:?}",
destination_node_ref, destination_node_ref,
node_ref node_ref
@ -915,7 +869,7 @@ impl NetworkManager {
data: &mut [u8], data: &mut [u8],
connection_descriptor: ConnectionDescriptor, connection_descriptor: ConnectionDescriptor,
) -> EyreResult<bool> { ) -> EyreResult<bool> {
#[cfg(feature="verbose-tracing")] #[cfg(feature = "verbose-tracing")]
let root = span!( let root = span!(
parent: None, parent: None,
Level::TRACE, Level::TRACE,
@ -923,7 +877,7 @@ impl NetworkManager {
"data.len" = data.len(), "data.len" = data.len(),
"descriptor" = ?connection_descriptor "descriptor" = ?connection_descriptor
); );
#[cfg(feature="verbose-tracing")] #[cfg(feature = "verbose-tracing")]
let _root_enter = root.enter(); let _root_enter = root.enter();
log_net!( log_net!(
@ -934,10 +888,7 @@ impl NetworkManager {
let remote_addr = connection_descriptor.remote_address().to_ip_addr(); let remote_addr = connection_descriptor.remote_address().to_ip_addr();
// Network accounting // Network accounting
self.stats_packet_rcvd( self.stats_packet_rcvd(remote_addr, ByteCount::new(data.len() as u64));
remote_addr,
ByteCount::new(data.len() as u64),
);
// If this is a zero length packet, just drop it, because these are used for hole punching // If this is a zero length packet, just drop it, because these are used for hole punching
// and possibly other low-level network connectivity tasks and will never require // and possibly other low-level network connectivity tasks and will never require
@ -978,20 +929,30 @@ impl NetworkManager {
} }
// Decode envelope header (may fail signature validation) // Decode envelope header (may fail signature validation)
let envelope = match Envelope::from_signed_data(self.crypto(), data, &self.unlocked_inner.network_key) { let envelope =
Ok(v) => v, match Envelope::from_signed_data(self.crypto(), data, &self.unlocked_inner.network_key)
Err(e) => { {
log_net!(debug "envelope failed to decode: {}", e); Ok(v) => v,
self.address_filter().punish(remote_addr); Err(e) => {
return Ok(false); log_net!(debug "envelope failed to decode: {}", e);
} self.address_filter().punish(remote_addr);
}; return Ok(false);
}
};
// Get timestamp range // Get timestamp range
let (tsbehind, tsahead) = self.with_config(|c| { let (tsbehind, tsahead) = self.with_config(|c| {
( (
c.network.rpc.max_timestamp_behind_ms.map(ms_to_us).map(TimestampDuration::new), c.network
c.network.rpc.max_timestamp_ahead_ms.map(ms_to_us).map(TimestampDuration::new), .rpc
.max_timestamp_behind_ms
.map(ms_to_us)
.map(TimestampDuration::new),
c.network
.rpc
.max_timestamp_ahead_ms
.map(ms_to_us)
.map(TimestampDuration::new),
) )
}); });
@ -1034,7 +995,10 @@ impl NetworkManager {
let some_relay_nr = if self.check_client_whitelist(sender_id) { let some_relay_nr = if self.check_client_whitelist(sender_id) {
// Full relay allowed, do a full resolve_node // Full relay allowed, do a full resolve_node
match rpc.resolve_node(recipient_id, SafetySelection::Unsafe(Sequencing::default())).await { match rpc
.resolve_node(recipient_id, SafetySelection::Unsafe(Sequencing::default()))
.await
{
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
log_net!(debug "failed to resolve recipient node for relay, dropping outbound relayed packet: {}" ,e); log_net!(debug "failed to resolve recipient node for relay, dropping outbound relayed packet: {}" ,e);
@ -1059,7 +1023,7 @@ impl NetworkManager {
}; };
if let Some(relay_nr) = some_relay_nr { if let Some(relay_nr) = some_relay_nr {
// Force sequencing if this came in sequenced. // Force sequencing if this came in sequenced.
// The sender did the prefer/ensure calculation when it did get_contact_method, // The sender did the prefer/ensure calculation when it did get_contact_method,
// so we don't need to do it here. // so we don't need to do it here.
let relay_nr = if connection_descriptor.remote().protocol_type().is_ordered() { let relay_nr = if connection_descriptor.remote().protocol_type().is_ordered() {
@ -1078,7 +1042,7 @@ impl NetworkManager {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
log_net!(debug "failed to forward envelope: {}" ,e); log_net!(debug "failed to forward envelope: {}" ,e);
return Ok(false); return Ok(false);
} }
} => [ format!(": relay_nr={}, data.len={}", relay_nr, data.len()) ] { } => [ format!(": relay_nr={}, data.len={}", relay_nr, data.len()) ] {
return Ok(false); return Ok(false);
@ -1093,15 +1057,19 @@ impl NetworkManager {
let node_id_secret = routing_table.node_id_secret_key(envelope.get_crypto_kind()); let node_id_secret = routing_table.node_id_secret_key(envelope.get_crypto_kind());
// Decrypt the envelope body // Decrypt the envelope body
let body = match envelope let body = match envelope.decrypt_body(
.decrypt_body(self.crypto(), data, &node_id_secret, &self.unlocked_inner.network_key) { self.crypto(),
Ok(v) => v, data,
Err(e) => { &node_id_secret,
log_net!(debug "failed to decrypt envelope body: {}",e); &self.unlocked_inner.network_key,
self.address_filter().punish(remote_addr); ) {
return Ok(false); Ok(v) => v,
} Err(e) => {
}; log_net!(debug "failed to decrypt envelope body: {}",e);
self.address_filter().punish(remote_addr);
return Ok(false);
}
};
// Cache the envelope information in the routing table // Cache the envelope information in the routing table
let source_noderef = match routing_table.register_node_with_existing_connection( let source_noderef = match routing_table.register_node_with_existing_connection(
@ -1195,7 +1163,7 @@ impl NetworkManager {
if pait.contains_key(&ipblock) { if pait.contains_key(&ipblock) {
return; return;
} }
pacc.insert(ipblock, socket_address, |_k,_v| { pacc.insert(ipblock, socket_address, |_k, _v| {
// do nothing on LRU evict // do nothing on LRU evict
}); });
@ -1253,8 +1221,8 @@ impl NetworkManager {
.public_address_inconsistencies_table .public_address_inconsistencies_table
.entry(key) .entry(key)
.or_insert_with(|| HashMap::new()); .or_insert_with(|| HashMap::new());
let exp_ts = let exp_ts = get_aligned_timestamp()
get_aligned_timestamp() + PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US; + PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US;
for i in inconsistencies { for i in inconsistencies {
pait.insert(i, exp_ts); pait.insert(i, exp_ts);
} }
@ -1325,5 +1293,4 @@ impl NetworkManager {
} }
} }
} }
} }

View File

@ -773,16 +773,34 @@ impl Network {
// set up the routing table's network config // set up the routing table's network config
// if we have static public dialinfo, upgrade our network class // if we have static public dialinfo, upgrade our network class
let public_internet_capabilities = {
let c = self.config.get();
PUBLIC_INTERNET_CAPABILITIES
.iter()
.copied()
.filter(|cap| !c.capabilities.disable.contains(cap))
.collect::<Vec<Capability>>()
};
let local_network_capabilities = {
let c = self.config.get();
LOCAL_NETWORK_CAPABILITIES
.iter()
.copied()
.filter(|cap| !c.capabilities.disable.contains(cap))
.collect::<Vec<Capability>>()
};
editor_public_internet.setup_network( editor_public_internet.setup_network(
protocol_config.outbound, protocol_config.outbound,
protocol_config.inbound, protocol_config.inbound,
protocol_config.family_global, protocol_config.family_global,
public_internet_capabilities,
); );
editor_local_network.setup_network( editor_local_network.setup_network(
protocol_config.outbound, protocol_config.outbound,
protocol_config.inbound, protocol_config.inbound,
protocol_config.family_local, protocol_config.family_local,
local_network_capabilities,
); );
let detect_address_changes = { let detect_address_changes = {
let c = self.config.get(); let c = self.config.get();

View File

@ -20,6 +20,7 @@ pub async fn test_signed_node_info() {
AddressTypeSet::all(), AddressTypeSet::all(),
VALID_ENVELOPE_VERSIONS.to_vec(), VALID_ENVELOPE_VERSIONS.to_vec(),
VALID_CRYPTO_KINDS.to_vec(), VALID_CRYPTO_KINDS.to_vec(),
PUBLIC_INTERNET_CAPABILITIES.to_vec(),
vec![DialInfoDetail { vec![DialInfoDetail {
class: DialInfoClass::Mapped, class: DialInfoClass::Mapped,
dial_info: DialInfo::udp(SocketAddress::default()), dial_info: DialInfo::udp(SocketAddress::default()),
@ -75,6 +76,7 @@ pub async fn test_signed_node_info() {
AddressTypeSet::all(), AddressTypeSet::all(),
VALID_ENVELOPE_VERSIONS.to_vec(), VALID_ENVELOPE_VERSIONS.to_vec(),
VALID_CRYPTO_KINDS.to_vec(), VALID_CRYPTO_KINDS.to_vec(),
PUBLIC_INTERNET_CAPABILITIES.to_vec(),
vec![DialInfoDetail { vec![DialInfoDetail {
class: DialInfoClass::Blocked, class: DialInfoClass::Blocked,
dial_info: DialInfo::udp(SocketAddress::default()), dial_info: DialInfo::udp(SocketAddress::default()),

View File

@ -51,7 +51,7 @@ pub struct BucketEntryPublicInternet {
/// The last node info timestamp of ours that this entry has seen /// The last node info timestamp of ours that this entry has seen
last_seen_our_node_info_ts: Timestamp, last_seen_our_node_info_ts: Timestamp,
/// Last known node status /// Last known node status
node_status: Option<PublicInternetNodeStatus>, node_status: Option<NodeStatus>,
} }
/// Bucket entry information specific to the LocalNetwork RoutingDomain /// Bucket entry information specific to the LocalNetwork RoutingDomain
@ -63,7 +63,7 @@ pub struct BucketEntryLocalNetwork {
/// The last node info timestamp of ours that this entry has seen /// The last node info timestamp of ours that this entry has seen
last_seen_our_node_info_ts: Timestamp, last_seen_our_node_info_ts: Timestamp,
/// Last known node status /// Last known node status
node_status: Option<LocalNetworkNodeStatus>, node_status: Option<NodeStatus>,
} }
/// The data associated with each bucket entry /// The data associated with each bucket entry
@ -502,13 +502,13 @@ impl BucketEntryInner {
&self.peer_stats &self.peer_stats
} }
pub fn update_node_status(&mut self, status: NodeStatus) { pub fn update_node_status(&mut self, routing_domain: RoutingDomain, status: NodeStatus) {
match status { match routing_domain {
NodeStatus::LocalNetwork(ln) => { RoutingDomain::LocalNetwork => {
self.local_network.node_status = Some(ln); self.local_network.node_status = Some(status);
} }
NodeStatus::PublicInternet(pi) => { RoutingDomain::PublicInternet => {
self.public_internet.node_status = Some(pi); self.public_internet.node_status = Some(status);
} }
} }
} }
@ -518,12 +518,12 @@ impl BucketEntryInner {
.local_network .local_network
.node_status .node_status
.as_ref() .as_ref()
.map(|ln| NodeStatus::LocalNetwork(ln.clone())), .map(|ns| ns.clone()),
RoutingDomain::PublicInternet => self RoutingDomain::PublicInternet => self
.public_internet .public_internet
.node_status .node_status
.as_ref() .as_ref()
.map(|pi| NodeStatus::PublicInternet(pi.clone())), .map(|ns| ns.clone()),
} }
} }

View File

@ -118,9 +118,9 @@ pub trait NodeRefBase: Sized {
fn set_updated_since_last_network_change(&self) { fn set_updated_since_last_network_change(&self) {
self.operate_mut(|_rti, e| e.set_updated_since_last_network_change(true)); self.operate_mut(|_rti, e| e.set_updated_since_last_network_change(true));
} }
fn update_node_status(&self, node_status: NodeStatus) { fn update_node_status(&self, routing_domain: RoutingDomain, node_status: NodeStatus) {
self.operate_mut(|_rti, e| { self.operate_mut(|_rti, e| {
e.update_node_status(node_status); e.update_node_status(routing_domain, node_status);
}); });
} }
fn envelope_support(&self) -> Vec<u8> { fn envelope_support(&self) -> Vec<u8> {

View File

@ -257,20 +257,9 @@ impl RouteSpecStore {
// Exclude nodes with no publicinternet nodeinfo, or incompatible nodeinfo or node status won't route // Exclude nodes with no publicinternet nodeinfo, or incompatible nodeinfo or node status won't route
entry.with_inner(|e| { entry.with_inner(|e| {
let node_info_ok = e.signed_node_info(RoutingDomain::PublicInternet).map(|sni|
if let Some(sni) = e.signed_node_info(RoutingDomain::PublicInternet) { sni.has_sequencing_matched_dial_info(sequencing) && sni.node_info().can_route()
sni.has_sequencing_matched_dial_info(sequencing) ).unwrap_or(false)
} else {
false
};
let node_status_ok =
if let Some(ns) = e.node_status(RoutingDomain::PublicInternet) {
ns.has_capability(CAP_WILL_ROUTE)
} else {
false
};
node_info_ok && node_status_ok
}) })
}, },
) as RoutingTableEntryFilter; ) as RoutingTableEntryFilter;

View File

@ -13,6 +13,7 @@ enum RoutingDomainChange {
outbound_protocols: ProtocolTypeSet, outbound_protocols: ProtocolTypeSet,
inbound_protocols: ProtocolTypeSet, inbound_protocols: ProtocolTypeSet,
address_types: AddressTypeSet, address_types: AddressTypeSet,
capabilities: Vec<Capability>,
}, },
SetNetworkClass { SetNetworkClass {
network_class: Option<NetworkClass>, network_class: Option<NetworkClass>,
@ -79,11 +80,13 @@ impl RoutingDomainEditor {
outbound_protocols: ProtocolTypeSet, outbound_protocols: ProtocolTypeSet,
inbound_protocols: ProtocolTypeSet, inbound_protocols: ProtocolTypeSet,
address_types: AddressTypeSet, address_types: AddressTypeSet,
capabilities: Vec<Capability>,
) { ) {
self.changes.push(RoutingDomainChange::SetupNetwork { self.changes.push(RoutingDomainChange::SetupNetwork {
outbound_protocols, outbound_protocols,
inbound_protocols, inbound_protocols,
address_types, address_types,
capabilities,
}) })
} }
@ -142,27 +145,32 @@ impl RoutingDomainEditor {
outbound_protocols, outbound_protocols,
inbound_protocols, inbound_protocols,
address_types, address_types,
capabilities,
} => { } => {
let old_outbound_protocols = detail.common().outbound_protocols(); let old_outbound_protocols = detail.common().outbound_protocols();
let old_inbound_protocols = detail.common().inbound_protocols(); let old_inbound_protocols = detail.common().inbound_protocols();
let old_address_types = detail.common().address_types(); let old_address_types = detail.common().address_types();
let old_capabilities = detail.common().capabilities();
let this_changed = old_outbound_protocols != outbound_protocols let this_changed = old_outbound_protocols != outbound_protocols
|| old_inbound_protocols != inbound_protocols || old_inbound_protocols != inbound_protocols
|| old_address_types != address_types; || old_address_types != address_types
|| old_capabilities != capabilities;
debug!( debug!(
"[{:?}] setup network: {:?} {:?} {:?}", "[{:?}] setup network: {:?} {:?} {:?} {:?}",
self.routing_domain, self.routing_domain,
outbound_protocols, outbound_protocols,
inbound_protocols, inbound_protocols,
address_types address_types,
capabilities
); );
detail.common_mut().setup_network( detail.common_mut().setup_network(
outbound_protocols, outbound_protocols,
inbound_protocols, inbound_protocols,
address_types, address_types,
capabilities,
); );
if this_changed { if this_changed {
changed = true; changed = true;

View File

@ -27,6 +27,7 @@ pub struct RoutingDomainDetailCommon {
inbound_protocols: ProtocolTypeSet, inbound_protocols: ProtocolTypeSet,
address_types: AddressTypeSet, address_types: AddressTypeSet,
relay_node: Option<NodeRef>, relay_node: Option<NodeRef>,
capabilities: Vec<Capability>,
dial_info_details: Vec<DialInfoDetail>, dial_info_details: Vec<DialInfoDetail>,
// caches // caches
cached_peer_info: Mutex<Option<PeerInfo>>, cached_peer_info: Mutex<Option<PeerInfo>>,
@ -41,6 +42,7 @@ impl RoutingDomainDetailCommon {
inbound_protocols: Default::default(), inbound_protocols: Default::default(),
address_types: Default::default(), address_types: Default::default(),
relay_node: Default::default(), relay_node: Default::default(),
capabilities: Default::default(),
dial_info_details: Default::default(), dial_info_details: Default::default(),
cached_peer_info: Mutex::new(Default::default()), cached_peer_info: Mutex::new(Default::default()),
} }
@ -52,10 +54,12 @@ impl RoutingDomainDetailCommon {
outbound_protocols: ProtocolTypeSet, outbound_protocols: ProtocolTypeSet,
inbound_protocols: ProtocolTypeSet, inbound_protocols: ProtocolTypeSet,
address_types: AddressTypeSet, address_types: AddressTypeSet,
capabilities: Vec<Capability>,
) { ) {
self.outbound_protocols = outbound_protocols; self.outbound_protocols = outbound_protocols;
self.inbound_protocols = inbound_protocols; self.inbound_protocols = inbound_protocols;
self.address_types = address_types; self.address_types = address_types;
self.capabilities = capabilities;
self.clear_cache(); self.clear_cache();
} }
@ -75,6 +79,9 @@ impl RoutingDomainDetailCommon {
pub fn address_types(&self) -> AddressTypeSet { pub fn address_types(&self) -> AddressTypeSet {
self.address_types self.address_types
} }
pub fn capabilities(&self) -> Vec<Capability> {
self.capabilities.clone()
}
pub fn relay_node(&self) -> Option<NodeRef> { pub fn relay_node(&self) -> Option<NodeRef> {
self.relay_node.clone() self.relay_node.clone()
} }
@ -108,6 +115,7 @@ impl RoutingDomainDetailCommon {
self.address_types, self.address_types,
VALID_ENVELOPE_VERSIONS.to_vec(), VALID_ENVELOPE_VERSIONS.to_vec(),
VALID_CRYPTO_KINDS.to_vec(), VALID_CRYPTO_KINDS.to_vec(),
self.capabilities.clone(),
self.dial_info_details.clone() self.dial_info_details.clone()
); );

View File

@ -344,6 +344,7 @@ impl RoutingTable {
AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable AddressTypeSet::all(), // Bootstraps are always IPV4 and IPV6 capable
bsrec.envelope_support, // Envelope support is as specified in the bootstrap list bsrec.envelope_support, // Envelope support is as specified in the bootstrap list
crypto_support, // Crypto support is derived from list of node ids crypto_support, // Crypto support is derived from list of node ids
vec![], // Bootstrap needs no capabilities
bsrec.dial_info_details, // Dial info is as specified in the bootstrap list bsrec.dial_info_details, // Dial info is as specified in the bootstrap list
))); )));

View File

@ -146,9 +146,9 @@ impl RoutingTable {
let entry2 = entry.clone(); let entry2 = entry.clone();
entry.with(rti, |rti, e| { entry.with(rti, |rti, e| {
// Ensure we have the node's status // Ensure we have the node's status
if let Some(node_status) = e.node_status(routing_domain) { if let Some(node_info) = e.node_info(routing_domain) {
// Ensure the node will relay // Ensure the node will relay
if node_status.has_capability(CAP_WILL_RELAY) { if node_info.can_inbound_relay() {
// Compare against previous candidate // Compare against previous candidate
if let Some(best_inbound_relay) = best_inbound_relay.as_mut() { if let Some(best_inbound_relay) = best_inbound_relay.as_mut() {
// Less is faster // Less is faster

View File

@ -100,6 +100,7 @@ pub async fn test_round_trip_peerinfo() {
AddressTypeSet::new(), AddressTypeSet::new(),
vec![0], vec![0],
vec![CRYPTO_KIND_VLD0], vec![CRYPTO_KIND_VLD0],
PUBLIC_INTERNET_CAPABILITIES.to_vec(),
vec![], vec![],
), ),
Timestamp::new(0), Timestamp::new(0),

View File

@ -1,5 +1,54 @@
use super::*; use super::*;
pub type Capability = FourCC;
pub const CAP_WILL_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");
#[cfg(feature = "unstable-blockstore")]
pub const CAP_WILL_BLOCKSTORE: Capability = FourCC(*b"BLOC");
cfg_if! {
if #[cfg(all(feature = "unstable-blockstore", feature="unstable-tunnels"))] {
const PUBLIC_INTERNET_CAPABILITIES_LEN: usize = 8;
} else if #[cfg(any(feature = "unstable-blockstore", feature="unstable-tunnels"))] {
const PUBLIC_INTERNET_CAPABILITIES_LEN: usize = 7;
} else {
const PUBLIC_INTERNET_CAPABILITIES_LEN: usize = 6;
}
}
pub const PUBLIC_INTERNET_CAPABILITIES: [Capability; PUBLIC_INTERNET_CAPABILITIES_LEN] = [
CAP_WILL_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,
#[cfg(feature = "unstable-blockstore")]
CAP_WILL_BLOCKSTORE,
];
#[cfg(feature = "unstable-blockstore")]
const LOCAL_NETWORK_CAPABILITIES_LEN: usize = 4;
#[cfg(not(feature = "unstable-blockstore"))]
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,
#[cfg(feature = "unstable-blockstore")]
CAP_WILL_BLOCKSTORE,
];
pub const MAX_CAPABILITIES: usize = 64;
#[derive( #[derive(
Clone, Clone,
Default, Default,
@ -21,6 +70,7 @@ pub struct NodeInfo {
address_types: AddressTypeSet, address_types: AddressTypeSet,
envelope_support: Vec<u8>, envelope_support: Vec<u8>,
crypto_support: Vec<CryptoKind>, crypto_support: Vec<CryptoKind>,
capabilities: Vec<Capability>,
dial_info_detail_list: Vec<DialInfoDetail>, dial_info_detail_list: Vec<DialInfoDetail>,
} }
@ -31,6 +81,7 @@ impl NodeInfo {
address_types: AddressTypeSet, address_types: AddressTypeSet,
envelope_support: Vec<u8>, envelope_support: Vec<u8>,
crypto_support: Vec<CryptoKind>, crypto_support: Vec<CryptoKind>,
capabilities: Vec<Capability>,
dial_info_detail_list: Vec<DialInfoDetail>, dial_info_detail_list: Vec<DialInfoDetail>,
) -> Self { ) -> Self {
Self { Self {
@ -39,6 +90,7 @@ impl NodeInfo {
address_types, address_types,
envelope_support, envelope_support,
crypto_support, crypto_support,
capabilities,
dial_info_detail_list, dial_info_detail_list,
} }
} }
@ -58,6 +110,9 @@ impl NodeInfo {
pub fn crypto_support(&self) -> &[CryptoKind] { pub fn crypto_support(&self) -> &[CryptoKind] {
&self.crypto_support &self.crypto_support
} }
pub fn capabilities(&self) -> &[Capability] {
&self.capabilities
}
pub fn dial_info_detail_list(&self) -> &[DialInfoDetail] { pub fn dial_info_detail_list(&self) -> &[DialInfoDetail] {
&self.dial_info_detail_list &self.dial_info_detail_list
} }
@ -144,8 +199,17 @@ impl NodeInfo {
false false
} }
fn has_capability(&self, cap: Capability) -> bool {
self.capabilities.contains(&cap)
}
/// Can this node assist with signalling? Yes but only if it doesn't require signalling, itself. /// Can this node assist with signalling? Yes but only if it doesn't require signalling, itself.
pub fn can_signal(&self) -> bool { pub fn can_signal(&self) -> bool {
// Has capability?
if !self.has_capability(CAP_WILL_SIGNAL) {
return false;
}
// Must be inbound capable // Must be inbound capable
if !matches!(self.network_class, NetworkClass::InboundCapable) { if !matches!(self.network_class, NetworkClass::InboundCapable) {
return false; return false;
@ -161,13 +225,44 @@ impl NodeInfo {
/// Can this node relay be an inbound relay? /// Can this node relay be an inbound relay?
pub fn can_inbound_relay(&self) -> bool { pub fn can_inbound_relay(&self) -> bool {
// Has capability?
if !self.has_capability(CAP_WILL_RELAY) {
return false;
}
// For now this is the same // For now this is the same
self.can_signal() self.can_signal()
} }
/// Is this node capable of validating dial info /// Is this node capable of validating dial info
pub fn can_validate_dial_info(&self) -> bool { 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 // For now this is the same
self.can_signal() 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)
}
} }

View File

@ -1,47 +1,9 @@
use super::*; use super::*;
/// RoutingDomain-specific status for each node /// Non-nodeinfo status for each node is returned by the StatusA call
/// is returned by the StatusA call
pub type Capability = FourCC;
pub const CAP_WILL_ROUTE: Capability = FourCC(*b"ROUT");
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 MAX_CAPABILITIES: usize = 64;
/// PublicInternet RoutingDomain Status
#[derive(
Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct PublicInternetNodeStatus {
pub capabilities: Vec<Capability>,
}
#[derive(
Clone, Debug, Default, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize,
)]
#[archive_attr(repr(C), derive(CheckBytes))]
pub struct LocalNetworkNodeStatus {
pub capabilities: Vec<Capability>,
}
#[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)] #[derive(Clone, Debug, Serialize, Deserialize, RkyvArchive, RkyvSerialize, RkyvDeserialize)]
#[archive_attr(repr(u8), derive(CheckBytes))] #[archive_attr(repr(C), derive(CheckBytes))]
pub enum NodeStatus { pub struct NodeStatus {
PublicInternet(PublicInternetNodeStatus), // Reserved for expansion
LocalNetwork(LocalNetworkNodeStatus),
}
impl NodeStatus {
pub fn has_capability(&self, cap: Capability) -> bool {
match self {
NodeStatus::PublicInternet(pi) => pi.capabilities.contains(&cap),
NodeStatus::LocalNetwork(ln) => ln.capabilities.contains(&cap),
}
}
} }

View File

@ -31,6 +31,18 @@ pub fn encode_node_info(
s.clone_from_slice(&csvec); s.clone_from_slice(&csvec);
} }
let mut cap_builder = builder
.reborrow()
.init_capabilities(node_info.capabilities().len() as u32);
if let Some(s) = cap_builder.as_slice() {
let capvec: Vec<u32> = node_info
.capabilities()
.iter()
.map(|x| u32::from_be_bytes(x.0))
.collect();
s.clone_from_slice(&capvec);
}
let mut didl_builder = builder.reborrow().init_dial_info_detail_list( let mut didl_builder = builder.reborrow().init_dial_info_detail_list(
node_info node_info
.dial_info_detail_list() .dial_info_detail_list()
@ -121,6 +133,18 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result<Node
return Err(RPCError::protocol("no crypto kinds")); return Err(RPCError::protocol("no crypto kinds"));
} }
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();
let didl_reader = reader let didl_reader = reader
.reborrow() .reborrow()
.get_dial_info_detail_list() .get_dial_info_detail_list()
@ -141,6 +165,7 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result<Node
address_types, address_types,
envelope_support, envelope_support,
crypto_support, crypto_support,
capabilities,
dial_info_detail_list, dial_info_detail_list,
)) ))
} }

View File

@ -1,113 +1,14 @@
use super::*; use super::*;
pub fn encode_public_internet_node_status(
public_internet_node_status: &PublicInternetNodeStatus,
builder: &mut veilid_capnp::public_internet_node_status::Builder,
) -> Result<(), RPCError> {
let mut cap_builder = builder
.reborrow()
.init_capabilities(public_internet_node_status.capabilities.len() as u32);
if let Some(s) = cap_builder.as_slice() {
let capvec: Vec<u32> = public_internet_node_status
.capabilities
.iter()
.map(|x| u32::from_be_bytes(x.0))
.collect();
s.clone_from_slice(&capvec);
}
Ok(())
}
pub fn decode_public_internet_node_status(
reader: &veilid_capnp::public_internet_node_status::Reader,
) -> Result<PublicInternetNodeStatus, RPCError> {
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(PublicInternetNodeStatus { capabilities })
}
pub fn encode_local_network_node_status(
local_network_node_status: &LocalNetworkNodeStatus,
builder: &mut veilid_capnp::local_network_node_status::Builder,
) -> Result<(), RPCError> {
let mut cap_builder = builder
.reborrow()
.init_capabilities(local_network_node_status.capabilities.len() as u32);
if let Some(s) = cap_builder.as_slice() {
let capvec: Vec<u32> = local_network_node_status
.capabilities
.iter()
.map(|x| u32::from_be_bytes(x.0))
.collect();
s.clone_from_slice(&capvec);
}
Ok(())
}
pub fn decode_local_network_node_status(
reader: &veilid_capnp::local_network_node_status::Reader,
) -> Result<LocalNetworkNodeStatus, RPCError> {
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(LocalNetworkNodeStatus { capabilities })
}
pub fn encode_node_status( pub fn encode_node_status(
node_status: &NodeStatus, _node_status: &NodeStatus,
builder: &mut veilid_capnp::node_status::Builder, _builder: &mut veilid_capnp::node_status::Builder,
) -> Result<(), RPCError> { ) -> Result<(), RPCError> {
match node_status { Ok(())
NodeStatus::PublicInternet(ns) => {
let mut pi_builder = builder.reborrow().init_public_internet();
encode_public_internet_node_status(&ns, &mut pi_builder)
}
NodeStatus::LocalNetwork(ns) => {
let mut ln_builder = builder.reborrow().init_local_network();
encode_local_network_node_status(&ns, &mut ln_builder)
}
}
} }
pub fn decode_node_status( pub fn decode_node_status(
reader: &veilid_capnp::node_status::Reader, _reader: &veilid_capnp::node_status::Reader,
) -> Result<NodeStatus, RPCError> { ) -> Result<NodeStatus, RPCError> {
Ok( Ok(NodeStatus {})
match reader
.which()
.map_err(RPCError::map_internal("invalid node status"))?
{
veilid_capnp::node_status::PublicInternet(pi) => {
let r = pi.map_err(RPCError::protocol)?;
let pins = decode_public_internet_node_status(&r)?;
NodeStatus::PublicInternet(pins)
}
veilid_capnp::node_status::LocalNetwork(ln) => {
let r = ln.map_err(RPCError::protocol)?;
let lnns = decode_local_network_node_status(&r)?;
NodeStatus::LocalNetwork(lnns)
}
},
)
} }

View File

@ -117,6 +117,13 @@ impl RPCMessageHeader {
// RPCMessageHeaderDetail::PrivateRouted(p) => p.direct.peer_noderef.clone(), // RPCMessageHeaderDetail::PrivateRouted(p) => p.direct.peer_noderef.clone(),
// } // }
// } // }
pub fn routing_domain(&self) -> RoutingDomain {
match &self.detail {
RPCMessageHeaderDetail::Direct(d) => d.routing_domain,
RPCMessageHeaderDetail::SafetyRouted(s) => s.direct.routing_domain,
RPCMessageHeaderDetail::PrivateRouted(p) => p.direct.routing_domain,
}
}
pub fn direct_sender_node_id(&self) -> TypedKey { pub fn direct_sender_node_id(&self) -> TypedKey {
match &self.detail { match &self.detail {
RPCMessageHeaderDetail::Direct(d) => { RPCMessageHeaderDetail::Direct(d) => {

View File

@ -54,10 +54,14 @@ impl RPCProcessor {
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled // Ignore if disabled
let routing_table = self.routing_table();
{ {
let c = self.config.get(); if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if c.capabilities.disable.contains(&CAP_WILL_APPMESSAGE) { if !opi.signed_node_info().node_info().can_appmessage() {
return Ok(NetworkResult::service_unavailable("appcall is disabled")); return Ok(NetworkResult::service_unavailable(
"app call is not available",
));
}
} }
} }

View File

@ -25,10 +25,14 @@ impl RPCProcessor {
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled // Ignore if disabled
let routing_table = self.routing_table();
{ {
let c = self.config.get(); if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if c.capabilities.disable.contains(&CAP_WILL_APPMESSAGE) { if !opi.signed_node_info().node_info().can_appmessage() {
return Ok(NetworkResult::service_unavailable("appmessage is disabled")); return Ok(NetworkResult::service_unavailable(
"app message is not available",
));
}
} }
} }

View File

@ -7,12 +7,17 @@ impl RPCProcessor {
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled // Ignore if disabled
#[cfg(feature = "unstable-tunnels")]
{ {
let c = self.config.get(); let routing_table = self.routing_table();
if c.capabilities.disable.contains(&CAP_WILL_TUNNEL) { {
return Ok(NetworkResult::service_unavailable( if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
"cancel tunnel is disabled", if !opi.signed_node_info().node_info().can_tunnel() {
)); return Ok(NetworkResult::service_unavailable(
"tunnel is not available",
));
}
}
} }
} }

View File

@ -7,12 +7,17 @@ impl RPCProcessor {
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled // Ignore if disabled
#[cfg(feature = "unstable-tunnels")]
{ {
let c = self.config.get(); let routing_table = self.routing_table();
if c.capabilities.disable.contains(&CAP_WILL_TUNNEL) { {
return Ok(NetworkResult::service_unavailable( if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
"complete tunnel is disabled", if !opi.signed_node_info().node_info().can_tunnel() {
)); return Ok(NetworkResult::service_unavailable(
"tunnel is not available",
));
}
}
} }
} }
Err(RPCError::unimplemented("process_complete_tunnel_q")) Err(RPCError::unimplemented("process_complete_tunnel_q"))

View File

@ -7,10 +7,17 @@ impl RPCProcessor {
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled // Ignore if disabled
#[cfg(feature = "unstable-blockstore")]
{ {
let c = self.config.get(); let routing_table = self.routing_table();
if c.capabilities.disable.contains(&CAP_WILL_BLOCKSTORE) { {
return Ok(NetworkResult::service_unavailable("find block is disabled")); if let Some(opi) = routing_table.get_own_peer_info(detail.routing_domain) {
if !opi.signed_node_info().node_info().can_blockstore() {
return Ok(NetworkResult::service_unavailable(
"block store is not available",
));
}
}
} }
} }
Err(RPCError::unimplemented("process_find_block_q")) Err(RPCError::unimplemented("process_find_block_q"))

View File

@ -163,13 +163,7 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
{
let c = self.config.get();
if c.capabilities.disable.contains(&CAP_WILL_DHT) {
return Ok(NetworkResult::service_unavailable("get value is disabled"));
}
}
// Ensure this never came over a private route, safety route is okay though // Ensure this never came over a private route, safety route is okay though
match &msg.header.detail { match &msg.header.detail {
RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => {} RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => {}
@ -179,7 +173,17 @@ impl RPCProcessor {
)) ))
} }
} }
// Ignore if disabled
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() {
return Ok(NetworkResult::service_unavailable(
"dht is not available",
));
}
}
}
// Get the question // Get the question
let kind = msg.operation.kind().clone(); let kind = msg.operation.kind().clone();
let get_value_q = match kind { let get_value_q = match kind {

View File

@ -366,15 +366,14 @@ impl RPCProcessor {
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled // Ignore if disabled
let routing_table = self.routing_table();
{ {
let c = self.config.get(); if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if c.capabilities if !opi.signed_node_info().node_info().can_route() {
.disable return Ok(NetworkResult::service_unavailable(
.contains(&CAP_WILL_ROUTE) "route is not available",
{ ));
return Ok(NetworkResult::service_unavailable( }
"route is disabled",
));
} }
} }

View File

@ -176,10 +176,14 @@ impl RPCProcessor {
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled // Ignore if disabled
let routing_table = self.routing_table();
{ {
let c = self.config.get(); if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if c.capabilities.disable.contains(&CAP_WILL_DHT) { if !opi.signed_node_info().node_info().can_dht() {
return Ok(NetworkResult::service_unavailable("set value is disabled")); return Ok(NetworkResult::service_unavailable(
"dht is not available",
));
}
} }
} }
// Ensure this never came over a private route, safety route is okay though // Ensure this never came over a private route, safety route is okay though

View File

@ -38,10 +38,14 @@ impl RPCProcessor {
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled // Ignore if disabled
let routing_table = self.routing_table();
{ {
let c = self.config.get(); if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if c.capabilities.disable.contains(&CAP_WILL_SIGNAL) { if !opi.signed_node_info().node_info().can_signal() {
return Ok(NetworkResult::service_unavailable("signal is disabled")); return Ok(NetworkResult::service_unavailable(
"signal is not available",
));
}
} }
} }

View File

@ -7,12 +7,17 @@ impl RPCProcessor {
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled // Ignore if disabled
#[cfg(feature = "unstable-tunnels")]
{ {
let c = self.config.get(); let routing_table = self.routing_table();
if c.capabilities.disable.contains(&CAP_WILL_TUNNEL) { {
return Ok(NetworkResult::service_unavailable( if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
"start tunnel is disabled", if !opi.signed_node_info().node_info().can_tunnel() {
)); return Ok(NetworkResult::service_unavailable(
"tunnel is not available",
));
}
}
} }
} }
Err(RPCError::unimplemented("process_start_tunnel_q")) Err(RPCError::unimplemented("process_start_tunnel_q"))

View File

@ -133,25 +133,8 @@ impl RPCProcessor {
// Ensure the returned node status is the kind for the routing domain we asked for // Ensure the returned node status is the kind for the routing domain we asked for
if let Some(target_nr) = opt_target_nr { if let Some(target_nr) = opt_target_nr {
if let Some(a_node_status) = a_node_status { if let Some(a_node_status) = a_node_status {
match routing_domain {
RoutingDomain::PublicInternet => {
if !matches!(a_node_status, NodeStatus::PublicInternet(_)) {
return Ok(NetworkResult::invalid_message(
"node status doesn't match PublicInternet routing domain",
));
}
}
RoutingDomain::LocalNetwork => {
if !matches!(a_node_status, NodeStatus::LocalNetwork(_)) {
return Ok(NetworkResult::invalid_message(
"node status doesn't match LocalNetwork routing domain",
));
}
}
}
// Update latest node status in routing table // Update latest node status in routing table
target_nr.update_node_status(a_node_status.clone()); target_nr.update_node_status(routing_domain, a_node_status.clone());
} }
} }
@ -236,27 +219,10 @@ impl RPCProcessor {
// Ensure the node status from the question is the kind for the routing domain we received the request in // Ensure the node status from the question is the kind for the routing domain we received the request in
if let Some(q_node_status) = q_node_status { if let Some(q_node_status) = q_node_status {
match routing_domain {
RoutingDomain::PublicInternet => {
if !matches!(q_node_status, NodeStatus::PublicInternet(_)) {
return Ok(NetworkResult::invalid_message(
"node status doesn't match PublicInternet routing domain",
));
}
}
RoutingDomain::LocalNetwork => {
if !matches!(q_node_status, NodeStatus::LocalNetwork(_)) {
return Ok(NetworkResult::invalid_message(
"node status doesn't match LocalNetwork routing domain",
));
}
}
}
// update node status for the requesting node to our routing table // update node status for the requesting node to our routing table
if let Some(sender_nr) = msg.opt_sender_nr.clone() { if let Some(sender_nr) = msg.opt_sender_nr.clone() {
// Update latest node status in routing table for the statusq sender // Update latest node status in routing table for the statusq sender
sender_nr.update_node_status(q_node_status.clone()); sender_nr.update_node_status(routing_domain, q_node_status.clone());
} }
} }

View File

@ -7,12 +7,17 @@ impl RPCProcessor {
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled // Ignore if disabled
#[cfg(feature = "unstable-blockstore")]
{ {
let c = self.config.get(); let routing_table = self.routing_table();
if c.capabilities.disable.contains(&CAP_WILL_BLOCKSTORE) { {
return Ok(NetworkResult::service_unavailable( if let Some(opi) = routing_table.get_own_peer_info(detail.routing_domain) {
"supply block is disabled", if !opi.signed_node_info().node_info().can_blockstore() {
)); return Ok(NetworkResult::service_unavailable(
"block store is not available",
));
}
}
} }
} }
Err(RPCError::unimplemented("process_supply_block_q")) Err(RPCError::unimplemented("process_supply_block_q"))

View File

@ -58,19 +58,6 @@ impl RPCProcessor {
&self, &self,
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled
{
let c = self.config.get();
if c.capabilities
.disable
.contains(&CAP_WILL_VALIDATE_DIAL_INFO)
{
return Ok(NetworkResult::service_unavailable(
"validate dial info is disabled",
));
}
}
let detail = match msg.header.detail { let detail = match msg.header.detail {
RPCMessageHeaderDetail::Direct(detail) => detail, RPCMessageHeaderDetail::Direct(detail) => detail,
RPCMessageHeaderDetail::SafetyRouted(_) | RPCMessageHeaderDetail::PrivateRouted(_) => { RPCMessageHeaderDetail::SafetyRouted(_) | RPCMessageHeaderDetail::PrivateRouted(_) => {
@ -80,6 +67,18 @@ impl RPCProcessor {
} }
}; };
// Ignore if disabled
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() {
return Ok(NetworkResult::service_unavailable(
"validate dial info is not available",
));
}
}
}
// Get the statement // Get the statement
let (_, _, _, kind) = msg.operation.destructure(); let (_, _, _, kind) = msg.operation.destructure();
let (dial_info, receipt, redirect) = match kind { let (dial_info, receipt, redirect) = match kind {
@ -96,7 +95,6 @@ impl RPCProcessor {
// We filter on the -outgoing- protocol capability status not the node's dial info // We filter on the -outgoing- protocol capability status not the node's dial info
// Use the address type though, to ensure we reach an ipv6 capable node if this is // Use the address type though, to ensure we reach an ipv6 capable node if this is
// an ipv6 address // an ipv6 address
let routing_table = self.routing_table();
let sender_node_id = TypedKey::new( let sender_node_id = TypedKey::new(
detail.envelope.get_crypto_kind(), detail.envelope.get_crypto_kind(),
detail.envelope.get_sender_id(), detail.envelope.get_sender_id(),
@ -117,11 +115,9 @@ impl RPCProcessor {
move |rti: &RoutingTableInner, v: Option<Arc<BucketEntry>>| { move |rti: &RoutingTableInner, v: Option<Arc<BucketEntry>>| {
let entry = v.unwrap(); let entry = v.unwrap();
entry.with(rti, move |_rti, e| { entry.with(rti, move |_rti, e| {
if let Some(status) = &e.node_status(routing_domain) { e.node_info(routing_domain)
status.has_capability(CAP_WILL_VALIDATE_DIAL_INFO) .map(|ni| ni.can_validate_dial_info())
} else { .unwrap_or(false)
true
}
}) })
}, },
) as RoutingTableEntryFilter; ) as RoutingTableEntryFilter;

View File

@ -7,12 +7,12 @@ impl RPCProcessor {
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled // Ignore if disabled
let routing_table = self.routing_table();
{ {
let c = self.config.get(); if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if c.capabilities.disable.contains(&CAP_WILL_DHT) { if !opi.signed_node_info().node_info().can_dht() {
return Ok(NetworkResult::service_unavailable( return Ok(NetworkResult::service_unavailable("dht is not available"));
"value changed is disabled", }
));
} }
} }
Err(RPCError::unimplemented("process_value_changed")) Err(RPCError::unimplemented("process_value_changed"))

View File

@ -7,15 +7,14 @@ impl RPCProcessor {
msg: RPCMessage, msg: RPCMessage,
) -> Result<NetworkResult<()>, RPCError> { ) -> Result<NetworkResult<()>, RPCError> {
// Ignore if disabled // Ignore if disabled
let routing_table = self.routing_table();
{ {
let c = self.config.get(); if let Some(opi) = routing_table.get_own_peer_info(msg.header.routing_domain()) {
if c.capabilities.disable.contains(&CAP_WILL_DHT) { if !opi.signed_node_info().node_info().can_dht() {
return Ok(NetworkResult::service_unavailable( return Ok(NetworkResult::service_unavailable("dht is not available"));
"watch value is disabled", }
));
} }
} }
Err(RPCError::unimplemented("process_watch_value_q")) Err(RPCError::unimplemented("process_watch_value_q"))
} }
} }

View File

@ -60,6 +60,7 @@ class Capability(StrEnum):
CAP_WILL_VALIDATE_DIAL_INFO = "DIAL" CAP_WILL_VALIDATE_DIAL_INFO = "DIAL"
CAP_WILL_DHT = "DHTV" CAP_WILL_DHT = "DHTV"
CAP_WILL_APPMESSAGE = "APPM" CAP_WILL_APPMESSAGE = "APPM"
CAP_WILL_BLOCKSTORE = "BLOC"
class Stability(StrEnum): class Stability(StrEnum):