diff --git a/veilid-cli/src/client_api_connection.rs b/veilid-cli/src/client_api_connection.rs index 24923c58..cb39e5de 100644 --- a/veilid-cli/src/client_api_connection.rs +++ b/veilid-cli/src/client_api_connection.rs @@ -76,7 +76,6 @@ impl ClientApiConnection { }; if let Err(e) = reply_channel.send_async(response).await { error!("failed to process reply: {}", e); - return; } } diff --git a/veilid-cli/src/command_processor.rs b/veilid-cli/src/command_processor.rs index 4eb4337f..a9fe9168 100644 --- a/veilid-cli/src/command_processor.rs +++ b/veilid-cli/src/command_processor.rs @@ -248,7 +248,6 @@ Server Debug Commands: _ => { ui.add_node_event(Level::Error, format!("unknown flag: {}", flag)); ui.send_callback(callback); - return; } } }); @@ -271,7 +270,6 @@ Server Debug Commands: _ => { ui.add_node_event(Level::Error, format!("unknown flag: {}", flag)); ui.send_callback(callback); - return; } } }); @@ -399,12 +397,12 @@ Server Debug Commands: } pub fn update_route(&self, route: &json::JsonValue) { let mut out = String::new(); - if route["dead_routes"].len() != 0 { + if !route["dead_routes"].is_empty() { out.push_str(&format!("Dead routes: {:?}", route["dead_routes"])); } - if route["dead_routes"].len() != 0 { + if !route["dead_routes"].is_empty() { if !out.is_empty() { - out.push_str("\n"); + out.push('\n'); } out.push_str(&format!( "Dead remote routes: {:?}", @@ -460,7 +458,7 @@ Server Debug Commands: }; let strmsg = if printable { - format!("\"{}\"", String::from_utf8_lossy(&message).to_string()) + format!("\"{}\"", String::from_utf8_lossy(message)) } else { hex::encode(message) }; @@ -498,7 +496,7 @@ Server Debug Commands: }; let strmsg = if printable { - format!("\"{}\"", String::from_utf8_lossy(&message).to_string()) + format!("\"{}\"", String::from_utf8_lossy(message)) } else { hex::encode(message) }; diff --git a/veilid-cli/src/main.rs b/veilid-cli/src/main.rs index 11f0ebde..4f46cfdd 100644 --- a/veilid-cli/src/main.rs +++ b/veilid-cli/src/main.rs @@ -1,4 +1,5 @@ #![deny(clippy::all)] +#![allow(clippy::comparison_chain, clippy::upper_case_acronyms)] #![deny(unused_must_use)] #![recursion_limit = "256"] @@ -58,7 +59,7 @@ fn main() -> Result<(), String> { None }; - let mut settings = settings::Settings::new(settings_path.as_ref().map(|x| x.as_os_str())) + let mut settings = settings::Settings::new(settings_path.as_deref()) .map_err(|e| format!("configuration is invalid: {}", e))?; // Set config from command line diff --git a/veilid-cli/src/peers_table_view.rs b/veilid-cli/src/peers_table_view.rs index 6ba9639a..03d66795 100644 --- a/veilid-cli/src/peers_table_view.rs +++ b/veilid-cli/src/peers_table_view.rs @@ -58,7 +58,7 @@ impl TableViewItem for json::JsonValue { PeerTableColumn::NodeId => self["node_ids"][0].to_string(), PeerTableColumn::Address => self["peer_address"].to_string(), PeerTableColumn::LatencyAvg => { - format!("{}", format_ts(&self["peer_stats"]["latency"]["average"])) + format_ts(&self["peer_stats"]["latency"]["average"]).to_string() } PeerTableColumn::TransferDownAvg => { format_bps(&self["peer_stats"]["transfer"]["down"]["average"]) diff --git a/veilid-cli/src/settings.rs b/veilid-cli/src/settings.rs index 5cc9268f..73ecb29d 100644 --- a/veilid-cli/src/settings.rs +++ b/veilid-cli/src/settings.rs @@ -6,7 +6,7 @@ use std::net::{SocketAddr, ToSocketAddrs}; use std::path::{Path, PathBuf}; pub fn load_default_config() -> Result { - let default_config = r###"--- + let default_config = r#"--- address: "localhost:5959" autoconnect: true autoreconnect: true @@ -44,7 +44,7 @@ interface: info : "white" warn : "light yellow" error : "light red" - "### + "# .replace( "%LOGGING_FILE_DIRECTORY%", &Settings::get_default_log_directory().to_string_lossy(), diff --git a/veilid-cli/src/ui.rs b/veilid-cli/src/ui.rs index 24fcc803..0c401e38 100644 --- a/veilid-cli/src/ui.rs +++ b/veilid-cli/src/ui.rs @@ -469,7 +469,7 @@ impl UI { let color = *Self::inner_mut(s).log_colors.get(&Level::Warn).unwrap(); cursive_flexi_logger_view::parse_lines_to_log( color.into(), - format!(">> Could not copy to clipboard"), + ">> Could not copy to clipboard".to_string(), ); } } else { @@ -483,14 +483,13 @@ impl UI { .as_bytes(), ) .is_ok() + && std::io::stdout().flush().is_ok() { - if std::io::stdout().flush().is_ok() { - let color = *Self::inner_mut(s).log_colors.get(&Level::Info).unwrap(); - cursive_flexi_logger_view::parse_lines_to_log( - color.into(), - format!(">> Copied: {}", text.as_ref()), - ); - } + let color = *Self::inner_mut(s).log_colors.get(&Level::Info).unwrap(); + cursive_flexi_logger_view::parse_lines_to_log( + color.into(), + format!(">> Copied: {}", text.as_ref()), + ); } } } @@ -523,7 +522,7 @@ impl UI { let mut reset: bool = false; match state { ConnectionState::Disconnected => { - if inner.connection_dialog_state == None + if inner.connection_dialog_state.is_none() || inner .connection_dialog_state .as_ref() @@ -541,7 +540,7 @@ impl UI { } } ConnectionState::Connected(_, _) => { - if inner.connection_dialog_state != None + if inner.connection_dialog_state.is_some() && !inner .connection_dialog_state .as_ref() @@ -552,7 +551,7 @@ impl UI { } } ConnectionState::Retrying(_, _) => { - if inner.connection_dialog_state == None + if inner.connection_dialog_state.is_none() || inner .connection_dialog_state .as_ref() @@ -941,10 +940,12 @@ impl UI { // } } +type CallbackSink = Box; + #[derive(Clone)] pub struct UISender { inner: Arc>, - cb_sink: Sender>, + cb_sink: Sender, } impl UISender { @@ -1020,7 +1021,7 @@ impl UISender { for l in 0..node_ids.len() { let nid = &node_ids[l]; if !node_id_str.is_empty() { - node_id_str.push_str(" "); + node_id_str.push(' '); } node_id_str.push_str(nid.to_string().as_ref()); } diff --git a/veilid-core/build.rs b/veilid-core/build.rs index 97d82650..3eb93400 100644 --- a/veilid-core/build.rs +++ b/veilid-core/build.rs @@ -3,7 +3,7 @@ use std::process::{Command, Stdio}; fn search_file, P: AsRef>(start: T, name: P) -> Option { let start_path = PathBuf::from(start.as_ref()).canonicalize().ok(); - let mut path = start_path.as_ref().map(|x| x.as_path()); + let mut path = start_path.as_deref(); while let Some(some_path) = path { let file_path = some_path.join(name.as_ref()); if file_path.exists() { @@ -18,10 +18,7 @@ fn get_desired_capnp_version_string() -> String { let capnp_path = search_file(env!("CARGO_MANIFEST_DIR"), ".capnp_version") .expect("should find .capnp_version file"); std::fs::read_to_string(&capnp_path) - .expect(&format!( - "can't read .capnp_version file here: {:?}", - capnp_path - )) + .unwrap_or_else(|_| panic!("can't read .capnp_version file here: {:?}", capnp_path)) .trim() .to_owned() } @@ -47,10 +44,7 @@ fn get_desired_protoc_version_string() -> String { let protoc_path = search_file(env!("CARGO_MANIFEST_DIR"), ".protoc_version") .expect("should find .protoc_version file"); std::fs::read_to_string(&protoc_path) - .expect(&format!( - "can't read .protoc_version file here: {:?}", - protoc_path - )) + .unwrap_or_else(|_| panic!("can't read .protoc_version file here: {:?}", protoc_path)) .trim() .to_owned() } @@ -79,11 +73,18 @@ fn main() { let protoc_version_string = get_protoc_version_string(); // Check capnp version - let desired_capnp_major_version = - usize::from_str_radix(desired_capnp_version_string.split_once(".").unwrap().0, 10) - .expect("should be valid int"); + let desired_capnp_major_version = desired_capnp_version_string + .split_once('.') + .unwrap() + .0 + .parse::() + .expect("should be valid int"); - if usize::from_str_radix(capnp_version_string.split_once(".").unwrap().0, 10) + if capnp_version_string + .split_once('.') + .unwrap() + .0 + .parse::() .expect("should be valid int") != desired_capnp_major_version { @@ -99,10 +100,17 @@ fn main() { } // Check protoc version - let desired_protoc_major_version = - usize::from_str_radix(desired_protoc_version_string.split_once(".").unwrap().0, 10) - .expect("should be valid int"); - if usize::from_str_radix(protoc_version_string.split_once(".").unwrap().0, 10) + let desired_protoc_major_version = desired_protoc_version_string + .split_once('.') + .unwrap() + .0 + .parse::() + .expect("should be valid int"); + if protoc_version_string + .split_once('.') + .unwrap() + .0 + .parse::() .expect("should be valid int") < desired_protoc_major_version { diff --git a/veilid-core/src/api_tracing_layer.rs b/veilid-core/src/api_tracing_layer.rs index 8de371ec..44b9b67a 100644 --- a/veilid-core/src/api_tracing_layer.rs +++ b/veilid-core/src/api_tracing_layer.rs @@ -103,11 +103,11 @@ impl registry::LookupSpan<'a>> Layer for ApiTracingLa None }; - (inner.update_callback)(VeilidUpdate::Log(VeilidLog { + (inner.update_callback)(VeilidUpdate::Log(Box::new(VeilidLog { log_level, message, backtrace, - })) + }))) } } } diff --git a/veilid-core/src/attachment_manager.rs b/veilid-core/src/attachment_manager.rs index 14b7856d..40fa9b78 100644 --- a/veilid-core/src/attachment_manager.rs +++ b/veilid-core/src/attachment_manager.rs @@ -168,7 +168,7 @@ impl AttachmentManager { }) .unwrap_or(true); if send_update { - Some((update_callback, Self::get_veilid_state_inner(&*inner))) + Some((update_callback, Self::get_veilid_state_inner(&inner))) } else { None } @@ -197,11 +197,11 @@ impl AttachmentManager { }; if let Some(update_callback) = update_callback { - update_callback(VeilidUpdate::Attachment(VeilidStateAttachment { + update_callback(VeilidUpdate::Attachment(Box::new(VeilidStateAttachment { state, public_internet_ready: false, local_network_ready: false, - })) + }))) } } @@ -325,8 +325,8 @@ impl AttachmentManager { // self.inner.lock().last_attachment_state // } - fn get_veilid_state_inner(inner: &AttachmentManagerInner) -> VeilidStateAttachment { - VeilidStateAttachment { + fn get_veilid_state_inner(inner: &AttachmentManagerInner) -> Box { + Box::new(VeilidStateAttachment { state: inner.last_attachment_state, public_internet_ready: inner .last_routing_table_health @@ -338,11 +338,11 @@ impl AttachmentManager { .as_ref() .map(|x| x.local_network_ready) .unwrap_or(false), - } + }) } - pub fn get_veilid_state(&self) -> VeilidStateAttachment { + pub fn get_veilid_state(&self) -> Box { let inner = self.inner.lock(); - Self::get_veilid_state_inner(&*inner) + Self::get_veilid_state_inner(&inner) } } diff --git a/veilid-core/src/crypto/envelope.rs b/veilid-core/src/crypto/envelope.rs index 86f1324e..dc92e317 100644 --- a/veilid-core/src/crypto/envelope.rs +++ b/veilid-core/src/crypto/envelope.rs @@ -236,7 +236,7 @@ impl Envelope { } // Compress body - let body = compress_prepend_size(&body); + let body = compress_prepend_size(body); // Ensure body isn't too long let envelope_size: usize = body.len() + MIN_ENVELOPE_SIZE; diff --git a/veilid-core/src/crypto/tests/mod.rs b/veilid-core/src/crypto/tests/mod.rs index 04a29d9f..33ec1eac 100644 --- a/veilid-core/src/crypto/tests/mod.rs +++ b/veilid-core/src/crypto/tests/mod.rs @@ -8,10 +8,10 @@ use crate::tests::common::test_veilid_config::*; async fn crypto_tests_startup() -> VeilidAPI { trace!("crypto_tests: starting"); let (update_callback, config_callback) = setup_veilid_core(); - let api = api_startup(update_callback, config_callback) + + api_startup(update_callback, config_callback) .await - .expect("startup failed"); - api + .expect("startup failed") } async fn crypto_tests_shutdown(api: VeilidAPI) { diff --git a/veilid-core/src/crypto/tests/test_types.rs b/veilid-core/src/crypto/tests/test_types.rs index 3a544186..6d08d34b 100644 --- a/veilid-core/src/crypto/tests/test_types.rs +++ b/veilid-core/src/crypto/tests/test_types.rs @@ -1,5 +1,3 @@ -#![allow(clippy::bool_assert_comparison)] - use super::*; use core::convert::TryFrom; @@ -228,7 +226,7 @@ pub async fn test_encode_decode(vcrypto: CryptoSystemVersion) { pub async fn test_typed_convert(vcrypto: CryptoSystemVersion) { let tks1 = format!( "{}:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ", - vcrypto.kind().to_string() + vcrypto.kind() ); let tk1 = TypedKey::from_str(&tks1).expect("failed"); let tks1x = tk1.to_string(); @@ -236,22 +234,22 @@ pub async fn test_typed_convert(vcrypto: CryptoSystemVersion) { let tks2 = format!( "{}:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzd", - vcrypto.kind().to_string() + vcrypto.kind() ); let _tk2 = TypedKey::from_str(&tks2).expect_err("succeeded when it shouldnt have"); - let tks3 = format!("XXXX:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ",); + let tks3 = "XXXX:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ".to_string(); let tk3 = TypedKey::from_str(&tks3).expect("failed"); let tks3x = tk3.to_string(); assert_eq!(tks3, tks3x); - let tks4 = format!("XXXX:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzd",); + let tks4 = "XXXX:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzd".to_string(); let _tk4 = TypedKey::from_str(&tks4).expect_err("succeeded when it shouldnt have"); - let tks5 = format!("XXX:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ",); + let tks5 = "XXX:7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ".to_string(); let _tk5 = TypedKey::from_str(&tks5).expect_err("succeeded when it shouldnt have"); - let tks6 = format!("7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ",); + let tks6 = "7lxDEabK_qgjbe38RtBa3IZLrud84P6NhGP-pRTZzdQ".to_string(); let tk6 = TypedKey::from_str(&tks6).expect("failed"); let tks6x = tk6.to_string(); assert!(tks6x.ends_with(&tks6)); @@ -338,14 +336,14 @@ async fn test_operations(vcrypto: CryptoSystemVersion) { assert_eq!(d4.first_nonzero_nibble(), Some((0, 0x9u8))); // Verify bits - assert_eq!(d1.bit(0), true); - assert_eq!(d1.bit(1), false); - assert_eq!(d1.bit(7), false); - assert_eq!(d1.bit(8), false); - assert_eq!(d1.bit(14), true); - assert_eq!(d1.bit(15), false); - assert_eq!(d1.bit(254), true); - assert_eq!(d1.bit(255), false); + assert!(d1.bit(0)); + assert!(!d1.bit(1)); + assert!(!d1.bit(7)); + assert!(!d1.bit(8)); + assert!(d1.bit(14)); + assert!(!d1.bit(15)); + assert!(d1.bit(254)); + assert!(!d1.bit(255)); assert_eq!(d1.first_nonzero_bit(), Some(0)); assert_eq!(d2.first_nonzero_bit(), Some(0)); diff --git a/veilid-core/src/crypto/types/byte_array_types.rs b/veilid-core/src/crypto/types/byte_array_types.rs index d47a323a..4c989bab 100644 --- a/veilid-core/src/crypto/types/byte_array_types.rs +++ b/veilid-core/src/crypto/types/byte_array_types.rs @@ -77,7 +77,7 @@ where macro_rules! byte_array_type { ($name:ident, $size:expr, $encoded_size:expr) => { - #[derive(Clone, Copy, Hash)] + #[derive(Clone, Copy, Hash, PartialOrd, Ord, PartialEq, Eq)] #[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(into_wasm_abi))] pub struct $name { pub bytes: [u8; $size], @@ -114,32 +114,6 @@ macro_rules! byte_array_type { } } - impl PartialOrd for $name { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } - } - - impl Ord for $name { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - for n in 0..$size { - let c = self.bytes[n].cmp(&other.bytes[n]); - if c != core::cmp::Ordering::Equal { - return c; - } - } - core::cmp::Ordering::Equal - } - } - - impl PartialEq for $name { - fn eq(&self, other: &Self) -> bool { - self.bytes == other.bytes - } - } - - impl Eq for $name {} - impl $name { pub fn new(bytes: [u8; $size]) -> Self { Self { bytes } diff --git a/veilid-core/src/crypto/types/crypto_typed_group.rs b/veilid-core/src/crypto/types/crypto_typed_group.rs index 80f717bc..fbdd855c 100644 --- a/veilid-core/src/crypto/types/crypto_typed_group.rs +++ b/veilid-core/src/crypto/types/crypto_typed_group.rs @@ -93,16 +93,13 @@ where } /// Return preferred typed key of our supported crypto kinds pub fn best(&self) -> Option> { - match self.items.first().copied() { - None => None, - Some(k) => { - if !VALID_CRYPTO_KINDS.contains(&k.kind) { - None - } else { - Some(k) - } - } - } + self.items + .first() + .copied() + .filter(|k| VALID_CRYPTO_KINDS.contains(&k.kind)) + } + pub fn is_empty(&self) -> bool { + self.items.is_empty() } pub fn len(&self) -> usize { self.items.len() @@ -204,7 +201,7 @@ where if &s[0..1] != "[" || &s[(s.len() - 1)..] != "]" { apibail_parse_error!("invalid format", s); } - for x in s[1..s.len() - 1].split(",") { + for x in s[1..s.len() - 1].split(',') { let tk = CryptoTyped::::from_str(x.trim())?; items.push(tk); } @@ -272,7 +269,7 @@ where tks } } -impl Into>> for CryptoTypedGroup +impl From> for Vec> where K: Clone + Copy @@ -286,7 +283,7 @@ where + Hash + Encodable, { - fn into(self) -> Vec> { - self.items + fn from(val: CryptoTypedGroup) -> Self { + val.items } } diff --git a/veilid-core/src/crypto/types/keypair.rs b/veilid-core/src/crypto/types/keypair.rs index 6530eefb..4fafd4ea 100644 --- a/veilid-core/src/crypto/types/keypair.rs +++ b/veilid-core/src/crypto/types/keypair.rs @@ -96,7 +96,7 @@ impl<'de> serde::Deserialize<'de> for KeyPair { D: serde::Deserializer<'de>, { let s = ::deserialize(deserializer)?; - if s == "" { + if s.is_empty() { return Ok(KeyPair::default()); } KeyPair::try_decode(s.as_str()).map_err(serde::de::Error::custom) diff --git a/veilid-core/src/crypto/vld0/mod.rs b/veilid-core/src/crypto/vld0/mod.rs index 5214f148..b2b2ce1c 100644 --- a/veilid-core/src/crypto/vld0/mod.rs +++ b/veilid-core/src/crypto/vld0/mod.rs @@ -134,8 +134,8 @@ impl CryptoSystem for CryptoSystemVLD0 { SharedSecret::new(s) } fn compute_dh(&self, key: &PublicKey, secret: &SecretKey) -> VeilidAPIResult { - let pk_xd = public_to_x25519_pk(&key)?; - let sk_xd = secret_to_x25519_sk(&secret)?; + let pk_xd = public_to_x25519_pk(key)?; + let sk_xd = secret_to_x25519_sk(secret)?; let dh_bytes = sk_xd.diffie_hellman(&pk_xd).to_bytes(); @@ -188,9 +188,9 @@ impl CryptoSystem for CryptoSystemVLD0 { fn distance(&self, key1: &PublicKey, key2: &PublicKey) -> CryptoKeyDistance { let mut bytes = [0u8; CRYPTO_KEY_LENGTH]; - for n in 0..CRYPTO_KEY_LENGTH { + (0..CRYPTO_KEY_LENGTH).for_each(|n| { bytes[n] = key1.bytes[n] ^ key2.bytes[n]; - } + }); CryptoKeyDistance::new(bytes) } @@ -219,7 +219,7 @@ impl CryptoSystem for CryptoSystemVLD0 { let sig = Signature::new(sig_bytes.to_bytes()); - self.verify(dht_key, &data, &sig)?; + self.verify(dht_key, data, &sig)?; Ok(sig) } diff --git a/veilid-core/src/intf/mod.rs b/veilid-core/src/intf/mod.rs index 81756b61..af238bee 100644 --- a/veilid-core/src/intf/mod.rs +++ b/veilid-core/src/intf/mod.rs @@ -9,4 +9,4 @@ mod native; #[cfg(not(target_arch = "wasm32"))] pub use native::*; -pub static KNOWN_PROTECTED_STORE_KEYS: [&'static str; 2] = ["device_encryption_key", "_test_key"]; +pub static KNOWN_PROTECTED_STORE_KEYS: [&str; 2] = ["device_encryption_key", "_test_key"]; diff --git a/veilid-core/src/intf/native/network_interfaces/apple.rs b/veilid-core/src/intf/native/network_interfaces/apple.rs index a34174fb..88422f60 100644 --- a/veilid-core/src/intf/native/network_interfaces/apple.rs +++ b/veilid-core/src/intf/native/network_interfaces/apple.rs @@ -324,7 +324,7 @@ impl PlatformSupportApple { let intf_index = unsafe { (*rt).rtm_index } as u32; // Fill in sockaddr table - for i in 0..(RTAX_MAX as usize) { + (0..(RTAX_MAX as usize)).for_each(|i| { if rtm_addrs & (1 << i) != 0 { sa_tab[i] = sa; sa = unsafe { @@ -333,7 +333,7 @@ impl PlatformSupportApple { sa }; } - } + }); // Look for gateways if rtm_addrs & (RTA_DST | RTA_GATEWAY) == (RTA_DST | RTA_GATEWAY) { @@ -373,7 +373,7 @@ impl PlatformSupportApple { } fn get_address_flags(ifname: &str, addr: sockaddr_in6) -> EyreResult { - let mut req = in6_ifreq::from_name(&ifname).unwrap(); + let mut req = in6_ifreq::from_name(ifname).unwrap(); req.set_addr(addr); let sock = unsafe { socket(AF_INET6, SOCK_DGRAM, 0) }; diff --git a/veilid-core/src/intf/native/network_interfaces/mod.rs b/veilid-core/src/intf/native/network_interfaces/mod.rs index 59f1ae49..4c53cab9 100644 --- a/veilid-core/src/intf/native/network_interfaces/mod.rs +++ b/veilid-core/src/intf/native/network_interfaces/mod.rs @@ -359,7 +359,7 @@ impl NetworkInterfaces { let old_best_addresses = inner.interface_address_cache.clone(); // redo the address cache - Self::cache_best_addresses(&mut *inner); + Self::cache_best_addresses(&mut inner); // See if our best addresses have changed if old_best_addresses != inner.interface_address_cache { diff --git a/veilid-core/src/intf/wasm/protected_store.rs b/veilid-core/src/intf/wasm/protected_store.rs index 087596ef..84216ad0 100644 --- a/veilid-core/src/intf/wasm/protected_store.rs +++ b/veilid-core/src/intf/wasm/protected_store.rs @@ -69,14 +69,11 @@ impl ProtectedStore { let vkey = self.browser_key_name(key.as_ref()); - let prev = match ls + let prev = ls .get_item(&vkey) .map_err(map_jsvalue_error) .wrap_err("exception_thrown")? - { - Some(_) => true, - None => false, - }; + .is_some(); ls.set_item(&vkey, value.as_ref()) .map_err(map_jsvalue_error) diff --git a/veilid-core/src/lib.rs b/veilid-core/src/lib.rs index f2cb5df9..bea8cab5 100644 --- a/veilid-core/src/lib.rs +++ b/veilid-core/src/lib.rs @@ -22,6 +22,7 @@ //! #![deny(clippy::all)] +#![allow(clippy::comparison_chain, clippy::upper_case_acronyms)] #![deny(unused_must_use)] #![recursion_limit = "256"] diff --git a/veilid-core/src/network_manager/address_filter.rs b/veilid-core/src/network_manager/address_filter.rs index 5b17e3d0..ad869bfc 100644 --- a/veilid-core/src/network_manager/address_filter.rs +++ b/veilid-core/src/network_manager/address_filter.rs @@ -244,12 +244,12 @@ impl AddressFilter { self.unlocked_inner.max_connections_per_ip6_prefix_size, addr, ); - self.is_ip_addr_punished_inner(&*inner, ipblock) + self.is_ip_addr_punished_inner(&inner, ipblock) } pub fn get_dial_info_failed_ts(&self, dial_info: &DialInfo) -> Option { let inner = self.inner.lock(); - self.get_dial_info_failed_ts_inner(&*inner, dial_info) + self.get_dial_info_failed_ts_inner(&inner, dial_info) } pub fn set_dial_info_failed(&self, dial_info: DialInfo) { @@ -301,7 +301,7 @@ impl AddressFilter { pub fn is_node_id_punished(&self, node_id: TypedKey) -> bool { let inner = self.inner.lock(); - self.is_node_id_punished_inner(&*inner, node_id) + self.is_node_id_punished_inner(&inner, node_id) } pub fn punish_node_id(&self, node_id: TypedKey) { @@ -333,8 +333,8 @@ impl AddressFilter { ) -> EyreResult<()> { // let mut inner = self.inner.lock(); - self.purge_old_timestamps(&mut *inner, cur_ts); - self.purge_old_punishments(&mut *inner, cur_ts); + self.purge_old_timestamps(&mut inner, cur_ts); + self.purge_old_punishments(&mut inner, cur_ts); Ok(()) } @@ -411,7 +411,7 @@ impl AddressFilter { ); let ts = get_aligned_timestamp(); - self.purge_old_timestamps(&mut *inner, ts); + self.purge_old_timestamps(&mut inner, ts); match ipblock { IpAddr::V4(v4) => { diff --git a/veilid-core/src/network_manager/connection_handle.rs b/veilid-core/src/network_manager/connection_handle.rs index dbf53117..2f960f65 100644 --- a/veilid-core/src/network_manager/connection_handle.rs +++ b/veilid-core/src/network_manager/connection_handle.rs @@ -31,7 +31,7 @@ impl ConnectionHandle { } pub fn connection_descriptor(&self) -> ConnectionDescriptor { - self.descriptor.clone() + self.descriptor } #[cfg_attr(feature="verbose-tracing", instrument(level="trace", skip(self, message), fields(message.len = message.len())))] diff --git a/veilid-core/src/network_manager/connection_manager.rs b/veilid-core/src/network_manager/connection_manager.rs index 6f350a9f..df2fe3ef 100644 --- a/veilid-core/src/network_manager/connection_manager.rs +++ b/veilid-core/src/network_manager/connection_manager.rs @@ -117,13 +117,12 @@ impl ConnectionManager { // Remove the inner from the lock let mut inner = { let mut inner_lock = self.arc.inner.lock(); - let inner = match inner_lock.take() { + match inner_lock.take() { Some(v) => v, None => { panic!("not started"); } - }; - inner + } }; // Stop all the connections and the async processor @@ -250,8 +249,8 @@ impl ConnectionManager { &self, dial_info: DialInfo, ) -> EyreResult> { - let peer_address = dial_info.to_peer_address(); - let remote_addr = peer_address.to_socket_addr(); + let peer_address = dial_info.peer_address(); + let remote_addr = peer_address.socket_addr(); let mut preferred_local_address = self .network_manager() .net() @@ -302,26 +301,6 @@ impl ConnectionManager { .await; match result_net_res { Ok(net_res) => { - // // If the connection 'already exists', then try one last time to return a connection from the table, in case - // // an 'accept' happened at literally the same time as our connect. A preferred local address must have been - // // specified otherwise we would have picked a different ephemeral port and this could not have happened - // if net_res.is_already_exists() && preferred_local_address.is_some() { - // // Make 'already existing' connection descriptor - // let conn_desc = ConnectionDescriptor::new( - // dial_info.to_peer_address(), - // SocketAddress::from_socket_addr(preferred_local_address.unwrap()), - // ); - // // Return the connection for this if we have it - // if let Some(conn) = self - // .arc - // .connection_table - // .get_connection_by_descriptor(conn_desc) - // { - // // Should not really happen, lets make sure we see this if it does - // log_net!(warn "== Returning existing connection in race: {:?}", conn_desc); - // return Ok(NetworkResult::Value(conn)); - // } - // } if net_res.is_value() || retry_count == 0 { // Successful new connection, return it break net_res; diff --git a/veilid-core/src/network_manager/connection_table.rs b/veilid-core/src/network_manager/connection_table.rs index 233c447e..c391246b 100644 --- a/veilid-core/src/network_manager/connection_table.rs +++ b/veilid-core/src/network_manager/connection_table.rs @@ -164,7 +164,7 @@ impl ConnectionTable { } // Filter by ip for connection limits - let ip_addr = descriptor.remote_address().to_ip_addr(); + let ip_addr = descriptor.remote_address().ip_addr(); match inner.address_filter.add_connection(ip_addr) { Ok(()) => {} Err(e) => { @@ -196,7 +196,7 @@ impl ConnectionTable { } log_net!(debug "== LRU Connection Killed: {} -> {}", lruk, lru_conn.debug_print(get_aligned_timestamp())); - out_conn = Some(Self::remove_connection_records(&mut *inner, lruk)); + out_conn = Some(Self::remove_connection_records(&mut inner, lruk)); break; } } @@ -237,11 +237,11 @@ impl ConnectionTable { best_port: Option, remote: PeerAddress, ) -> Option { - let mut inner = self.inner.lock(); + let inner = &mut *self.inner.lock(); let all_ids_by_remote = inner.ids_by_remote.get(&remote)?; let protocol_index = Self::protocol_to_index(remote.protocol_type()); - if all_ids_by_remote.len() == 0 { + if all_ids_by_remote.is_empty() { // no connections return None; } @@ -253,11 +253,11 @@ impl ConnectionTable { } // multiple connections, find the one that matches the best port, or the most recent if let Some(best_port) = best_port { - for id in all_ids_by_remote.iter().copied() { - let nc = inner.conn_by_id[protocol_index].peek(&id).unwrap(); + for id in all_ids_by_remote { + let nc = inner.conn_by_id[protocol_index].peek(id).unwrap(); if let Some(local_addr) = nc.connection_descriptor().local() { if local_addr.port() == best_port { - let nc = inner.conn_by_id[protocol_index].get(&id).unwrap(); + let nc = inner.conn_by_id[protocol_index].get(id).unwrap(); return Some(nc.get_handle()); } } @@ -331,7 +331,7 @@ impl ConnectionTable { } } // address_filter - let ip_addr = remote.to_socket_addr().ip(); + let ip_addr = remote.socket_addr().ip(); inner .address_filter .remove_connection(ip_addr) @@ -347,7 +347,7 @@ impl ConnectionTable { if !inner.conn_by_id[protocol_index].contains_key(&id) { return None; } - let conn = Self::remove_connection_records(&mut *inner, id); + let conn = Self::remove_connection_records(&mut inner, id); Some(conn) } @@ -358,7 +358,7 @@ impl ConnectionTable { for t in 0..inner.conn_by_id.len() { out += &format!( " {} Connections: ({}/{})\n", - Self::index_to_protocol(t).to_string(), + Self::index_to_protocol(t), inner.conn_by_id[t].len(), inner.max_connections[t] ); diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index d1b7bc74..473d4d77 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -46,7 +46,7 @@ use storage_manager::*; #[cfg(target_arch = "wasm32")] use wasm::*; #[cfg(target_arch = "wasm32")] -pub use wasm::{LOCAL_NETWORK_CAPABILITIES, MAX_CAPABILITIES, PUBLIC_INTERNET_CAPABILITIES}; +pub use wasm::{/* LOCAL_NETWORK_CAPABILITIES, */ MAX_CAPABILITIES, PUBLIC_INTERNET_CAPABILITIES,}; //////////////////////////////////////////////////////////////////////////////////////// @@ -61,7 +61,7 @@ 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_PUNISHMENT_TIMEOUT_US: TimestampDuration = - TimestampDuration::new(3600_000_000u64); // 60 minutes + TimestampDuration::new(3_600_000_000_u64); // 60 minutes pub const ADDRESS_FILTER_TASK_INTERVAL_SECS: u32 = 60; pub const BOOT_MAGIC: &[u8; 4] = b"BOOT"; @@ -261,7 +261,7 @@ impl NetworkManager { where F: FnOnce(&VeilidConfigInner) -> R, { - f(&*self.unlocked_inner.config.get()) + f(&self.unlocked_inner.config.get()) } pub fn storage_manager(&self) -> StorageManager { self.unlocked_inner.storage_manager.clone() @@ -892,7 +892,7 @@ impl NetworkManager { data.len(), connection_descriptor ); - let remote_addr = connection_descriptor.remote_address().to_ip_addr(); + let remote_addr = connection_descriptor.remote_address().ip_addr(); // Network accounting self.stats_packet_rcvd(remote_addr, ByteCount::new(data.len() as u64)); @@ -900,7 +900,7 @@ impl NetworkManager { // 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 // more processing or forwarding - if data.len() == 0 { + if data.is_empty() { return Ok(true); } diff --git a/veilid-core/src/network_manager/native/discovery_context.rs b/veilid-core/src/network_manager/native/discovery_context.rs index 830dc28d..04101c70 100644 --- a/veilid-core/src/network_manager/native/discovery_context.rs +++ b/veilid-core/src/network_manager/native/discovery_context.rs @@ -141,10 +141,8 @@ impl DiscoveryContext { let dial_info_filter = DialInfoFilter::all() .with_protocol_type(protocol_type) .with_address_type(address_type); - let inbound_dial_info_entry_filter = RoutingTable::make_inbound_dial_info_entry_filter( - routing_domain, - dial_info_filter.clone(), - ); + let inbound_dial_info_entry_filter = + RoutingTable::make_inbound_dial_info_entry_filter(routing_domain, dial_info_filter); let disallow_relays_filter = Box::new( move |rti: &RoutingTableInner, v: Option>| { let v = v.unwrap(); @@ -199,7 +197,7 @@ impl DiscoveryContext { let node = node.filtered_clone( NodeRefFilter::new() .with_routing_domain(routing_domain) - .with_dial_info_filter(dial_info_filter.clone()), + .with_dial_info_filter(dial_info_filter), ); async move { if let Some(address) = this.request_public_address(node.clone()).await { @@ -219,9 +217,7 @@ impl DiscoveryContext { let mut external_address_infos = Vec::new(); - for ni in 0..nodes.len() - 1 { - let node = nodes[ni].clone(); - + for node in nodes.iter().take(nodes.len() - 1).cloned() { let gpa_future = get_public_address_func(node); unord.push(gpa_future); @@ -277,15 +273,15 @@ impl DiscoveryContext { node_ref.set_filter(None); // ask the node to send us a dial info validation receipt - let out = rpc_processor + + rpc_processor .rpc_call_validate_dial_info(node_ref.clone(), dial_info, redirect) .await .map_err(logthru_net!( "failed to send validate_dial_info to {:?}", node_ref )) - .unwrap_or(false); - out + .unwrap_or(false) } #[instrument(level = "trace", skip(self), ret)] @@ -307,9 +303,14 @@ impl DiscoveryContext { // Attempt a port mapping. If this doesn't succeed, it's not going to let Some(mapped_external_address) = igd_manager - .map_any_port(low_level_protocol_type, address_type, local_port, Some(external_1.address.to_ip_addr())) - .await else - { + .map_any_port( + low_level_protocol_type, + address_type, + local_port, + Some(external_1.address.ip_addr()), + ) + .await + else { return None; }; diff --git a/veilid-core/src/network_manager/native/igd_manager.rs b/veilid-core/src/network_manager/native/igd_manager.rs index ecf60618..32e3e3f2 100644 --- a/veilid-core/src/network_manager/native/igd_manager.rs +++ b/veilid-core/src/network_manager/native/igd_manager.rs @@ -184,7 +184,7 @@ impl IGDManager { let mut found = None; for (pmk, pmv) in &inner.port_maps { if pmk.llpt == llpt && pmk.at == at && pmv.mapped_port == mapped_port { - found = Some(pmk.clone()); + found = Some(*pmk); break; } } @@ -192,7 +192,7 @@ impl IGDManager { let _pmv = inner.port_maps.remove(&pmk).expect("key found but remove failed"); // Find gateway - let gw = Self::find_gateway(&mut *inner, at)?; + let gw = Self::find_gateway(&mut inner, at)?; // Unmap port match gw.remove_port(convert_llpt(llpt), mapped_port) { @@ -230,10 +230,10 @@ impl IGDManager { } // Get local ip address - let local_ip = Self::find_local_ip(&mut *inner, at)?; + let local_ip = Self::find_local_ip(&mut inner, at)?; // Find gateway - let gw = Self::find_gateway(&mut *inner, at)?; + let gw = Self::find_gateway(&mut inner, at)?; // Get external address let ext_ip = match gw.get_external_ip() { @@ -245,16 +245,12 @@ impl IGDManager { }; // Ensure external IP matches address type - if ext_ip.is_ipv4() { - if at != AddressType::IPV4 { - log_net!(debug "mismatched ip address type from igd, wanted v4, got v6"); - return None; - } - } else if ext_ip.is_ipv6() { - if at != AddressType::IPV6 { - log_net!(debug "mismatched ip address type from igd, wanted v6, got v4"); - return None; - } + if ext_ip.is_ipv4() && at != AddressType::IPV4 { + log_net!(debug "mismatched ip address type from igd, wanted v4, got v6"); + return None; + } else if ext_ip.is_ipv6() && at != AddressType::IPV6 { + log_net!(debug "mismatched ip address type from igd, wanted v6, got v4"); + return None; } if let Some(expected_external_address) = expected_external_address { diff --git a/veilid-core/src/network_manager/native/mod.rs b/veilid-core/src/network_manager/native/mod.rs index 02335a6a..0cd77f0b 100644 --- a/veilid-core/src/network_manager/native/mod.rs +++ b/veilid-core/src/network_manager/native/mod.rs @@ -421,7 +421,7 @@ impl Network { if self .network_manager() .address_filter() - .is_ip_addr_punished(dial_info.address().to_ip_addr()) + .is_ip_addr_punished(dial_info.address().ip_addr()) { return Ok(NetworkResult::no_connection_other("punished")); } @@ -462,7 +462,7 @@ impl Network { } // Network accounting self.network_manager() - .stats_packet_sent(dial_info.to_ip_addr(), ByteCount::new(data_len as u64)); + .stats_packet_sent(dial_info.ip_addr(), ByteCount::new(data_len as u64)); Ok(NetworkResult::Value(())) }) @@ -491,7 +491,7 @@ impl Network { if self .network_manager() .address_filter() - .is_ip_addr_punished(dial_info.address().to_ip_addr()) + .is_ip_addr_punished(dial_info.address().ip_addr()) { return Ok(NetworkResult::no_connection_other("punished")); } @@ -507,7 +507,7 @@ impl Network { .await .wrap_err("send message failure")?); self.network_manager() - .stats_packet_sent(dial_info.to_ip_addr(), ByteCount::new(data_len as u64)); + .stats_packet_sent(dial_info.ip_addr(), ByteCount::new(data_len as u64)); // receive single response let mut out = vec![0u8; MAX_MESSAGE_SIZE]; @@ -519,7 +519,7 @@ impl Network { .into_network_result()) .wrap_err("recv_message failure")?; - let recv_socket_addr = recv_addr.remote_address().to_socket_addr(); + let recv_socket_addr = recv_addr.remote_address().socket_addr(); self.network_manager() .stats_packet_rcvd(recv_socket_addr.ip(), ByteCount::new(recv_len as u64)); @@ -552,7 +552,7 @@ impl Network { network_result_try!(pnc.send(data).await.wrap_err("send failure")?); self.network_manager() - .stats_packet_sent(dial_info.to_ip_addr(), ByteCount::new(data_len as u64)); + .stats_packet_sent(dial_info.ip_addr(), ByteCount::new(data_len as u64)); let out = network_result_try!(network_result_try!(timeout(timeout_ms, pnc.recv()) @@ -560,10 +560,8 @@ impl Network { .into_network_result()) .wrap_err("recv failure")?); - self.network_manager().stats_packet_rcvd( - dial_info.to_ip_addr(), - ByteCount::new(out.len() as u64), - ); + self.network_manager() + .stats_packet_rcvd(dial_info.ip_addr(), ByteCount::new(out.len() as u64)); Ok(NetworkResult::Value(out)) } @@ -583,10 +581,10 @@ impl Network { // Handle connectionless protocol if descriptor.protocol_type() == ProtocolType::UDP { // send over the best udp socket we have bound since UDP is not connection oriented - let peer_socket_addr = descriptor.remote().to_socket_addr(); + let peer_socket_addr = descriptor.remote().socket_addr(); if let Some(ph) = self.find_best_udp_protocol_handler( &peer_socket_addr, - &descriptor.local().map(|sa| sa.to_socket_addr()), + &descriptor.local().map(|sa| sa.socket_addr()), ) { network_result_value_or_log!(ph.clone() .send_message(data.clone(), peer_socket_addr) @@ -612,7 +610,7 @@ impl Network { ConnectionHandleSendResult::Sent => { // Network accounting self.network_manager().stats_packet_sent( - descriptor.remote().to_socket_addr().ip(), + descriptor.remote().socket_addr().ip(), ByteCount::new(data_len as u64), ); @@ -676,7 +674,7 @@ impl Network { // Network accounting self.network_manager() - .stats_packet_sent(dial_info.to_ip_addr(), ByteCount::new(data_len as u64)); + .stats_packet_sent(dial_info.ip_addr(), ByteCount::new(data_len as u64)); Ok(NetworkResult::value(connection_descriptor)) }) @@ -701,7 +699,7 @@ impl Network { .with_interfaces(|interfaces| { trace!("interfaces: {:#?}", interfaces); - for (_name, intf) in interfaces { + for intf in interfaces.values() { // Skip networks that we should never encounter if intf.is_loopback() || !intf.is_running() { continue; diff --git a/veilid-core/src/network_manager/native/network_udp.rs b/veilid-core/src/network_manager/native/network_udp.rs index 3a0e6c17..2c06a378 100644 --- a/veilid-core/src/network_manager/native/network_udp.rs +++ b/veilid-core/src/network_manager/native/network_udp.rs @@ -68,7 +68,7 @@ impl Network { Ok(Ok((size, descriptor))) => { // Network accounting network_manager.stats_packet_rcvd( - descriptor.remote_address().to_ip_addr(), + descriptor.remote_address().ip_addr(), ByteCount::new(size as u64), ); diff --git a/veilid-core/src/network_manager/native/protocol/mod.rs b/veilid-core/src/network_manager/native/protocol/mod.rs index 6cbd79e9..e2fe457c 100644 --- a/veilid-core/src/network_manager/native/protocol/mod.rs +++ b/veilid-core/src/network_manager/native/protocol/mod.rs @@ -24,7 +24,7 @@ impl ProtocolNetworkConnection { timeout_ms: u32, address_filter: AddressFilter, ) -> io::Result> { - if address_filter.is_ip_addr_punished(dial_info.address().to_ip_addr()) { + if address_filter.is_ip_addr_punished(dial_info.address().ip_addr()) { return Ok(NetworkResult::no_connection_other("punished")); } match dial_info.protocol_type() { diff --git a/veilid-core/src/network_manager/native/protocol/tcp.rs b/veilid-core/src/network_manager/native/protocol/tcp.rs index edd2a6c9..653033fe 100644 --- a/veilid-core/src/network_manager/native/protocol/tcp.rs +++ b/veilid-core/src/network_manager/native/protocol/tcp.rs @@ -19,7 +19,7 @@ impl RawTcpNetworkConnection { } pub fn descriptor(&self) -> ConnectionDescriptor { - self.descriptor.clone() + self.descriptor } // #[instrument(level = "trace", err, skip(self))] @@ -132,11 +132,12 @@ impl RawTcpProtocolHandler { ) -> io::Result> { log_net!("TCP: on_accept_async: enter"); let mut peekbuf: [u8; PEEK_DETECT_LEN] = [0u8; PEEK_DETECT_LEN]; - if let Err(_) = timeout( + if (timeout( self.connection_initial_timeout_ms, ps.peek_exact(&mut peekbuf), ) - .await + .await) + .is_err() { return Ok(None); } diff --git a/veilid-core/src/network_manager/native/protocol/udp.rs b/veilid-core/src/network_manager/native/protocol/udp.rs index 53e793a3..f63a53f8 100644 --- a/veilid-core/src/network_manager/native/protocol/udp.rs +++ b/veilid-core/src/network_manager/native/protocol/udp.rs @@ -79,9 +79,9 @@ impl RawUdpProtocolHandler { }; #[cfg(feature = "verbose-tracing")] - tracing::Span::current().record("ret.len", &message_len); + tracing::Span::current().record("ret.len", message_len); #[cfg(feature = "verbose-tracing")] - tracing::Span::current().record("ret.descriptor", &format!("{:?}", descriptor).as_str()); + tracing::Span::current().record("ret.descriptor", format!("{:?}", descriptor).as_str()); Ok((message_len, descriptor)) } @@ -134,7 +134,7 @@ impl RawUdpProtocolHandler { ); #[cfg(feature = "verbose-tracing")] - tracing::Span::current().record("ret.descriptor", &format!("{:?}", descriptor).as_str()); + tracing::Span::current().record("ret.descriptor", format!("{:?}", descriptor).as_str()); Ok(NetworkResult::value(descriptor)) } @@ -143,7 +143,7 @@ impl RawUdpProtocolHandler { socket_addr: &SocketAddr, ) -> io::Result { // get local wildcard address for bind - let local_socket_addr = compatible_unspecified_socket_addr(&socket_addr); + let local_socket_addr = compatible_unspecified_socket_addr(socket_addr); let socket = UdpSocket::bind(local_socket_addr).await?; Ok(RawUdpProtocolHandler::new(Arc::new(socket), None)) } diff --git a/veilid-core/src/network_manager/native/protocol/ws.rs b/veilid-core/src/network_manager/native/protocol/ws.rs index 80153ba4..a98519d6 100644 --- a/veilid-core/src/network_manager/native/protocol/ws.rs +++ b/veilid-core/src/network_manager/native/protocol/ws.rs @@ -74,7 +74,7 @@ where } pub fn descriptor(&self) -> ConnectionDescriptor { - self.descriptor.clone() + self.descriptor } // #[instrument(level = "trace", err, skip(self))] @@ -232,7 +232,7 @@ impl WebsocketProtocolHandler { // This check could be loosened if necessary, but until we have a reason to do so // a stricter interpretation of HTTP is possible and desirable to reduce attack surface - if peek_buf.windows(4).position(|w| w == b"\r\n\r\n").is_none() { + if !peek_buf.windows(4).any(|w| w == b"\r\n\r\n") { return Ok(None); } @@ -306,7 +306,7 @@ impl WebsocketProtocolHandler { // Make our connection descriptor let descriptor = ConnectionDescriptor::new( - dial_info.to_peer_address(), + dial_info.peer_address(), SocketAddress::from_socket_addr(actual_local_addr), ); @@ -339,8 +339,7 @@ impl Callback for WebsocketProtocolHandler { || request .headers() .iter() - .find(|h| (h.0.as_str().len() + h.1.as_bytes().len()) > MAX_WS_HEADER_LENGTH) - .is_some() + .any(|h| (h.0.as_str().len() + h.1.as_bytes().len()) > MAX_WS_HEADER_LENGTH) { let mut error_response = ErrorResponse::new(None); *error_response.status_mut() = StatusCode::NOT_FOUND; diff --git a/veilid-core/src/network_manager/native/start_protocols.rs b/veilid-core/src/network_manager/native/start_protocols.rs index 416225c4..c5511563 100644 --- a/veilid-core/src/network_manager/native/start_protocols.rs +++ b/veilid-core/src/network_manager/native/start_protocols.rs @@ -312,7 +312,7 @@ impl Network { // if no other public address is specified if !detect_address_changes && public_address.is_none() - && routing_table.ensure_dial_info_is_valid(RoutingDomain::PublicInternet, &di) + && routing_table.ensure_dial_info_is_valid(RoutingDomain::PublicInternet, di) { editor_public_internet.register_dial_info(di.clone(), DialInfoClass::Direct)?; static_public = true; @@ -449,7 +449,7 @@ impl Network { for socket_address in socket_addresses { // Skip addresses we already did - if registered_addresses.contains(&socket_address.to_ip_addr()) { + if registered_addresses.contains(&socket_address.ip_addr()) { continue; } // Build dial info request url @@ -628,7 +628,7 @@ impl Network { } // Register interface dial info editor_local_network.register_dial_info(di.clone(), DialInfoClass::Direct)?; - registered_addresses.insert(socket_address.to_ip_addr()); + registered_addresses.insert(socket_address.ip_addr()); } // Add static public dialinfo if it's configured diff --git a/veilid-core/src/network_manager/network_connection.rs b/veilid-core/src/network_manager/network_connection.rs index 59f1a018..247866f0 100644 --- a/veilid-core/src/network_manager/network_connection.rs +++ b/veilid-core/src/network_manager/network_connection.rs @@ -52,7 +52,7 @@ pub struct DummyNetworkConnection { impl DummyNetworkConnection { pub fn descriptor(&self) -> ConnectionDescriptor { - self.descriptor.clone() + self.descriptor } // pub fn close(&self) -> io::Result<()> { // Ok(()) @@ -144,7 +144,7 @@ impl NetworkConnection { local_stop_token, manager_stop_token, connection_id, - descriptor.clone(), + descriptor, receiver, protocol_connection, stats.clone(), @@ -168,11 +168,11 @@ impl NetworkConnection { } pub fn connection_descriptor(&self) -> ConnectionDescriptor { - self.descriptor.clone() + self.descriptor } pub fn get_handle(&self) -> ConnectionHandle { - ConnectionHandle::new(self.connection_id, self.descriptor.clone(), self.sender.clone()) + ConnectionHandle::new(self.connection_id, self.descriptor, self.sender.clone()) } pub fn is_protected(&self) -> bool { @@ -197,12 +197,12 @@ impl NetworkConnection { message: Vec, ) -> io::Result> { let ts = get_aligned_timestamp(); - let out = network_result_try!(protocol_connection.send(message).await?); + network_result_try!(protocol_connection.send(message).await?); let mut stats = stats.lock(); stats.last_message_sent_time.max_assign(Some(ts)); - Ok(NetworkResult::Value(out)) + Ok(NetworkResult::Value(())) } #[cfg_attr(feature="verbose-tracing", instrument(level="trace", skip(stats), fields(ret.len)))] @@ -234,6 +234,7 @@ impl NetworkConnection { } // Connection receiver loop + #[allow(clippy::too_many_arguments)] fn process_connection( connection_manager: ConnectionManager, local_stop_token: StopToken, @@ -316,19 +317,19 @@ impl NetworkConnection { let peer_address = protocol_connection.descriptor().remote(); // Check to see if it is punished - if address_filter.is_ip_addr_punished(peer_address.to_socket_addr().ip()) { + if address_filter.is_ip_addr_punished(peer_address.socket_addr().ip()) { return RecvLoopAction::Finish; } // Check for connection close if v.is_no_connection() { - log_net!(debug "Connection closed from: {} ({})", peer_address.to_socket_addr(), peer_address.protocol_type()); + log_net!(debug "Connection closed from: {} ({})", peer_address.socket_addr(), peer_address.protocol_type()); return RecvLoopAction::Finish; } // Punish invalid framing (tcp framing or websocket framing) if v.is_invalid_message() { - address_filter.punish_ip_addr(peer_address.to_socket_addr().ip()); + address_filter.punish_ip_addr(peer_address.socket_addr().ip()); return RecvLoopAction::Finish; } diff --git a/veilid-core/src/network_manager/send_data.rs b/veilid-core/src/network_manager/send_data.rs index beecf26b..6818dbc3 100644 --- a/veilid-core/src/network_manager/send_data.rs +++ b/veilid-core/src/network_manager/send_data.rs @@ -338,7 +338,7 @@ impl NetworkManager { let routing_table = self.routing_table(); // If a node is punished, then don't try to contact it - if target_node_ref.node_ids().iter().find(|nid| self.address_filter().is_node_id_punished(**nid)).is_some() { + if target_node_ref.node_ids().iter().any(|nid| self.address_filter().is_node_id_punished(*nid)) { return Ok(NodeContactMethod::Unreachable); } @@ -396,7 +396,7 @@ impl NetworkManager { dial_info_failures_map.insert(did.dial_info, ts); } } - let dif_sort: Option core::cmp::Ordering>> = if dial_info_failures_map.is_empty() { + let dif_sort: Option> = if dial_info_failures_map.is_empty() { None } else { Some(Arc::new(move |a: &DialInfoDetail, b: &DialInfoDetail| { diff --git a/veilid-core/src/network_manager/stats.rs b/veilid-core/src/network_manager/stats.rs index 24836707..f96da01d 100644 --- a/veilid-core/src/network_manager/stats.rs +++ b/veilid-core/src/network_manager/stats.rs @@ -73,7 +73,7 @@ impl NetworkManager { inner.stats.clone() } - pub fn get_veilid_state(&self) -> VeilidStateNetwork { + pub fn get_veilid_state(&self) -> Box { let has_state = self .unlocked_inner .components @@ -83,12 +83,12 @@ impl NetworkManager { .unwrap_or(false); if !has_state { - return VeilidStateNetwork { + return Box::new(VeilidStateNetwork { started: false, bps_down: 0.into(), bps_up: 0.into(), peers: Vec::new(), - }; + }); } let routing_table = self.routing_table(); @@ -100,7 +100,7 @@ impl NetworkManager { ) }; - VeilidStateNetwork { + Box::new(VeilidStateNetwork { started: true, bps_down, bps_up, @@ -119,7 +119,7 @@ impl NetworkManager { } out }, - } + }) } pub(super) fn send_network_update(&self) { diff --git a/veilid-core/src/network_manager/tasks/public_address_check.rs b/veilid-core/src/network_manager/tasks/public_address_check.rs index e106af9f..b227dabd 100644 --- a/veilid-core/src/network_manager/tasks/public_address_check.rs +++ b/veilid-core/src/network_manager/tasks/public_address_check.rs @@ -11,7 +11,7 @@ impl NetworkManager { ) -> EyreResult<()> { // go through public_address_inconsistencies_table and time out things that have expired let mut inner = self.inner.lock(); - for (_, pait_v) in &mut inner.public_address_inconsistencies_table { + for pait_v in inner.public_address_inconsistencies_table.values_mut() { let mut expired = Vec::new(); for (addr, exp_ts) in pait_v.iter() { if *exp_ts <= cur_ts { @@ -79,7 +79,7 @@ impl NetworkManager { // Get the ip(block) this report is coming from let reporting_ipblock = ip_to_ipblock( ip6_prefix_size, - connection_descriptor.remote_address().to_ip_addr(), + connection_descriptor.remote_address().ip_addr(), ); // Reject public address reports from nodes that we know are behind symmetric nat or @@ -94,7 +94,7 @@ impl NetworkManager { // If the socket address reported is the same as the reporter, then this is coming through a relay // or it should be ignored due to local proximity (nodes on the same network block should not be trusted as // public ip address reporters, only disinterested parties) - if reporting_ipblock == ip_to_ipblock(ip6_prefix_size, socket_address.to_ip_addr()) { + if reporting_ipblock == ip_to_ipblock(ip6_prefix_size, socket_address.ip_addr()) { return; } @@ -192,7 +192,7 @@ impl NetworkManager { let pait = inner .public_address_inconsistencies_table .entry(addr_proto_type_key) - .or_insert_with(|| HashMap::new()); + .or_insert_with(HashMap::new); for i in &inconsistencies { pait.insert(*i, exp_ts); } @@ -204,7 +204,7 @@ impl NetworkManager { let pait = inner .public_address_inconsistencies_table .entry(addr_proto_type_key) - .or_insert_with(|| HashMap::new()); + .or_insert_with(HashMap::new); let exp_ts = get_aligned_timestamp() + PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US; for i in inconsistencies { diff --git a/veilid-core/src/network_manager/types/address.rs b/veilid-core/src/network_manager/types/address.rs index e89ed185..6b0f510a 100644 --- a/veilid-core/src/network_manager/types/address.rs +++ b/veilid-core/src/network_manager/types/address.rs @@ -71,16 +71,16 @@ impl Address { } } } - pub fn to_ip_addr(&self) -> IpAddr { + pub fn ip_addr(&self) -> IpAddr { match self { Self::IPV4(a) => IpAddr::V4(*a), Self::IPV6(a) => IpAddr::V6(*a), } } - pub fn to_socket_addr(&self, port: u16) -> SocketAddr { - SocketAddr::new(self.to_ip_addr(), port) + pub fn socket_addr(&self, port: u16) -> SocketAddr { + SocketAddr::new(self.ip_addr(), port) } - pub fn to_canonical(&self) -> Address { + pub fn canonical(&self) -> Address { match self { Address::IPV4(v4) => Address::IPV4(*v4), Address::IPV6(v6) => match v6.to_ipv4() { diff --git a/veilid-core/src/network_manager/types/address_type.rs b/veilid-core/src/network_manager/types/address_type.rs index 53ddcb4f..4c7033d6 100644 --- a/veilid-core/src/network_manager/types/address_type.rs +++ b/veilid-core/src/network_manager/types/address_type.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] use super::*; -#[allow(clippy::derive_hash_xor_eq)] +#[allow(clippy::derived_hash_with_manual_eq)] #[derive(Debug, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumSetType)] #[enumset(repr = "u8")] pub enum AddressType { diff --git a/veilid-core/src/network_manager/types/dial_info/mod.rs b/veilid-core/src/network_manager/types/dial_info/mod.rs index 1450b3b3..1940b80e 100644 --- a/veilid-core/src/network_manager/types/dial_info/mod.rs +++ b/veilid-core/src/network_manager/types/dial_info/mod.rs @@ -36,10 +36,10 @@ impl fmt::Display for DialInfo { let split_url = SplitUrl::from_str(&url).unwrap(); match split_url.host { SplitUrlHost::Hostname(_) => { - write!(f, "ws|{}|{}", di.socket_address.to_ip_addr(), di.request) + write!(f, "ws|{}|{}", di.socket_address.ip_addr(), di.request) } SplitUrlHost::IpAddr(a) => { - if di.socket_address.to_ip_addr() == a { + if di.socket_address.ip_addr() == a { write!(f, "ws|{}", di.request) } else { panic!("resolved address does not match url: {}", di.request); @@ -52,7 +52,7 @@ impl fmt::Display for DialInfo { let split_url = SplitUrl::from_str(&url).unwrap(); match split_url.host { SplitUrlHost::Hostname(_) => { - write!(f, "wss|{}|{}", di.socket_address.to_ip_addr(), di.request) + write!(f, "wss|{}|{}", di.socket_address.ip_addr(), di.request) } SplitUrlHost::IpAddr(_) => { panic!( @@ -143,22 +143,22 @@ impl FromStr for DialInfo { impl DialInfo { pub fn udp_from_socketaddr(socket_addr: SocketAddr) -> Self { Self::UDP(DialInfoUDP { - socket_address: SocketAddress::from_socket_addr(socket_addr).to_canonical(), + socket_address: SocketAddress::from_socket_addr(socket_addr).canonical(), }) } pub fn tcp_from_socketaddr(socket_addr: SocketAddr) -> Self { Self::TCP(DialInfoTCP { - socket_address: SocketAddress::from_socket_addr(socket_addr).to_canonical(), + socket_address: SocketAddress::from_socket_addr(socket_addr).canonical(), }) } pub fn udp(socket_address: SocketAddress) -> Self { Self::UDP(DialInfoUDP { - socket_address: socket_address.to_canonical(), + socket_address: socket_address.canonical(), }) } pub fn tcp(socket_address: SocketAddress) -> Self { Self::TCP(DialInfoTCP { - socket_address: socket_address.to_canonical(), + socket_address: socket_address.canonical(), }) } pub fn try_ws(socket_address: SocketAddress, url: String) -> VeilidAPIResult { @@ -173,7 +173,7 @@ impl DialInfo { apibail_parse_error!("socket address port doesn't match url port", url); } if let SplitUrlHost::IpAddr(a) = split_url.host { - if socket_address.to_ip_addr() != a { + if socket_address.ip_addr() != a { apibail_parse_error!( format!("request address does not match socket address: {}", a), socket_address @@ -181,7 +181,7 @@ impl DialInfo { } } Ok(Self::WS(DialInfoWS { - socket_address: socket_address.to_canonical(), + socket_address: socket_address.canonical(), request: url[5..].to_string(), })) } @@ -203,7 +203,7 @@ impl DialInfo { ); } Ok(Self::WSS(DialInfoWSS { - socket_address: socket_address.to_canonical(), + socket_address: socket_address.canonical(), request: url[6..].to_string(), })) } @@ -242,12 +242,12 @@ impl DialInfo { Self::WSS(di) => di.socket_address, } } - pub fn to_ip_addr(&self) -> IpAddr { + pub fn ip_addr(&self) -> IpAddr { match self { - Self::UDP(di) => di.socket_address.to_ip_addr(), - Self::TCP(di) => di.socket_address.to_ip_addr(), - Self::WS(di) => di.socket_address.to_ip_addr(), - Self::WSS(di) => di.socket_address.to_ip_addr(), + Self::UDP(di) => di.socket_address.ip_addr(), + Self::TCP(di) => di.socket_address.ip_addr(), + Self::WS(di) => di.socket_address.ip_addr(), + Self::WSS(di) => di.socket_address.ip_addr(), } } pub fn port(&self) -> u16 { @@ -268,13 +268,13 @@ impl DialInfo { } pub fn to_socket_addr(&self) -> SocketAddr { match self { - Self::UDP(di) => di.socket_address.to_socket_addr(), - Self::TCP(di) => di.socket_address.to_socket_addr(), - Self::WS(di) => di.socket_address.to_socket_addr(), - Self::WSS(di) => di.socket_address.to_socket_addr(), + Self::UDP(di) => di.socket_address.socket_addr(), + Self::TCP(di) => di.socket_address.socket_addr(), + Self::WS(di) => di.socket_address.socket_addr(), + Self::WSS(di) => di.socket_address.socket_addr(), } } - pub fn to_peer_address(&self) -> PeerAddress { + pub fn peer_address(&self) -> PeerAddress { match self { Self::UDP(di) => PeerAddress::new(di.socket_address, ProtocolType::UDP), Self::TCP(di) => PeerAddress::new(di.socket_address, ProtocolType::TCP), @@ -376,11 +376,11 @@ impl DialInfo { "udp" => Self::udp_from_socketaddr(sa), "tcp" => Self::tcp_from_socketaddr(sa), "ws" => Self::try_ws( - SocketAddress::from_socket_addr(sa).to_canonical(), + SocketAddress::from_socket_addr(sa).canonical(), url.to_string(), )?, "wss" => Self::try_wss( - SocketAddress::from_socket_addr(sa).to_canonical(), + SocketAddress::from_socket_addr(sa).canonical(), url.to_string(), )?, _ => { @@ -395,13 +395,13 @@ impl DialInfo { match self { DialInfo::UDP(di) => ( format!("U{}", di.socket_address.port()), - intf::ptr_lookup(di.socket_address.to_ip_addr()) + intf::ptr_lookup(di.socket_address.ip_addr()) .await .unwrap_or_else(|_| di.socket_address.to_string()), ), DialInfo::TCP(di) => ( format!("T{}", di.socket_address.port()), - intf::ptr_lookup(di.socket_address.to_ip_addr()) + intf::ptr_lookup(di.socket_address.ip_addr()) .await .unwrap_or_else(|_| di.socket_address.to_string()), ), @@ -447,11 +447,11 @@ impl DialInfo { } pub async fn to_url(&self) -> String { match self { - DialInfo::UDP(di) => intf::ptr_lookup(di.socket_address.to_ip_addr()) + DialInfo::UDP(di) => intf::ptr_lookup(di.socket_address.ip_addr()) .await .map(|h| format!("udp://{}:{}", h, di.socket_address.port())) .unwrap_or_else(|_| format!("udp://{}", di.socket_address)), - DialInfo::TCP(di) => intf::ptr_lookup(di.socket_address.to_ip_addr()) + DialInfo::TCP(di) => intf::ptr_lookup(di.socket_address.ip_addr()) .await .map(|h| format!("tcp://{}:{}", h, di.socket_address.port())) .unwrap_or_else(|_| format!("tcp://{}", di.socket_address)), diff --git a/veilid-core/src/network_manager/types/low_level_protocol_type.rs b/veilid-core/src/network_manager/types/low_level_protocol_type.rs index be552282..c9e96d3b 100644 --- a/veilid-core/src/network_manager/types/low_level_protocol_type.rs +++ b/veilid-core/src/network_manager/types/low_level_protocol_type.rs @@ -4,7 +4,7 @@ use super::*; // Keep member order appropriate for sorting < preference // Must match DialInfo order -#[allow(clippy::derive_hash_xor_eq)] +#[allow(clippy::derived_hash_with_manual_eq)] #[derive(Debug, PartialOrd, Ord, Hash, EnumSetType, Serialize, Deserialize)] #[enumset(repr = "u8")] pub enum LowLevelProtocolType { diff --git a/veilid-core/src/network_manager/types/peer_address.rs b/veilid-core/src/network_manager/types/peer_address.rs index e505aa1c..1e56bd9b 100644 --- a/veilid-core/src/network_manager/types/peer_address.rs +++ b/veilid-core/src/network_manager/types/peer_address.rs @@ -10,7 +10,7 @@ pub struct PeerAddress { impl PeerAddress { pub fn new(socket_address: SocketAddress, protocol_type: ProtocolType) -> Self { Self { - socket_address: socket_address.to_canonical(), + socket_address: socket_address.canonical(), protocol_type, } } @@ -23,8 +23,8 @@ impl PeerAddress { self.protocol_type } - pub fn to_socket_addr(&self) -> SocketAddr { - self.socket_address.to_socket_addr() + pub fn socket_addr(&self) -> SocketAddr { + self.socket_address.socket_addr() } pub fn address_type(&self) -> AddressType { @@ -42,7 +42,10 @@ impl FromStr for PeerAddress { type Err = VeilidAPIError; fn from_str(s: &str) -> VeilidAPIResult { let Some((first, second)) = s.split_once(':') else { - return Err(VeilidAPIError::parse_error("PeerAddress is missing a colon: {}", s)); + return Err(VeilidAPIError::parse_error( + "PeerAddress is missing a colon: {}", + s, + )); }; let protocol_type = ProtocolType::from_str(first)?; let socket_address = SocketAddress::from_str(second)?; diff --git a/veilid-core/src/network_manager/types/protocol_type.rs b/veilid-core/src/network_manager/types/protocol_type.rs index 54319322..0279d307 100644 --- a/veilid-core/src/network_manager/types/protocol_type.rs +++ b/veilid-core/src/network_manager/types/protocol_type.rs @@ -3,7 +3,7 @@ use super::*; // Keep member order appropriate for sorting < preference // Must match DialInfo order -#[allow(clippy::derive_hash_xor_eq)] +#[allow(clippy::derived_hash_with_manual_eq)] #[derive(Debug, PartialOrd, Ord, Hash, EnumSetType, Serialize, Deserialize)] #[enumset(repr = "u8")] pub enum ProtocolType { diff --git a/veilid-core/src/network_manager/types/socket_address.rs b/veilid-core/src/network_manager/types/socket_address.rs index 23700543..ad30bbdf 100644 --- a/veilid-core/src/network_manager/types/socket_address.rs +++ b/veilid-core/src/network_manager/types/socket_address.rs @@ -34,27 +34,27 @@ impl SocketAddress { self.port = port } pub fn with_port(&self, port: u16) -> Self { - let mut sa = self.clone(); + let mut sa = *self; sa.port = port; sa } - pub fn to_canonical(&self) -> SocketAddress { + pub fn canonical(&self) -> SocketAddress { SocketAddress { - address: self.address.to_canonical(), + address: self.address.canonical(), port: self.port, } } - pub fn to_ip_addr(&self) -> IpAddr { - self.address.to_ip_addr() + pub fn ip_addr(&self) -> IpAddr { + self.address.ip_addr() } - pub fn to_socket_addr(&self) -> SocketAddr { - self.address.to_socket_addr(self.port) + pub fn socket_addr(&self) -> SocketAddr { + self.address.socket_addr(self.port) } } impl fmt::Display for SocketAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", self.to_socket_addr()) + write!(f, "{}", self.socket_addr()) } } diff --git a/veilid-core/src/network_manager/wasm/mod.rs b/veilid-core/src/network_manager/wasm/mod.rs index b9e66e8c..5924c5eb 100644 --- a/veilid-core/src/network_manager/wasm/mod.rs +++ b/veilid-core/src/network_manager/wasm/mod.rs @@ -32,18 +32,18 @@ pub const PUBLIC_INTERNET_CAPABILITIES: [Capability; PUBLIC_INTERNET_CAPABILITIE CAP_BLOCKSTORE, ]; -#[cfg(feature = "unstable-blockstore")] -const LOCAL_NETWORK_CAPABILITIES_LEN: usize = 3; -#[cfg(not(feature = "unstable-blockstore"))] -const LOCAL_NETWORK_CAPABILITIES_LEN: usize = 2; +// #[cfg(feature = "unstable-blockstore")] +// const LOCAL_NETWORK_CAPABILITIES_LEN: usize = 3; +// #[cfg(not(feature = "unstable-blockstore"))] +// const LOCAL_NETWORK_CAPABILITIES_LEN: usize = 2; -pub const LOCAL_NETWORK_CAPABILITIES: [Capability; LOCAL_NETWORK_CAPABILITIES_LEN] = [ - //CAP_RELAY, - CAP_DHT, - CAP_APPMESSAGE, - #[cfg(feature = "unstable-blockstore")] - CAP_BLOCKSTORE, -]; +// pub const LOCAL_NETWORK_CAPABILITIES: [Capability; LOCAL_NETWORK_CAPABILITIES_LEN] = [ +// //CAP_RELAY, +// CAP_DHT, +// CAP_APPMESSAGE, +// #[cfg(feature = "unstable-blockstore")] +// CAP_BLOCKSTORE, +// ]; pub const MAX_CAPABILITIES: usize = 64; @@ -149,7 +149,7 @@ impl Network { if self .network_manager() .address_filter() - .is_ip_addr_punished(dial_info.address().to_ip_addr()) + .is_ip_addr_punished(dial_info.address().ip_addr()) { return Ok(NetworkResult::no_connection_other("punished")); } @@ -173,7 +173,7 @@ impl Network { // Network accounting self.network_manager() - .stats_packet_sent(dial_info.to_ip_addr(), ByteCount::new(data_len as u64)); + .stats_packet_sent(dial_info.ip_addr(), ByteCount::new(data_len as u64)); Ok(NetworkResult::Value(())) }) @@ -202,7 +202,7 @@ impl Network { if self .network_manager() .address_filter() - .is_ip_addr_punished(dial_info.address().to_ip_addr()) + .is_ip_addr_punished(dial_info.address().ip_addr()) { return Ok(NetworkResult::no_connection_other("punished")); } @@ -227,7 +227,7 @@ impl Network { network_result_try!(pnc.send(data).await.wrap_err("send failure")?); self.network_manager() - .stats_packet_sent(dial_info.to_ip_addr(), ByteCount::new(data_len as u64)); + .stats_packet_sent(dial_info.ip_addr(), ByteCount::new(data_len as u64)); let out = network_result_try!(network_result_try!(timeout(timeout_ms, pnc.recv()) @@ -235,10 +235,8 @@ impl Network { .into_network_result()) .wrap_err("recv failure")?); - self.network_manager().stats_packet_rcvd( - dial_info.to_ip_addr(), - ByteCount::new(out.len() as u64), - ); + self.network_manager() + .stats_packet_rcvd(dial_info.ip_addr(), ByteCount::new(out.len() as u64)); Ok(NetworkResult::Value(out)) } @@ -273,7 +271,7 @@ impl Network { ConnectionHandleSendResult::Sent => { // Network accounting self.network_manager().stats_packet_sent( - descriptor.remote().to_socket_addr().ip(), + descriptor.remote().socket_addr().ip(), ByteCount::new(data_len as u64), ); @@ -324,7 +322,7 @@ impl Network { // Network accounting self.network_manager() - .stats_packet_sent(dial_info.to_ip_addr(), ByteCount::new(data_len as u64)); + .stats_packet_sent(dial_info.ip_addr(), ByteCount::new(data_len as u64)); Ok(NetworkResult::value(connection_descriptor)) }) @@ -434,11 +432,11 @@ impl Network { Vec::new() } - pub fn get_local_port(&self, protocol_type: ProtocolType) -> Option { + pub fn get_local_port(&self, _protocol_type: ProtocolType) -> Option { None } - pub fn get_preferred_local_address(&self, dial_info: &DialInfo) -> Option { + pub fn get_preferred_local_address(&self, _dial_info: &DialInfo) -> Option { None } @@ -456,7 +454,7 @@ impl Network { } pub fn get_protocol_config(&self) -> ProtocolConfig { - self.inner.lock().protocol_config.clone() + self.inner.lock().protocol_config } ////////////////////////////////////////// diff --git a/veilid-core/src/network_manager/wasm/protocol/mod.rs b/veilid-core/src/network_manager/wasm/protocol/mod.rs index 846cff18..c3f3392b 100644 --- a/veilid-core/src/network_manager/wasm/protocol/mod.rs +++ b/veilid-core/src/network_manager/wasm/protocol/mod.rs @@ -19,7 +19,7 @@ impl ProtocolNetworkConnection { timeout_ms: u32, address_filter: AddressFilter, ) -> io::Result> { - if address_filter.is_ip_addr_punished(dial_info.address().to_ip_addr()) { + if address_filter.is_ip_addr_punished(dial_info.address().ip_addr()) { return Ok(NetworkResult::no_connection_other("punished")); } match dial_info.protocol_type() { diff --git a/veilid-core/src/network_manager/wasm/protocol/ws.rs b/veilid-core/src/network_manager/wasm/protocol/ws.rs index 127122b3..670c70c9 100644 --- a/veilid-core/src/network_manager/wasm/protocol/ws.rs +++ b/veilid-core/src/network_manager/wasm/protocol/ws.rs @@ -56,7 +56,7 @@ impl WebsocketNetworkConnection { } pub fn descriptor(&self) -> ConnectionDescriptor { - self.descriptor.clone() + self.descriptor } // #[instrument(level = "trace", err, skip(self))] @@ -144,7 +144,7 @@ impl WebsocketProtocolHandler { // Make our connection descriptor let wnc = WebsocketNetworkConnection::new( - ConnectionDescriptor::new_no_local(dial_info.to_peer_address()), + ConnectionDescriptor::new_no_local(dial_info.peer_address()), wsmeta, wsio, ); diff --git a/veilid-core/src/receipt_manager.rs b/veilid-core/src/receipt_manager.rs index 0df15aa1..416e5573 100644 --- a/veilid-core/src/receipt_manager.rs +++ b/veilid-core/src/receipt_manager.rs @@ -261,9 +261,8 @@ impl ReceiptManager { // Wait on all the multi-call callbacks loop { - match callbacks.next().timeout_at(stop_token.clone()).await { - Ok(Some(_)) => {} - Ok(None) | Err(_) => break, + if let Ok(None) | Err(_) = callbacks.next().timeout_at(stop_token.clone()).await { + break; } } } @@ -307,7 +306,7 @@ impl ReceiptManager { // Wait for everything to stop debug!("waiting for timeout task to stop"); - if !timeout_task.join().await.is_ok() { + if timeout_task.join().await.is_err() { panic!("joining timeout task failed"); } @@ -333,7 +332,7 @@ impl ReceiptManager { let mut inner = self.inner.lock(); inner.records_by_nonce.insert(receipt_nonce, record); - Self::update_next_oldest_timestamp(&mut *inner); + Self::update_next_oldest_timestamp(&mut inner); } pub fn record_single_shot_receipt( @@ -351,7 +350,7 @@ impl ReceiptManager { let mut inner = self.inner.lock(); inner.records_by_nonce.insert(receipt_nonce, record); - Self::update_next_oldest_timestamp(&mut *inner); + Self::update_next_oldest_timestamp(&mut inner); } fn update_next_oldest_timestamp(inner: &mut ReceiptManagerInner) { @@ -382,7 +381,7 @@ impl ReceiptManager { bail!("receipt not recorded"); } }; - Self::update_next_oldest_timestamp(&mut *inner); + Self::update_next_oldest_timestamp(&mut inner); record }; @@ -448,14 +447,12 @@ impl ReceiptManager { let receipt_event = match receipt_returned { ReceiptReturned::OutOfBand => ReceiptEvent::ReturnedOutOfBand, ReceiptReturned::Safety => ReceiptEvent::ReturnedSafety, - ReceiptReturned::InBand { - ref inbound_noderef, - } => ReceiptEvent::ReturnedInBand { - inbound_noderef: inbound_noderef.clone(), - }, - ReceiptReturned::Private { ref private_route } => ReceiptEvent::ReturnedPrivate { - private_route: private_route.clone(), - }, + ReceiptReturned::InBand { inbound_noderef } => { + ReceiptEvent::ReturnedInBand { inbound_noderef } + } + ReceiptReturned::Private { private_route } => { + ReceiptEvent::ReturnedPrivate { private_route } + } }; let callback_future = Self::perform_callback(receipt_event, &mut record_mut); @@ -464,7 +461,7 @@ impl ReceiptManager { if record_mut.returns_so_far == record_mut.expected_returns { inner.records_by_nonce.remove(&receipt_nonce); - Self::update_next_oldest_timestamp(&mut *inner); + Self::update_next_oldest_timestamp(&mut inner); } (callback_future, stop_token) }; diff --git a/veilid-core/src/routing_table/bucket.rs b/veilid-core/src/routing_table/bucket.rs index f34405f6..78323ed3 100644 --- a/veilid-core/src/routing_table/bucket.rs +++ b/veilid-core/src/routing_table/bucket.rs @@ -75,8 +75,8 @@ impl Bucket { }); } let bucket_data = SerializedBucketData { entries }; - let out = serialize_json_bytes(&bucket_data); - out + + serialize_json_bytes(bucket_data) } /// Create a new entry with a node_id of this crypto kind and return it @@ -129,11 +129,8 @@ impl Bucket { let mut extra_entries = bucket_len - bucket_depth; // Get the sorted list of entries by their kick order - let mut sorted_entries: Vec<(PublicKey, Arc)> = self - .entries - .iter() - .map(|(k, v)| (k.clone(), v.clone())) - .collect(); + let mut sorted_entries: Vec<(PublicKey, Arc)> = + self.entries.iter().map(|(k, v)| (*k, v.clone())).collect(); let cur_ts = get_aligned_timestamp(); sorted_entries.sort_by(|a, b| -> core::cmp::Ordering { if a.0 == b.0 { diff --git a/veilid-core/src/routing_table/bucket_entry.rs b/veilid-core/src/routing_table/bucket_entry.rs index 0d6fd71b..2d768fec 100644 --- a/veilid-core/src/routing_table/bucket_entry.rs +++ b/veilid-core/src/routing_table/bucket_entry.rs @@ -223,7 +223,7 @@ impl BucketEntryInner { // Lower timestamp to the front, recent or no timestamp is at the end if let Some(e1_ts) = &e1.peer_stats.rpc_stats.first_consecutive_seen_ts { if let Some(e2_ts) = &e2.peer_stats.rpc_stats.first_consecutive_seen_ts { - e1_ts.cmp(&e2_ts) + e1_ts.cmp(e2_ts) } else { std::cmp::Ordering::Less } @@ -437,7 +437,7 @@ impl BucketEntryInner { // Clears the table of last connections except the most recent one pub fn clear_last_connections_except_latest(&mut self) { - if self.last_connections.len() == 0 { + if self.last_connections.is_empty() { // No last_connections return; } @@ -454,7 +454,7 @@ impl BucketEntryInner { let Some(most_recent_connection) = most_recent_connection else { return; }; - for (k, _) in &self.last_connections { + for k in self.last_connections.keys() { if k != most_recent_connection { dead_keys.push(k.clone()); } @@ -492,7 +492,7 @@ impl BucketEntryInner { } if !only_live { - return Some(v.clone()); + return Some(*v); } // Check if the connection is still considered live @@ -509,7 +509,7 @@ impl BucketEntryInner { }; if alive { - Some(v.clone()) + Some(*v) } else { None } @@ -583,13 +583,11 @@ impl BucketEntryInner { RoutingDomain::LocalNetwork => self .local_network .node_status - .as_ref() - .map(|ns| ns.clone()), + .as_ref().cloned(), RoutingDomain::PublicInternet => self .public_internet .node_status - .as_ref() - .map(|ns| ns.clone()), + .as_ref().cloned() } } @@ -892,7 +890,7 @@ impl BucketEntry { F: FnOnce(&RoutingTableInner, &BucketEntryInner) -> R, { let inner = self.inner.read(); - f(rti, &*inner) + f(rti, &inner) } // Note, that this requires -also- holding the RoutingTable write lock, as a @@ -902,7 +900,7 @@ impl BucketEntry { F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner) -> R, { let mut inner = self.inner.write(); - f(rti, &mut *inner) + f(rti, &mut inner) } // Internal inner access for RoutingTableInner only @@ -911,7 +909,7 @@ impl BucketEntry { F: FnOnce(&BucketEntryInner) -> R, { let inner = self.inner.read(); - f(&*inner) + f(&inner) } // Internal inner access for RoutingTableInner only @@ -920,7 +918,7 @@ impl BucketEntry { F: FnOnce(&mut BucketEntryInner) -> R, { let mut inner = self.inner.write(); - f(&mut *inner) + f(&mut inner) } } diff --git a/veilid-core/src/routing_table/debug.rs b/veilid-core/src/routing_table/debug.rs index 64207987..3e2e02b3 100644 --- a/veilid-core/src/routing_table/debug.rs +++ b/veilid-core/src/routing_table/debug.rs @@ -112,7 +112,7 @@ impl RoutingTable { let mut out = String::new(); out += &format!("Entries: {}\n", inner.bucket_entry_count()); - out += &format!(" Live:\n"); + out += " Live:\n"; for ec in inner.cached_entry_counts() { let routing_domain = ec.0 .0; let crypto_kind = ec.0 .1; diff --git a/veilid-core/src/routing_table/mod.rs b/veilid-core/src/routing_table/mod.rs index 38b89504..45823b14 100644 --- a/veilid-core/src/routing_table/mod.rs +++ b/veilid-core/src/routing_table/mod.rs @@ -129,7 +129,7 @@ impl RoutingTableUnlockedInner { where F: FnOnce(&VeilidConfigInner) -> R, { - f(&*self.config.get()) + f(&self.config.get()) } pub fn node_id(&self, kind: CryptoKind) -> TypedKey { @@ -388,11 +388,15 @@ impl RoutingTable { } // Caches valid, load saved routing table - let Some(serialized_bucket_map): Option = db.load_json(0, SERIALIZED_BUCKET_MAP).await? else { + let Some(serialized_bucket_map): Option = + db.load_json(0, SERIALIZED_BUCKET_MAP).await? + else { log_rtab!(debug "no bucket map in saved routing table"); return Ok(()); }; - let Some(all_entry_bytes): Option = db.load_json(0, ALL_ENTRY_BYTES).await? else { + let Some(all_entry_bytes): Option = + db.load_json(0, ALL_ENTRY_BYTES).await? + else { log_rtab!(debug "no all_entry_bytes in saved routing table"); return Ok(()); }; @@ -537,7 +541,7 @@ impl RoutingTable { peer_b: &PeerInfo, dial_info_filter: DialInfoFilter, sequencing: Sequencing, - dif_sort: Option core::cmp::Ordering>>, + dif_sort: Option>, ) -> ContactMethod { self.inner.read().get_contact_method( routing_domain, @@ -881,7 +885,7 @@ impl RoutingTable { crypto_kind: CryptoKind, max_per_type: usize, ) -> Vec { - let protocol_types = vec![ + let protocol_types = [ ProtocolType::UDP, ProtocolType::TCP, ProtocolType::WS, @@ -889,8 +893,8 @@ impl RoutingTable { ]; let protocol_types_len = protocol_types.len(); - let mut nodes_proto_v4 = vec![0usize, 0usize, 0usize, 0usize]; - let mut nodes_proto_v6 = vec![0usize, 0usize, 0usize, 0usize]; + let mut nodes_proto_v4 = [0usize, 0usize, 0usize, 0usize]; + let mut nodes_proto_v6 = [0usize, 0usize, 0usize, 0usize]; let filter = Box::new( move |rti: &RoutingTableInner, entry: Option>| { diff --git a/veilid-core/src/routing_table/node_ref.rs b/veilid-core/src/routing_table/node_ref.rs index fd59c82b..939d54a7 100644 --- a/veilid-core/src/routing_table/node_ref.rs +++ b/veilid-core/src/routing_table/node_ref.rs @@ -85,7 +85,7 @@ pub trait NodeRefBase: Sized { self.common() .filter .as_ref() - .map(|f| f.dial_info_filter.clone()) + .map(|f| f.dial_info_filter) .unwrap_or(DialInfoFilter::all()) } @@ -283,7 +283,7 @@ pub trait NodeRefBase: Sized { self.operate(|rti, e| { // apply sequencing to filter and get sort let sequencing = self.common().sequencing; - let filter = self.common().filter.clone().unwrap_or_default(); + let filter = self.common().filter.unwrap_or_default(); let (ordered, filter) = filter.with_sequencing(sequencing); let mut last_connections = e.last_connections(rti, true, filter); @@ -444,7 +444,7 @@ impl Clone for NodeRef { common: NodeRefBaseCommon { routing_table: self.common.routing_table.clone(), entry: self.common.entry.clone(), - filter: self.common.filter.clone(), + filter: self.common.filter, sequencing: self.common.sequencing, #[cfg(feature = "tracking")] track_id: self.common.entry.write().track(), diff --git a/veilid-core/src/routing_table/privacy.rs b/veilid-core/src/routing_table/privacy.rs index 66f08c57..d2570ef9 100644 --- a/veilid-core/src/routing_table/privacy.rs +++ b/veilid-core/src/routing_table/privacy.rs @@ -18,7 +18,7 @@ pub enum RouteNode { /// Route node is optimized, no contact method information as this node id has been seen before NodeId(PublicKey), /// Route node with full contact method information to ensure the peer is reachable - PeerInfo(PeerInfo), + PeerInfo(Box), } impl RouteNode { @@ -41,7 +41,7 @@ impl RouteNode { Ok(nr) => nr, Err(e) => { log_rtab!(debug "failed to look up route node: {}", e); - return None; + None } } } @@ -49,13 +49,13 @@ impl RouteNode { // match routing_table.register_node_with_peer_info( RoutingDomain::PublicInternet, - pi.clone(), + *pi.clone(), false, ) { Ok(nr) => Some(nr), Err(e) => { log_rtab!(debug "failed to register route node: {}", e); - return None; + None } } } @@ -95,7 +95,7 @@ impl RouteHop { #[derive(Clone, Debug)] pub enum PrivateRouteHops { /// The first hop of a private route, unencrypted, route_hops == total hop count - FirstHop(RouteHop), + FirstHop(Box), /// Private route internal node. Has > 0 private route hops left but < total hop count Data(RouteHopData), /// Private route has ended (hop count = 0) @@ -134,10 +134,10 @@ impl PrivateRoute { Self { public_key, hop_count: 1, - hops: PrivateRouteHops::FirstHop(RouteHop { + hops: PrivateRouteHops::FirstHop(Box::new(RouteHop { node, next_hop: None, - }), + })), } } @@ -177,10 +177,10 @@ impl PrivateRoute { None => PrivateRouteHops::Empty, }; - return Some(first_hop_node); + Some(first_hop_node) } - PrivateRouteHops::Data(_) => return None, - PrivateRouteHops::Empty => return None, + PrivateRouteHops::Data(_) => None, + PrivateRouteHops::Empty => None, } } diff --git a/veilid-core/src/routing_table/route_spec_store/mod.rs b/veilid-core/src/routing_table/route_spec_store/mod.rs index 618da7f8..9cee1340 100644 --- a/veilid-core/src/routing_table/route_spec_store/mod.rs +++ b/veilid-core/src/routing_table/route_spec_store/mod.rs @@ -3,20 +3,20 @@ use super::*; mod permutation; mod remote_private_route_info; mod route_set_spec_detail; -mod route_spec_store; mod route_spec_store_cache; mod route_spec_store_content; mod route_stats; pub use remote_private_route_info::*; pub use route_set_spec_detail::*; -pub use route_spec_store::*; pub use route_spec_store_cache::*; pub use route_spec_store_content::*; pub use route_stats::*; use crate::veilid_api::*; +use permutation::*; + /// The size of the remote private route cache const REMOTE_PRIVATE_ROUTE_CACHE_SIZE: usize = 1024; /// Remote private route cache entries expire in 5 minutes if they haven't been used @@ -25,3 +25,1710 @@ const REMOTE_PRIVATE_ROUTE_CACHE_EXPIRY: TimestampDuration = TimestampDuration:: const ROUTE_MIN_IDLE_TIME_MS: u32 = 30_000; /// The size of the compiled route cache const COMPILED_ROUTE_CACHE_SIZE: usize = 256; + +#[derive(Debug)] +pub struct RouteSpecStoreInner { + /// Serialize RouteSpecStore content + content: RouteSpecStoreContent, + /// RouteSpecStore cache + cache: RouteSpecStoreCache, +} + +pub struct RouteSpecStoreUnlockedInner { + /// Handle to routing table + routing_table: RoutingTable, + /// Maximum number of hops in a route + max_route_hop_count: usize, + /// Default number of hops in a route + default_route_hop_count: usize, +} + +impl fmt::Debug for RouteSpecStoreUnlockedInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RouteSpecStoreUnlockedInner") + .field("max_route_hop_count", &self.max_route_hop_count) + .field("default_route_hop_count", &self.default_route_hop_count) + .finish() + } +} + +/// The routing table's storage for private/safety routes +#[derive(Clone, Debug)] +pub struct RouteSpecStore { + inner: Arc>, + unlocked_inner: Arc, +} + +impl RouteSpecStore { + pub fn new(routing_table: RoutingTable) -> Self { + let config = routing_table.network_manager().config(); + let c = config.get(); + + Self { + unlocked_inner: Arc::new(RouteSpecStoreUnlockedInner { + max_route_hop_count: c.network.rpc.max_route_hop_count.into(), + default_route_hop_count: c.network.rpc.default_route_hop_count.into(), + routing_table, + }), + inner: Arc::new(Mutex::new(RouteSpecStoreInner { + content: RouteSpecStoreContent::new(), + cache: Default::default(), + })), + } + } + + #[instrument(level = "trace", skip(routing_table), err)] + pub async fn load(routing_table: RoutingTable) -> EyreResult { + let (max_route_hop_count, default_route_hop_count) = { + let config = routing_table.network_manager().config(); + let c = config.get(); + ( + c.network.rpc.max_route_hop_count as usize, + c.network.rpc.default_route_hop_count as usize, + ) + }; + + // Get frozen blob from table store + let content = RouteSpecStoreContent::load(routing_table.clone()).await?; + + let mut inner = RouteSpecStoreInner { + content, + cache: Default::default(), + }; + + // Rebuild the routespecstore cache + let rti = &*routing_table.inner.read(); + for (_, rssd) in inner.content.iter_details() { + inner.cache.add_to_cache(rti, rssd); + } + + // Return the loaded RouteSpecStore + let rss = RouteSpecStore { + unlocked_inner: Arc::new(RouteSpecStoreUnlockedInner { + max_route_hop_count, + default_route_hop_count, + routing_table: routing_table.clone(), + }), + inner: Arc::new(Mutex::new(inner)), + }; + + Ok(rss) + } + + #[instrument(level = "trace", skip(self), err)] + pub async fn save(&self) -> EyreResult<()> { + let content = { + let inner = self.inner.lock(); + inner.content.clone() + }; + + // Save our content + content + .save(self.unlocked_inner.routing_table.clone()) + .await?; + + Ok(()) + } + + #[instrument(level = "trace", skip(self))] + pub fn send_route_update(&self) { + let (dead_routes, dead_remote_routes) = { + let mut inner = self.inner.lock(); + let Some(dr) = inner.cache.take_dead_routes() else { + // Nothing to do + return; + }; + dr + }; + + let update = VeilidUpdate::RouteChange(Box::new(VeilidRouteChange { + dead_routes, + dead_remote_routes, + })); + + let update_callback = self.unlocked_inner.routing_table.update_callback(); + update_callback(update); + } + + /// Purge the route spec store + pub async fn purge(&self) -> EyreResult<()> { + { + let inner = &mut *self.inner.lock(); + inner.content = Default::default(); + inner.cache = Default::default(); + } + self.save().await + } + + /// Create a new route + /// Prefers nodes that are not currently in use by another route + /// The route is not yet tested for its reachability + /// Returns None if no route could be allocated at this time + /// Returns Some route id string + #[instrument(level = "trace", skip(self), ret, err)] + pub fn allocate_route( + &self, + crypto_kinds: &[CryptoKind], + stability: Stability, + sequencing: Sequencing, + hop_count: usize, + directions: DirectionSet, + avoid_nodes: &[TypedKey], + ) -> EyreResult> { + let inner = &mut *self.inner.lock(); + let routing_table = self.unlocked_inner.routing_table.clone(); + let rti = &mut *routing_table.inner.write(); + + self.allocate_route_inner( + inner, + rti, + crypto_kinds, + stability, + sequencing, + hop_count, + directions, + avoid_nodes, + ) + } + + #[instrument(level = "trace", skip(self, inner, rti), ret, err)] + #[allow(clippy::too_many_arguments)] + fn allocate_route_inner( + &self, + inner: &mut RouteSpecStoreInner, + rti: &mut RoutingTableInner, + crypto_kinds: &[CryptoKind], + stability: Stability, + sequencing: Sequencing, + hop_count: usize, + directions: DirectionSet, + avoid_nodes: &[TypedKey], + ) -> EyreResult> { + use core::cmp::Ordering; + + if hop_count < 1 { + bail!("Not allocating route less than one hop in length"); + } + + if hop_count > self.unlocked_inner.max_route_hop_count { + bail!("Not allocating route longer than max route hop count"); + } + + // Ensure we have a valid network class so our peer info is useful + if !rti.has_valid_network_class(RoutingDomain::PublicInternet) { + log_rtab!(debug "unable to allocate route until we have a valid PublicInternet network class"); + return Ok(None); + }; + + // Get our peer info + let our_peer_info = rti.get_own_peer_info(RoutingDomain::PublicInternet); + + // Get relay node if we have one + let opt_own_relay_nr = rti + .relay_node(RoutingDomain::PublicInternet) + .map(|nr| nr.locked(rti)); + + // Get list of all nodes, and sort them for selection + let cur_ts = get_aligned_timestamp(); + let filter = Box::new( + |_rti: &RoutingTableInner, entry: Option>| -> bool { + // Exclude our own node from routes + if entry.is_none() { + return false; + } + let entry = entry.unwrap(); + + // Exclude our relay if we have one + if let Some(own_relay_nr) = &opt_own_relay_nr { + if own_relay_nr.same_bucket_entry(&entry) { + return false; + } + } + + // Process node info exclusions + let keep = entry.with_inner(|e| { + // Exclude nodes that don't have our requested crypto kinds + let common_ck = e.common_crypto_kinds(crypto_kinds); + if common_ck.len() != crypto_kinds.len() { + return false; + } + + // Exclude nodes we have specifically chosen to avoid + if e.node_ids().contains_any(avoid_nodes) { + return false; + } + + // Exclude nodes on our local network + if e.node_info(RoutingDomain::LocalNetwork).is_some() { + return false; + } + + // Exclude nodes that have no publicinternet signednodeinfo + let Some(sni) = e.signed_node_info(RoutingDomain::PublicInternet) else { + return false; + }; + + // Relay check + let relay_ids = sni.relay_ids(); + if !relay_ids.is_empty() { + // Exclude nodes whose relays we have chosen to avoid + if relay_ids.contains_any(avoid_nodes) { + return false; + } + // Exclude nodes whose relay is our own relay if we have one + if let Some(own_relay_nr) = &opt_own_relay_nr { + if relay_ids.contains_any(&own_relay_nr.node_ids()) { + return false; + } + } + } + true + }); + if !keep { + return false; + } + + // 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().has_capability(CAP_ROUTE) + }) + .unwrap_or(false) + }) + }, + ) as RoutingTableEntryFilter; + let filters = VecDeque::from([filter]); + let compare = |_rti: &RoutingTableInner, + entry1: &Option>, + entry2: &Option>| + -> Ordering { + // Our own node is filtered out + let entry1 = entry1.as_ref().unwrap().clone(); + let entry2 = entry2.as_ref().unwrap().clone(); + let entry1_node_ids = entry1.with_inner(|e| e.node_ids()); + let entry2_node_ids = entry2.with_inner(|e| e.node_ids()); + + // deprioritize nodes that we have already used as end points + let e1_used_end = inner.cache.get_used_end_node_count(&entry1_node_ids); + let e2_used_end = inner.cache.get_used_end_node_count(&entry2_node_ids); + let cmp_used_end = e1_used_end.cmp(&e2_used_end); + if !matches!(cmp_used_end, Ordering::Equal) { + return cmp_used_end; + } + + // deprioritize nodes we have used already anywhere + let e1_used = inner.cache.get_used_node_count(&entry1_node_ids); + let e2_used = inner.cache.get_used_node_count(&entry2_node_ids); + let cmp_used = e1_used.cmp(&e2_used); + if !matches!(cmp_used, Ordering::Equal) { + return cmp_used; + } + + // apply sequencing preference + // ensureordered will be taken care of by filter + // and nopreference doesn't care + if matches!(sequencing, Sequencing::PreferOrdered) { + let cmp_seq = entry1.with_inner(|e1| { + entry2.with_inner(|e2| { + let e1_can_do_ordered = e1 + .signed_node_info(RoutingDomain::PublicInternet) + .map(|sni| sni.has_sequencing_matched_dial_info(sequencing)) + .unwrap_or(false); + let e2_can_do_ordered = e2 + .signed_node_info(RoutingDomain::PublicInternet) + .map(|sni| sni.has_sequencing_matched_dial_info(sequencing)) + .unwrap_or(false); + e2_can_do_ordered.cmp(&e1_can_do_ordered) + }) + }); + if !matches!(cmp_seq, Ordering::Equal) { + return cmp_seq; + } + } + + // always prioritize reliable nodes, but sort by oldest or fastest + + entry1.with_inner(|e1| { + entry2.with_inner(|e2| match stability { + Stability::LowLatency => BucketEntryInner::cmp_fastest_reliable(cur_ts, e1, e2), + Stability::Reliable => BucketEntryInner::cmp_oldest_reliable(cur_ts, e1, e2), + }) + }) + }; + + let routing_table = self.unlocked_inner.routing_table.clone(); + let transform = |_rti: &RoutingTableInner, entry: Option>| -> NodeRef { + NodeRef::new(routing_table.clone(), entry.unwrap(), None) + }; + + // Pull the whole routing table in sorted order + let nodes: Vec = + rti.find_peers_with_sort_and_filter(usize::MAX, cur_ts, filters, compare, transform); + + // If we couldn't find enough nodes, wait until we have more nodes in the routing table + if nodes.len() < hop_count { + log_rtab!(debug "not enough nodes to construct route at this time"); + return Ok(None); + } + + // Get peer info for everything + let nodes_pi: Vec = nodes + .iter() + .map(|nr| { + nr.locked(rti) + .make_peer_info(RoutingDomain::PublicInternet) + .unwrap() + }) + .collect(); + + // Now go through nodes and try to build a route we haven't seen yet + let mut perm_func = Box::new(|permutation: &[usize]| { + // Get the hop cache key for a particular route permutation + // uses the same algorithm as RouteSetSpecDetail::make_cache_key + let route_permutation_to_hop_cache = + |_rti: &RoutingTableInner, nodes: &[NodeRef], perm: &[usize]| -> Vec { + let mut cache: Vec = Vec::with_capacity(perm.len() * PUBLIC_KEY_LENGTH); + for n in perm { + cache.extend_from_slice(&nodes[*n].locked(rti).best_node_id().value.bytes) + } + cache + }; + let cache_key = route_permutation_to_hop_cache(rti, &nodes, permutation); + + // Skip routes we have already seen + if inner.cache.contains_route(&cache_key) { + return None; + } + + // Ensure the route doesn't contain both a node and its relay + let mut seen_nodes: HashSet = HashSet::new(); + for n in permutation { + let node = nodes.get(*n).unwrap(); + if !seen_nodes.insert(node.locked(rti).best_node_id()) { + // Already seen this node, should not be in the route twice + return None; + } + let opt_relay = match node.locked_mut(rti).relay(RoutingDomain::PublicInternet) { + Ok(r) => r, + Err(_) => { + // Not selecting a relay through ourselves + return None; + } + }; + if let Some(relay) = opt_relay { + let relay_id = relay.locked(rti).best_node_id(); + if !seen_nodes.insert(relay_id) { + // Already seen this node, should not be in the route twice + return None; + } + } + } + + // Ensure this route is viable by checking that each node can contact the next one + let mut can_do_sequenced = true; + if directions.contains(Direction::Outbound) { + let mut previous_node = &our_peer_info; + let mut reachable = true; + for n in permutation { + let current_node = nodes_pi.get(*n).unwrap(); + let cm = rti.get_contact_method( + RoutingDomain::PublicInternet, + previous_node, + current_node, + DialInfoFilter::all(), + sequencing, + None, + ); + if matches!(cm, ContactMethod::Unreachable) { + reachable = false; + break; + } + + // Check if we can do sequenced specifically + if can_do_sequenced { + let cm = rti.get_contact_method( + RoutingDomain::PublicInternet, + previous_node, + current_node, + DialInfoFilter::all(), + Sequencing::EnsureOrdered, + None, + ); + if matches!(cm, ContactMethod::Unreachable) { + can_do_sequenced = false; + } + } + + previous_node = current_node; + } + if !reachable { + return None; + } + } + if directions.contains(Direction::Inbound) { + let mut next_node = &our_peer_info; + let mut reachable = true; + for n in permutation.iter().rev() { + let current_node = nodes_pi.get(*n).unwrap(); + let cm = rti.get_contact_method( + RoutingDomain::PublicInternet, + next_node, + current_node, + DialInfoFilter::all(), + sequencing, + None, + ); + if matches!(cm, ContactMethod::Unreachable) { + reachable = false; + break; + } + + // Check if we can do sequenced specifically + if can_do_sequenced { + let cm = rti.get_contact_method( + RoutingDomain::PublicInternet, + next_node, + current_node, + DialInfoFilter::all(), + Sequencing::EnsureOrdered, + None, + ); + if matches!(cm, ContactMethod::Unreachable) { + can_do_sequenced = false; + } + } + next_node = current_node; + } + if !reachable { + return None; + } + } + // Keep this route + let route_nodes = permutation.to_vec(); + Some((route_nodes, can_do_sequenced)) + }) as PermFunc; + + let mut route_nodes: Vec = Vec::new(); + let mut can_do_sequenced: bool = true; + + for start in 0..(nodes.len() - hop_count) { + // Try the permutations available starting with 'start' + if let Some((rn, cds)) = with_route_permutations(hop_count, start, &mut perm_func) { + route_nodes = rn; + can_do_sequenced = cds; + break; + } + } + if route_nodes.is_empty() { + log_rtab!(debug "unable to find unique route at this time"); + return Ok(None); + } + + drop(perm_func); + + // Got a unique route, lets build the details, register it, and return it + let hop_node_refs: Vec = route_nodes.iter().map(|k| nodes[*k].clone()).collect(); + let mut route_set = BTreeMap::::new(); + for crypto_kind in crypto_kinds.iter().copied() { + let vcrypto = self + .unlocked_inner + .routing_table + .crypto() + .get(crypto_kind) + .unwrap(); + let keypair = vcrypto.generate_keypair(); + let hops: Vec = route_nodes + .iter() + .map(|v| { + nodes[*v] + .locked(rti) + .node_ids() + .get(crypto_kind) + .unwrap() + .value + }) + .collect(); + + route_set.insert( + keypair.key, + RouteSpecDetail { + crypto_kind, + secret_key: keypair.secret, + hops, + }, + ); + } + + let rssd = RouteSetSpecDetail::new( + cur_ts, + route_set, + hop_node_refs, + directions, + stability, + can_do_sequenced, + ); + + // make id + let id = self.generate_allocated_route_id(&rssd)?; + + // Add to cache + inner.cache.add_to_cache(rti, &rssd); + + // Keep route in spec store + inner.content.add_detail(id, rssd); + + Ok(Some(id)) + } + + /// validate data using a private route's key and signature chain + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip(self, data, callback), ret) + )] + pub fn with_signature_validated_route( + &self, + public_key: &TypedKey, + signatures: &[Signature], + data: &[u8], + last_hop_id: PublicKey, + callback: F, + ) -> Option + where + F: FnOnce(&RouteSetSpecDetail, &RouteSpecDetail) -> R, + R: fmt::Debug, + { + let inner = &*self.inner.lock(); + let crypto = self.unlocked_inner.routing_table.crypto(); + let Some(vcrypto) = crypto.get(public_key.kind) else { + log_rpc!(debug "can't handle route with public key: {:?}", public_key); + return None; + }; + + let Some(rsid) = inner.content.get_id_by_key(&public_key.value) else { + log_rpc!(debug "route id does not exist: {:?}", public_key.value); + return None; + }; + let Some(rssd) = inner.content.get_detail(&rsid) else { + log_rpc!(debug "route detail does not exist: {:?}", rsid); + return None; + }; + let Some(rsd) = rssd.get_route_by_key(&public_key.value) else { + log_rpc!(debug "route set {:?} does not have key: {:?}", rsid, public_key.value); + return None; + }; + + // Ensure we have the right number of signatures + if signatures.len() != rsd.hops.len() - 1 { + // Wrong number of signatures + log_rpc!(debug "wrong number of signatures ({} should be {}) for routed operation on private route {}", signatures.len(), rsd.hops.len() - 1, public_key); + return None; + } + // Validate signatures to ensure the route was handled by the nodes and not messed with + // This is in private route (reverse) order as we are receiving over the route + for (hop_n, hop_public_key) in rsd.hops.iter().rev().enumerate() { + // The last hop is not signed, as the whole packet is signed + if hop_n == signatures.len() { + // Verify the node we received the routed operation from is the last hop in our route + if *hop_public_key != last_hop_id { + log_rpc!(debug "received routed operation from the wrong hop ({} should be {}) on private route {}", hop_public_key.encode(), last_hop_id.encode(), public_key); + return None; + } + } else { + // Verify a signature for a hop node along the route + if let Err(e) = vcrypto.verify(hop_public_key, data, &signatures[hop_n]) { + log_rpc!(debug "failed to verify signature for hop {} at {} on private route {}: {}", hop_n, hop_public_key, public_key, e); + return None; + } + } + } + // We got the correct signatures, return a key and response safety spec + Some(callback(rssd, rsd)) + } + + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip(self), ret, err) + )] + async fn test_allocated_route(&self, private_route_id: RouteId) -> EyreResult { + // Make loopback route to test with + let dest = { + // Get the best private route for this id + let (key, hop_count) = { + let inner = &mut *self.inner.lock(); + let Some(rssd) = inner.content.get_detail(&private_route_id) else { + bail!("route id not allocated"); + }; + let Some(key) = rssd.get_best_route_set_key() else { + bail!("no best key to test allocated route"); + }; + // Match the private route's hop length for safety route length + let hop_count = rssd.hop_count(); + (key, hop_count) + }; + + // Get the private route to send to + let private_route = self.assemble_private_route(&key, None)?; + // Always test routes with safety routes that are more likely to succeed + let stability = Stability::Reliable; + // Routes should test with the most likely to succeed sequencing they are capable of + let sequencing = Sequencing::PreferOrdered; + + let safety_spec = SafetySpec { + preferred_route: Some(private_route_id), + hop_count, + stability, + sequencing, + }; + let safety_selection = SafetySelection::Safe(safety_spec); + + Destination::PrivateRoute { + private_route, + safety_selection, + } + }; + + // Test with double-round trip ping to self + let rpc_processor = self.unlocked_inner.routing_table.rpc_processor(); + let _res = match rpc_processor.rpc_call_status(dest).await? { + NetworkResult::Value(v) => v, + _ => { + // Did not error, but did not come back, just return false + return Ok(false); + } + }; + + Ok(true) + } + + #[instrument(level = "trace", skip(self), ret, err)] + async fn test_remote_route(&self, private_route_id: RouteId) -> EyreResult { + // Make private route test + let dest = { + // Get the route to test + let Some(private_route) = self.best_remote_private_route(&private_route_id) else { + bail!("no best key to test remote route"); + }; + + // Always test routes with safety routes that are more likely to succeed + let stability = Stability::Reliable; + // Routes should test with the most likely to succeed sequencing they are capable of + let sequencing = Sequencing::PreferOrdered; + + // Get a safety route that is good enough + let safety_spec = SafetySpec { + preferred_route: None, + hop_count: self.unlocked_inner.default_route_hop_count, + stability, + sequencing, + }; + + let safety_selection = SafetySelection::Safe(safety_spec); + + Destination::PrivateRoute { + private_route, + safety_selection, + } + }; + + // Test with double-round trip ping to self + let rpc_processor = self.unlocked_inner.routing_table.rpc_processor(); + let _res = match rpc_processor.rpc_call_status(dest).await? { + NetworkResult::Value(v) => v, + _ => { + // Did not error, but did not come back, just return false + return Ok(false); + } + }; + + Ok(true) + } + + /// Release an allocated route that is no longer in use + #[instrument(level = "trace", skip(self), ret)] + fn release_allocated_route(&self, id: RouteId) -> bool { + let mut inner = self.inner.lock(); + let Some(rssd) = inner.content.remove_detail(&id) else { + return false; + }; + + // Remove from hop cache + let rti = &*self.unlocked_inner.routing_table.inner.read(); + if !inner.cache.remove_from_cache(rti, id, &rssd) { + panic!("hop cache should have contained cache key"); + } + + true + } + + /// Check if a route id is remote or not + pub fn is_route_id_remote(&self, id: &RouteId) -> bool { + let inner = &mut *self.inner.lock(); + let cur_ts = get_aligned_timestamp(); + inner + .cache + .peek_remote_private_route_mut(cur_ts, id) + .is_some() + } + + /// Test an allocated route for continuity + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip(self), ret, err) + )] + pub async fn test_route(&self, id: RouteId) -> EyreResult { + let is_remote = self.is_route_id_remote(&id); + if is_remote { + self.test_remote_route(id).await + } else { + self.test_allocated_route(id).await + } + } + + /// Release an allocated or remote route that is no longer in use + #[instrument(level = "trace", skip(self), ret)] + pub fn release_route(&self, id: RouteId) -> bool { + let is_remote = self.is_route_id_remote(&id); + if is_remote { + self.release_remote_private_route(id) + } else { + self.release_allocated_route(id) + } + } + + /// Find first matching unpublished route that fits into the selection criteria + /// Don't pick any routes that have failed and haven't been tested yet + #[allow(clippy::too_many_arguments)] + fn first_available_route_inner( + inner: &RouteSpecStoreInner, + crypto_kind: CryptoKind, + min_hop_count: usize, + max_hop_count: usize, + stability: Stability, + sequencing: Sequencing, + directions: DirectionSet, + avoid_nodes: &[TypedKey], + ) -> Option { + let cur_ts = get_aligned_timestamp(); + + let mut routes = Vec::new(); + + // Get all valid routes, allow routes that need testing + // but definitely prefer routes that have been recently tested + for (id, rssd) in inner.content.iter_details() { + if rssd.get_stability() >= stability + && rssd.is_sequencing_match(sequencing) + && rssd.hop_count() >= min_hop_count + && rssd.hop_count() <= max_hop_count + && rssd.get_directions().is_superset(directions) + && rssd.get_route_set_keys().kinds().contains(&crypto_kind) + && !rssd.is_published() + && !rssd.contains_nodes(avoid_nodes) + { + routes.push((id, rssd)); + } + } + + // Sort the routes by preference + routes.sort_by(|a, b| { + let a_needs_testing = a.1.get_stats().needs_testing(cur_ts); + let b_needs_testing = b.1.get_stats().needs_testing(cur_ts); + if !a_needs_testing && b_needs_testing { + return cmp::Ordering::Less; + } + if !b_needs_testing && a_needs_testing { + return cmp::Ordering::Greater; + } + let a_latency = a.1.get_stats().latency_stats().average; + let b_latency = b.1.get_stats().latency_stats().average; + + a_latency.cmp(&b_latency) + }); + + // Return the best one if we got one + routes.first().map(|r| *r.0) + } + + /// List all allocated routes + pub fn list_allocated_routes(&self, mut filter: F) -> Vec + where + F: FnMut(&RouteId, &RouteSetSpecDetail) -> Option, + { + let inner = self.inner.lock(); + let mut out = Vec::with_capacity(inner.content.get_detail_count()); + for detail in inner.content.iter_details() { + if let Some(x) = filter(detail.0, detail.1) { + out.push(x); + } + } + out + } + + /// List all allocated routes + pub fn list_remote_routes(&self, mut filter: F) -> Vec + where + F: FnMut(&RouteId, &RemotePrivateRouteInfo) -> Option, + { + let inner = self.inner.lock(); + let mut out = Vec::with_capacity(inner.cache.get_remote_private_route_count()); + for info in inner.cache.iter_remote_private_routes() { + if let Some(x) = filter(info.0, info.1) { + out.push(x); + } + } + out + } + + /// Get the debug description of a route + pub fn debug_route(&self, id: &RouteId) -> Option { + let inner = &mut *self.inner.lock(); + let cur_ts = get_aligned_timestamp(); + if let Some(rpri) = inner.cache.peek_remote_private_route_mut(cur_ts, id) { + return Some(format!("{:#?}", rpri)); + } + if let Some(rssd) = inner.content.get_detail(id) { + return Some(format!("{:#?}", rssd)); + } + None + } + + ////////////////////////////////////////////////////////////////////// + + /// Choose the best private route from a private route set to communicate with + pub fn best_remote_private_route(&self, id: &RouteId) -> Option { + let inner = &mut *self.inner.lock(); + let cur_ts = get_aligned_timestamp(); + let rpri = inner.cache.get_remote_private_route(cur_ts, id)?; + rpri.best_private_route() + } + + /// Compiles a safety route to the private route, with caching + /// Returns an Err() if the parameters are wrong + /// Returns Ok(None) if no allocation could happen at this time (not an error) + pub fn compile_safety_route( + &self, + safety_selection: SafetySelection, + mut private_route: PrivateRoute, + ) -> EyreResult> { + // let profile_start_ts = get_timestamp(); + let inner = &mut *self.inner.lock(); + let routing_table = self.unlocked_inner.routing_table.clone(); + let rti = &mut *routing_table.inner.write(); + + // Get useful private route properties + let crypto_kind = private_route.crypto_kind(); + let crypto = routing_table.crypto(); + let Some(vcrypto) = crypto.get(crypto_kind) else { + bail!("crypto not supported for route"); + }; + let pr_pubkey = private_route.public_key.value; + let pr_hopcount = private_route.hop_count as usize; + let max_route_hop_count = self.unlocked_inner.max_route_hop_count; + + // Check private route hop count isn't larger than the max route hop count plus one for the 'first hop' header + if pr_hopcount > (max_route_hop_count + 1) { + bail!("private route hop count too long"); + } + // See if we are using a safety route, if not, short circuit this operation + let safety_spec = match safety_selection { + // Safety route spec to use + SafetySelection::Safe(safety_spec) => safety_spec, + // Safety route stub with the node's public key as the safety route key since it's the 0th hop + SafetySelection::Unsafe(sequencing) => { + let Some(pr_first_hop_node) = private_route.pop_first_hop() else { + bail!("compiled private route should have first hop"); + }; + + let opt_first_hop = match pr_first_hop_node { + RouteNode::NodeId(id) => { + rti.lookup_node_ref(routing_table.clone(), TypedKey::new(crypto_kind, id))? + } + RouteNode::PeerInfo(pi) => Some(rti.register_node_with_peer_info( + routing_table.clone(), + RoutingDomain::PublicInternet, + *pi, + false, + )?), + }; + if opt_first_hop.is_none() { + // Can't reach this private route any more + log_rtab!(debug "can't reach private route any more"); + return Ok(None); + } + let mut first_hop = opt_first_hop.unwrap(); + + // Set sequencing requirement + first_hop.set_sequencing(sequencing); + + // Return the compiled safety route + //println!("compile_safety_route profile (stub): {} us", (get_timestamp() - profile_start_ts)); + return Ok(Some(CompiledRoute { + safety_route: SafetyRoute::new_stub( + routing_table.node_id(crypto_kind), + private_route, + ), + secret: routing_table.node_id_secret_key(crypto_kind), + first_hop, + })); + } + }; + + // If the safety route requested is also the private route, this is a loopback test, just accept it + let opt_private_route_id = inner.content.get_id_by_key(&pr_pubkey); + let sr_pubkey = if opt_private_route_id.is_some() + && safety_spec.preferred_route == opt_private_route_id + { + // Private route is also safety route during loopback test + pr_pubkey + } else { + let Some(avoid_node_id) = private_route.first_hop_node_id() else { + bail!("compiled private route should have first hop"); + }; + let Some(sr_pubkey) = self.get_route_for_safety_spec_inner( + inner, + rti, + crypto_kind, + &safety_spec, + Direction::Outbound.into(), + &[avoid_node_id], + )? + else { + // No safety route could be found for this spec + return Ok(None); + }; + sr_pubkey + }; + + // Look up a few things from the safety route detail we want for the compiled route and don't borrow inner + let Some(safety_route_id) = inner.content.get_id_by_key(&sr_pubkey) else { + bail!("route id missing"); + }; + let Some(safety_rssd) = inner.content.get_detail(&safety_route_id) else { + bail!("route set detail missing"); + }; + let Some(safety_rsd) = safety_rssd.get_route_by_key(&sr_pubkey) else { + bail!("route detail missing"); + }; + + // We can optimize the peer info in this safety route if it has been successfully + // communicated over either via an outbound test, or used as a private route inbound + // and we are replying over the same route as our safety route outbound + let optimize = safety_rssd.get_stats().last_tested_ts.is_some() + || safety_rssd.get_stats().last_received_ts.is_some(); + + // Get the first hop noderef of the safety route + let mut first_hop = safety_rssd.hop_node_ref(0).unwrap(); + + // Ensure sequencing requirement is set on first hop + first_hop.set_sequencing(safety_spec.sequencing); + + // Get the safety route secret key + let secret = safety_rsd.secret_key; + + // See if we have a cached route we can use + if optimize { + if let Some(safety_route) = inner + .cache + .lookup_compiled_route_cache(sr_pubkey, pr_pubkey) + { + // Build compiled route + let compiled_route = CompiledRoute { + safety_route, + secret, + first_hop, + }; + // Return compiled route + //println!("compile_safety_route profile (cached): {} us", (get_timestamp() - profile_start_ts)); + return Ok(Some(compiled_route)); + } + } + + // Create hops + let hops = { + // start last blob-to-encrypt data off as private route + let mut blob_data = { + let mut pr_message = ::capnp::message::Builder::new_default(); + let mut pr_builder = pr_message.init_root::(); + encode_private_route(&private_route, &mut pr_builder)?; + let mut blob_data = builder_to_vec(pr_message)?; + + // append the private route tag so we know how to decode it later + blob_data.push(1u8); + blob_data + }; + + // Encode each hop from inside to outside + // skips the outermost hop since that's entering the + // safety route and does not include the dialInfo + // (outer hop is a RouteHopData, not a RouteHop). + // Each loop mutates 'nonce', and 'blob_data' + let mut nonce = vcrypto.random_nonce(); + // Forward order (safety route), but inside-out + for h in (1..safety_rsd.hops.len()).rev() { + // Get blob to encrypt for next hop + blob_data = { + // Encrypt the previous blob ENC(nonce, DH(PKhop,SKsr)) + let dh_secret = vcrypto + .cached_dh(&safety_rsd.hops[h], &safety_rsd.secret_key) + .wrap_err("dh failed")?; + let enc_msg_data = vcrypto + .encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None) + .wrap_err("encryption failed")?; + + // Make route hop data + let route_hop_data = RouteHopData { + nonce, + blob: enc_msg_data, + }; + + // Make route hop + let route_hop = RouteHop { + node: if optimize { + // Optimized, no peer info, just the dht key + RouteNode::NodeId(safety_rsd.hops[h]) + } else { + // Full peer info, required until we are sure the route has been fully established + let node_id = TypedKey::new(safety_rsd.crypto_kind, safety_rsd.hops[h]); + let pi = rti + .with_node_entry(node_id, |entry| { + entry.with(rti, |_rti, e| { + e.make_peer_info(RoutingDomain::PublicInternet) + }) + }) + .flatten(); + if pi.is_none() { + bail!("peer info should exist for route but doesn't"); + } + RouteNode::PeerInfo(Box::new(pi.unwrap())) + }, + next_hop: Some(route_hop_data), + }; + + // Make next blob from route hop + let mut rh_message = ::capnp::message::Builder::new_default(); + let mut rh_builder = rh_message.init_root::(); + encode_route_hop(&route_hop, &mut rh_builder)?; + let mut blob_data = builder_to_vec(rh_message)?; + + // Append the route hop tag so we know how to decode it later + blob_data.push(0u8); + blob_data + }; + + // Make another nonce for the next hop + nonce = vcrypto.random_nonce(); + } + + // Encode first RouteHopData + let dh_secret = vcrypto + .cached_dh(&safety_rsd.hops[0], &safety_rsd.secret_key) + .map_err(RPCError::map_internal("dh failed"))?; + let enc_msg_data = vcrypto + .encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None) + .map_err(RPCError::map_internal("encryption failed"))?; + + let route_hop_data = RouteHopData { + nonce, + blob: enc_msg_data, + }; + + SafetyRouteHops::Data(route_hop_data) + }; + + // Build safety route + let safety_route = SafetyRoute { + public_key: TypedKey::new(crypto_kind, sr_pubkey), + hop_count: safety_spec.hop_count as u8, + hops, + }; + + // Add to cache but only if we have an optimized route + if optimize { + inner + .cache + .add_to_compiled_route_cache(pr_pubkey, safety_route.clone()); + } + + // Build compiled route + let compiled_route = CompiledRoute { + safety_route, + secret, + first_hop, + }; + + // Return compiled route + //println!("compile_safety_route profile (uncached): {} us", (get_timestamp() - profile_start_ts)); + Ok(Some(compiled_route)) + } + + /// Get an allocated route that matches a particular safety spec + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip(self, inner, rti), ret, err) + )] + fn get_route_for_safety_spec_inner( + &self, + inner: &mut RouteSpecStoreInner, + rti: &mut RoutingTableInner, + crypto_kind: CryptoKind, + safety_spec: &SafetySpec, + direction: DirectionSet, + avoid_nodes: &[TypedKey], + ) -> EyreResult> { + // Ensure the total hop count isn't too long for our config + let max_route_hop_count = self.unlocked_inner.max_route_hop_count; + if safety_spec.hop_count == 0 { + bail!("safety route hop count is zero"); + } + if safety_spec.hop_count > max_route_hop_count { + bail!("safety route hop count too long"); + } + + // See if the preferred route is here + if let Some(preferred_route) = safety_spec.preferred_route { + if let Some(preferred_rssd) = inner.content.get_detail(&preferred_route) { + // Only use the preferred route if it has the desired crypto kind + if let Some(preferred_key) = preferred_rssd.get_route_set_keys().get(crypto_kind) { + // Only use the preferred route if it doesn't contain the avoid nodes + if !preferred_rssd.contains_nodes(avoid_nodes) { + return Ok(Some(preferred_key.value)); + } + } + } + } + + // Select a safety route from the pool or make one if we don't have one that matches + let sr_route_id = if let Some(sr_route_id) = Self::first_available_route_inner( + inner, + crypto_kind, + safety_spec.hop_count, + safety_spec.hop_count, + safety_spec.stability, + safety_spec.sequencing, + direction, + avoid_nodes, + ) { + // Found a route to use + sr_route_id + } else { + // No route found, gotta allocate one + let Some(sr_route_id) = self + .allocate_route_inner( + inner, + rti, + &[crypto_kind], + safety_spec.stability, + safety_spec.sequencing, + safety_spec.hop_count, + direction, + avoid_nodes, + ) + .map_err(RPCError::internal)? + else { + return Ok(None); + }; + sr_route_id + }; + + let sr_pubkey = inner + .content + .get_detail(&sr_route_id) + .unwrap() + .get_route_set_keys() + .get(crypto_kind) + .unwrap() + .value; + + Ok(Some(sr_pubkey)) + } + + /// Get a private route to use for the answer to question + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip(self), ret, err) + )] + pub fn get_private_route_for_safety_spec( + &self, + crypto_kind: CryptoKind, + safety_spec: &SafetySpec, + avoid_nodes: &[TypedKey], + ) -> EyreResult> { + let inner = &mut *self.inner.lock(); + let routing_table = self.unlocked_inner.routing_table.clone(); + let rti = &mut *routing_table.inner.write(); + + self.get_route_for_safety_spec_inner( + inner, + rti, + crypto_kind, + safety_spec, + Direction::Inbound.into(), + avoid_nodes, + ) + } + + fn assemble_private_route_inner( + &self, + key: &PublicKey, + rsd: &RouteSpecDetail, + optimized: bool, + ) -> EyreResult { + let routing_table = self.unlocked_inner.routing_table.clone(); + let rti = &*routing_table.inner.read(); + + // Ensure we get the crypto for it + let crypto = routing_table.network_manager().crypto(); + let Some(vcrypto) = crypto.get(rsd.crypto_kind) else { + bail!("crypto not supported for route"); + }; + + // Ensure our network class is valid before attempting to assemble any routes + if !rti.has_valid_network_class(RoutingDomain::PublicInternet) { + bail!("can't make private routes until our node info is valid"); + } + + // Make innermost route hop to our own node + let mut route_hop = RouteHop { + node: if optimized { + let Some(node_id) = routing_table.node_ids().get(rsd.crypto_kind) else { + bail!("missing node id for crypto kind"); + }; + RouteNode::NodeId(node_id.value) + } else { + let pi = rti.get_own_peer_info(RoutingDomain::PublicInternet); + RouteNode::PeerInfo(Box::new(pi)) + }, + next_hop: None, + }; + + // Loop for each hop + let hop_count = rsd.hops.len(); + // iterate hops in private route order (reverse, but inside out) + for h in 0..hop_count { + let nonce = vcrypto.random_nonce(); + + let blob_data = { + let mut rh_message = ::capnp::message::Builder::new_default(); + let mut rh_builder = rh_message.init_root::(); + encode_route_hop(&route_hop, &mut rh_builder)?; + builder_to_vec(rh_message)? + }; + + // Encrypt the previous blob ENC(nonce, DH(PKhop,SKpr)) + let dh_secret = vcrypto + .cached_dh(&rsd.hops[h], &rsd.secret_key) + .wrap_err("dh failed")?; + let enc_msg_data = vcrypto + .encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None) + .wrap_err("encryption failed")?; + let route_hop_data = RouteHopData { + nonce, + blob: enc_msg_data, + }; + + route_hop = RouteHop { + node: if optimized { + // Optimized, no peer info, just the dht key + RouteNode::NodeId(rsd.hops[h]) + } else { + // Full peer info, required until we are sure the route has been fully established + let node_id = TypedKey::new(rsd.crypto_kind, rsd.hops[h]); + let pi = rti + .with_node_entry(node_id, |entry| { + entry.with(rti, |_rti, e| { + e.make_peer_info(RoutingDomain::PublicInternet) + }) + }) + .flatten(); + if pi.is_none() { + bail!("peer info should exist for route but doesn't",); + } + RouteNode::PeerInfo(Box::new(pi.unwrap())) + }, + next_hop: Some(route_hop_data), + } + } + + let private_route = PrivateRoute { + public_key: TypedKey::new(rsd.crypto_kind, *key), + // add hop for 'FirstHop' + hop_count: (hop_count + 1).try_into().unwrap(), + hops: PrivateRouteHops::FirstHop(Box::new(route_hop)), + }; + Ok(private_route) + } + + /// Assemble a single private route for publication + /// Returns a PrivateRoute object for an allocated private route key + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip(self), err) + )] + pub fn assemble_private_route( + &self, + key: &PublicKey, + optimized: Option, + ) -> EyreResult { + let inner = &*self.inner.lock(); + let Some(rsid) = inner.content.get_id_by_key(key) else { + bail!("route key does not exist"); + }; + let Some(rssd) = inner.content.get_detail(&rsid) else { + bail!("route id does not exist"); + }; + + // See if we can optimize this compilation yet + // We don't want to include full nodeinfo if we don't have to + let optimized = optimized.unwrap_or( + rssd.get_stats().last_tested_ts.is_some() + || rssd.get_stats().last_received_ts.is_some(), + ); + + let rsd = rssd + .get_route_by_key(key) + .expect("route key index is broken"); + + self.assemble_private_route_inner(key, rsd, optimized) + } + + /// Assemble private route set for publication + /// Returns a vec of PrivateRoute objects for an allocated private route + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip(self), err) + )] + pub fn assemble_private_routes( + &self, + id: &RouteId, + optimized: Option, + ) -> EyreResult> { + let inner = &*self.inner.lock(); + let Some(rssd) = inner.content.get_detail(id) else { + bail!("route id does not exist"); + }; + + // See if we can optimize this compilation yet + // We don't want to include full nodeinfo if we don't have to + let optimized = optimized.unwrap_or( + rssd.get_stats().last_tested_ts.is_some() + || rssd.get_stats().last_received_ts.is_some(), + ); + + let mut out = Vec::new(); + for (key, rsd) in rssd.iter_route_set() { + out.push(self.assemble_private_route_inner(key, rsd, optimized)?); + } + Ok(out) + } + + /// Import a remote private route for compilation + /// It is safe to import the same route more than once and it will return the same route id + /// Returns a route set id + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip(self, blob), ret, err) + )] + pub fn import_remote_private_route(&self, blob: Vec) -> EyreResult { + let cur_ts = get_aligned_timestamp(); + + // decode the pr blob + let private_routes = RouteSpecStore::blob_to_private_routes( + self.unlocked_inner.routing_table.crypto(), + blob, + )?; + + // make the route id + let id = self.generate_remote_route_id(&private_routes)?; + + // validate the private routes + let inner = &mut *self.inner.lock(); + for private_route in &private_routes { + // ensure private route has first hop + if !matches!(private_route.hops, PrivateRouteHops::FirstHop(_)) { + bail!("private route must have first hop"); + } + + // ensure this isn't also an allocated route + // if inner.content.get_id_by_key(&private_route.public_key.value).is_some() { + // bail!("should not import allocated route"); + // } + } + + inner + .cache + .cache_remote_private_route(cur_ts, id, private_routes); + + Ok(id) + } + + /// Release a remote private route that is no longer in use + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip(self), ret) + )] + pub fn release_remote_private_route(&self, id: RouteId) -> bool { + let inner = &mut *self.inner.lock(); + inner.cache.remove_remote_private_route(id) + } + + /// Get a route id for a route's public key + pub fn get_route_id_for_key(&self, key: &PublicKey) -> Option { + let inner = &mut *self.inner.lock(); + // Check for local route + if let Some(id) = inner.content.get_id_by_key(key) { + return Some(id); + } + + // Check for remote route + if let Some(rrid) = inner.cache.get_remote_private_route_id_by_key(key) { + return Some(rrid); + } + + None + } + + /// Check to see if this remote (not ours) private route has seen our current node info yet + /// This happens when you communicate with a private route without a safety route + pub fn has_remote_private_route_seen_our_node_info(&self, key: &PublicKey) -> bool { + let inner = &mut *self.inner.lock(); + + // Check for local route. If this is not a remote private route, + // we may be running a test and using our own local route as the destination private route. + // In that case we definitely have already seen our own node info + if inner.content.get_id_by_key(key).is_some() { + return true; + } + + if let Some(rrid) = inner.cache.get_remote_private_route_id_by_key(key) { + let cur_ts = get_aligned_timestamp(); + if let Some(rpri) = inner.cache.peek_remote_private_route_mut(cur_ts, &rrid) { + let our_node_info_ts = self + .unlocked_inner + .routing_table + .get_own_node_info_ts(RoutingDomain::PublicInternet); + return rpri.has_seen_our_node_info_ts(our_node_info_ts); + } + } + + false + } + + /// Mark a remote private route as having seen our current node info + /// PRIVACY: + /// We do not accept node info timestamps from remote private routes because this would + /// enable a deanonymization attack, whereby a node could be 'pinged' with a doctored node_info with a + /// special 'timestamp', which then may be sent back over a private route, identifying that it + /// was that node that had the private route. + pub fn mark_remote_private_route_seen_our_node_info( + &self, + key: &PublicKey, + cur_ts: Timestamp, + ) -> EyreResult<()> { + let our_node_info_ts = self + .unlocked_inner + .routing_table + .get_own_node_info_ts(RoutingDomain::PublicInternet); + + let inner = &mut *self.inner.lock(); + + // Check for local route. If this is not a remote private route + // then we just skip the recording. We may be running a test and using + // our own local route as the destination private route. + if inner.content.get_id_by_key(key).is_some() { + return Ok(()); + } + + if let Some(rrid) = inner.cache.get_remote_private_route_id_by_key(key) { + if let Some(rpri) = inner.cache.peek_remote_private_route_mut(cur_ts, &rrid) { + rpri.set_last_seen_our_node_info_ts(our_node_info_ts); + return Ok(()); + } + } + + bail!("private route is missing from store: {}", key); + } + + /// Get the route statistics for any route we know about, local or remote + pub fn with_route_stats(&self, cur_ts: Timestamp, key: &PublicKey, f: F) -> Option + where + F: FnOnce(&mut RouteStats) -> R, + { + let inner = &mut *self.inner.lock(); + + // Check for stub route + if self + .unlocked_inner + .routing_table + .matches_own_node_id_key(key) + { + return None; + } + + // Check for local route + if let Some(rsid) = inner.content.get_id_by_key(key) { + if let Some(rsd) = inner.content.get_detail_mut(&rsid) { + return Some(f(rsd.get_stats_mut())); + } + } + + // Check for remote route + if let Some(rrid) = inner.cache.get_remote_private_route_id_by_key(key) { + if let Some(rpri) = inner.cache.peek_remote_private_route_mut(cur_ts, &rrid) { + return Some(f(rpri.get_stats_mut())); + } + } + + None + } + + /// Clear caches when local our local node info changes + #[instrument(level = "trace", skip(self))] + pub fn reset(&self) { + let inner = &mut *self.inner.lock(); + + // Clean up local allocated routes + inner.content.reset_details(); + + // Reset private route cache + inner.cache.reset_remote_private_routes(); + } + + /// Mark route as published + /// When first deserialized, routes must be re-published in order to ensure they remain + /// in the RouteSpecStore. + pub fn mark_route_published(&self, id: &RouteId, published: bool) -> EyreResult<()> { + let inner = &mut *self.inner.lock(); + let Some(rssd) = inner.content.get_detail_mut(id) else { + bail!("route does not exist"); + }; + rssd.set_published(published); + Ok(()) + } + + /// Process transfer statistics to get averages + pub fn roll_transfers(&self, last_ts: Timestamp, cur_ts: Timestamp) { + let inner = &mut *self.inner.lock(); + + // Roll transfers for locally allocated routes + inner.content.roll_transfers(last_ts, cur_ts); + + // Roll transfers for remote private routes + inner.cache.roll_transfers(last_ts, cur_ts); + } + + /// Convert private route list to binary blob + pub fn private_routes_to_blob(private_routes: &[PrivateRoute]) -> EyreResult> { + let mut buffer = vec![]; + + // Serialize count + let pr_count = private_routes.len(); + if pr_count > MAX_CRYPTO_KINDS { + bail!("too many crypto kinds to encode blob"); + } + let pr_count = pr_count as u8; + buffer.push(pr_count); + + // Serialize stream of private routes + for private_route in private_routes { + let mut pr_message = ::capnp::message::Builder::new_default(); + let mut pr_builder = pr_message.init_root::(); + + encode_private_route(private_route, &mut pr_builder) + .wrap_err("failed to encode private route")?; + + capnp::serialize_packed::write_message(&mut buffer, &pr_message) + .map_err(RPCError::internal) + .wrap_err("failed to convert builder to vec")?; + } + Ok(buffer) + } + + /// Convert binary blob to private route + pub fn blob_to_private_routes(crypto: Crypto, blob: Vec) -> EyreResult> { + // Deserialize count + if blob.is_empty() { + bail!("not deserializing empty private route blob"); + } + + let pr_count = blob[0] as usize; + if pr_count > MAX_CRYPTO_KINDS { + bail!("too many crypto kinds to decode blob"); + } + + // Deserialize stream of private routes + let mut pr_slice = &blob[1..]; + let mut out = Vec::with_capacity(pr_count); + for _ in 0..pr_count { + let reader = capnp::serialize_packed::read_message( + &mut pr_slice, + capnp::message::ReaderOptions::new(), + ) + .map_err(RPCError::internal) + .wrap_err("failed to make message reader")?; + + let pr_reader = reader + .get_root::() + .map_err(RPCError::internal) + .wrap_err("failed to make reader for private_route")?; + let private_route = + decode_private_route(&pr_reader).wrap_err("failed to decode private route")?; + private_route + .validate(crypto.clone()) + .wrap_err("failed to validate private route")?; + + out.push(private_route); + } + + // Don't trust the order of the blob + out.sort_by(|a, b| a.public_key.cmp(&b.public_key)); + + Ok(out) + } + + /// Generate RouteId from typed key set of route public keys + fn generate_allocated_route_id(&self, rssd: &RouteSetSpecDetail) -> EyreResult { + let route_set_keys = rssd.get_route_set_keys(); + let crypto = self.unlocked_inner.routing_table.crypto(); + + let mut idbytes = Vec::with_capacity(PUBLIC_KEY_LENGTH * route_set_keys.len()); + let mut best_kind: Option = None; + for tk in route_set_keys.iter() { + if best_kind.is_none() + || compare_crypto_kind(&tk.kind, best_kind.as_ref().unwrap()) == cmp::Ordering::Less + { + best_kind = Some(tk.kind); + } + idbytes.extend_from_slice(&tk.value.bytes); + } + let Some(best_kind) = best_kind else { + bail!("no compatible crypto kinds in route"); + }; + let vcrypto = crypto.get(best_kind).unwrap(); + + Ok(RouteId::new(vcrypto.generate_hash(&idbytes).bytes)) + } + + /// Generate RouteId from set of private routes + fn generate_remote_route_id(&self, private_routes: &[PrivateRoute]) -> EyreResult { + let crypto = self.unlocked_inner.routing_table.crypto(); + + let mut idbytes = Vec::with_capacity(PUBLIC_KEY_LENGTH * private_routes.len()); + let mut best_kind: Option = None; + for private_route in private_routes { + if best_kind.is_none() + || compare_crypto_kind(&private_route.public_key.kind, best_kind.as_ref().unwrap()) + == cmp::Ordering::Less + { + best_kind = Some(private_route.public_key.kind); + } + idbytes.extend_from_slice(&private_route.public_key.value.bytes); + } + let Some(best_kind) = best_kind else { + bail!("no compatible crypto kinds in route"); + }; + let vcrypto = crypto.get(best_kind).unwrap(); + + Ok(RouteId::new(vcrypto.generate_hash(&idbytes).bytes)) + } +} diff --git a/veilid-core/src/routing_table/route_spec_store/permutation.rs b/veilid-core/src/routing_table/route_spec_store/permutation.rs index 691e75c3..5b0443ae 100644 --- a/veilid-core/src/routing_table/route_spec_store/permutation.rs +++ b/veilid-core/src/routing_table/route_spec_store/permutation.rs @@ -13,7 +13,7 @@ fn _get_route_permutation_count(hop_count: usize) -> usize { // more than two nodes has factorial permutation // hop_count = 3 -> 2! -> 2 // hop_count = 4 -> 3! -> 6 - (3..hop_count).into_iter().fold(2usize, |acc, x| acc * x) + (3..hop_count).fold(2usize, |acc, x| acc * x) } pub type PermReturnType = (Vec, bool); pub type PermFunc<'t> = Box Option + Send + 't>; @@ -47,7 +47,7 @@ pub fn with_route_permutations( f: &mut PermFunc, ) -> Option { if size == 1 { - return f(&permutation); + return f(permutation); } for i in 0..size { diff --git a/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs b/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs index e4f58462..c41bb92b 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs @@ -112,7 +112,7 @@ impl RouteSetSpecDetail { } pub fn contains_nodes(&self, nodes: &[TypedKey]) -> bool { for tk in nodes { - for (_pk, rsd) in &self.route_set { + for rsd in self.route_set.values() { if rsd.crypto_kind == tk.kind && rsd.hops.contains(&tk.value) { return true; } 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 deleted file mode 100644 index 1889abca..00000000 --- a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs +++ /dev/null @@ -1,1606 +0,0 @@ -use super::*; -use permutation::*; - -#[derive(Debug)] -pub struct RouteSpecStoreInner { - /// Serialize RouteSpecStore content - content: RouteSpecStoreContent, - /// RouteSpecStore cache - cache: RouteSpecStoreCache, -} - -pub struct RouteSpecStoreUnlockedInner { - /// Handle to routing table - routing_table: RoutingTable, - /// Maximum number of hops in a route - max_route_hop_count: usize, - /// Default number of hops in a route - default_route_hop_count: usize, -} - -impl fmt::Debug for RouteSpecStoreUnlockedInner { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RouteSpecStoreUnlockedInner") - .field("max_route_hop_count", &self.max_route_hop_count) - .field("default_route_hop_count", &self.default_route_hop_count) - .finish() - } -} - -/// The routing table's storage for private/safety routes -#[derive(Clone, Debug)] -pub struct RouteSpecStore { - inner: Arc>, - unlocked_inner: Arc, -} - -impl RouteSpecStore { - pub fn new(routing_table: RoutingTable) -> Self { - let config = routing_table.network_manager().config(); - let c = config.get(); - - Self { - unlocked_inner: Arc::new(RouteSpecStoreUnlockedInner { - max_route_hop_count: c.network.rpc.max_route_hop_count.into(), - default_route_hop_count: c.network.rpc.default_route_hop_count.into(), - routing_table, - }), - inner: Arc::new(Mutex::new(RouteSpecStoreInner { - content: RouteSpecStoreContent::new(), - cache: Default::default(), - })), - } - } - - #[instrument(level = "trace", skip(routing_table), err)] - pub async fn load(routing_table: RoutingTable) -> EyreResult { - let (max_route_hop_count, default_route_hop_count) = { - let config = routing_table.network_manager().config(); - let c = config.get(); - ( - c.network.rpc.max_route_hop_count as usize, - c.network.rpc.default_route_hop_count as usize, - ) - }; - - // Get frozen blob from table store - let content = RouteSpecStoreContent::load(routing_table.clone()).await?; - - let mut inner = RouteSpecStoreInner { - content, - cache: Default::default(), - }; - - // Rebuild the routespecstore cache - let rti = &*routing_table.inner.read(); - for (_, rssd) in inner.content.iter_details() { - inner.cache.add_to_cache(rti, &rssd); - } - - // Return the loaded RouteSpecStore - let rss = RouteSpecStore { - unlocked_inner: Arc::new(RouteSpecStoreUnlockedInner { - max_route_hop_count, - default_route_hop_count, - routing_table: routing_table.clone(), - }), - inner: Arc::new(Mutex::new(inner)), - }; - - Ok(rss) - } - - - #[instrument(level = "trace", skip(self), err)] - pub async fn save(&self) -> EyreResult<()> { - let content = { - let inner = self.inner.lock(); - inner.content.clone() - }; - - // Save our content - content.save(self.unlocked_inner.routing_table.clone()).await?; - - Ok(()) - } - - #[instrument(level = "trace", skip(self))] - pub fn send_route_update(&self) { - let (dead_routes, dead_remote_routes) = { - let mut inner = self.inner.lock(); - let Some(dr) = inner.cache.take_dead_routes() else { - // Nothing to do - return; - }; - dr - }; - - let update = VeilidUpdate::RouteChange(VeilidRouteChange { - dead_routes, - dead_remote_routes, - }); - - let update_callback = self.unlocked_inner.routing_table.update_callback(); - update_callback(update); - } - - /// Purge the route spec store - pub async fn purge(&self) -> EyreResult<()> { - { - let inner = &mut *self.inner.lock(); - inner.content = Default::default(); - inner.cache = Default::default(); - } - self.save().await - } - - /// Create a new route - /// Prefers nodes that are not currently in use by another route - /// The route is not yet tested for its reachability - /// Returns None if no route could be allocated at this time - /// Returns Some route id string - #[instrument(level = "trace", skip(self), ret, err)] - pub fn allocate_route( - &self, - crypto_kinds: &[CryptoKind], - stability: Stability, - sequencing: Sequencing, - hop_count: usize, - directions: DirectionSet, - avoid_nodes: &[TypedKey], - ) -> EyreResult> { - let inner = &mut *self.inner.lock(); - let routing_table = self.unlocked_inner.routing_table.clone(); - let rti = &mut *routing_table.inner.write(); - - self.allocate_route_inner( - inner, - rti, - crypto_kinds, - stability, - sequencing, - hop_count, - directions, - avoid_nodes, - ) - } - - #[instrument(level = "trace", skip(self, inner, rti), ret, err)] - fn allocate_route_inner( - &self, - inner: &mut RouteSpecStoreInner, - rti: &mut RoutingTableInner, - crypto_kinds: &[CryptoKind], - stability: Stability, - sequencing: Sequencing, - hop_count: usize, - directions: DirectionSet, - avoid_nodes: &[TypedKey], - ) -> EyreResult> { - use core::cmp::Ordering; - - if hop_count < 1 { - bail!("Not allocating route less than one hop in length"); - } - - if hop_count > self.unlocked_inner.max_route_hop_count { - bail!("Not allocating route longer than max route hop count"); - } - - // Ensure we have a valid network class so our peer info is useful - if !rti.has_valid_network_class(RoutingDomain::PublicInternet) { - log_rtab!(debug "unable to allocate route until we have a valid PublicInternet network class"); - return Ok(None); - }; - - // Get our peer info - let our_peer_info = rti.get_own_peer_info(RoutingDomain::PublicInternet); - - // Get relay node if we have one - let opt_own_relay_nr = rti.relay_node(RoutingDomain::PublicInternet).map(|nr| nr.locked(rti)); - - // Get list of all nodes, and sort them for selection - let cur_ts = get_aligned_timestamp(); - let filter = Box::new( - |_rti: &RoutingTableInner, entry: Option>| -> bool { - // Exclude our own node from routes - if entry.is_none() { - return false; - } - let entry = entry.unwrap(); - - // Exclude our relay if we have one - if let Some(own_relay_nr) = &opt_own_relay_nr { - if own_relay_nr.same_bucket_entry(&entry) { - return false; - } - } - - // Process node info exclusions - let keep = entry.with_inner(|e| { - - // Exclude nodes that don't have our requested crypto kinds - let common_ck = e.common_crypto_kinds(crypto_kinds); - if common_ck.len() != crypto_kinds.len() { - return false; - } - - // Exclude nodes we have specifically chosen to avoid - if e.node_ids().contains_any(avoid_nodes) { - return false; - } - - // Exclude nodes on our local network - if e.node_info(RoutingDomain::LocalNetwork).is_some() { - return false; - } - - // Exclude nodes that have no publicinternet signednodeinfo - let Some(sni) = e.signed_node_info(RoutingDomain::PublicInternet) else { - return false; - }; - - // Relay check - let relay_ids = sni.relay_ids(); - if relay_ids.len() != 0 { - // Exclude nodes whose relays we have chosen to avoid - if relay_ids.contains_any(avoid_nodes) { - return false; - } - // Exclude nodes whose relay is our own relay if we have one - if let Some(own_relay_nr) = &opt_own_relay_nr { - if relay_ids.contains_any(&own_relay_nr.node_ids()) { - return false; - } - } - } - return true; - }); - if !keep { - return false; - } - - // 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().has_capability(CAP_ROUTE) - ).unwrap_or(false) - }) - }, - ) as RoutingTableEntryFilter; - let filters = VecDeque::from([filter]); - let compare = |_rti: &RoutingTableInner, - entry1: &Option>, - entry2: &Option>| - -> Ordering { - - // Our own node is filtered out - let entry1 = entry1.as_ref().unwrap().clone(); - let entry2 = entry2.as_ref().unwrap().clone(); - let entry1_node_ids = entry1.with_inner(|e| e.node_ids()); - let entry2_node_ids = entry2.with_inner(|e| e.node_ids()); - - // deprioritize nodes that we have already used as end points - let e1_used_end = inner.cache.get_used_end_node_count(&entry1_node_ids); - let e2_used_end = inner.cache.get_used_end_node_count(&entry2_node_ids); - let cmp_used_end = e1_used_end.cmp(&e2_used_end); - if !matches!(cmp_used_end, Ordering::Equal) { - return cmp_used_end; - } - - // deprioritize nodes we have used already anywhere - let e1_used = inner.cache.get_used_node_count(&entry1_node_ids); - let e2_used = inner.cache.get_used_node_count(&entry2_node_ids); - let cmp_used = e1_used.cmp(&e2_used); - if !matches!(cmp_used, Ordering::Equal) { - return cmp_used; - } - - // apply sequencing preference - // ensureordered will be taken care of by filter - // and nopreference doesn't care - if matches!(sequencing, Sequencing::PreferOrdered) { - let cmp_seq = entry1.with_inner(|e1| { - entry2.with_inner(|e2| { - let e1_can_do_ordered = e1.signed_node_info(RoutingDomain::PublicInternet).map(|sni| sni.has_sequencing_matched_dial_info(sequencing)).unwrap_or(false); - let e2_can_do_ordered = e2.signed_node_info(RoutingDomain::PublicInternet).map(|sni| sni.has_sequencing_matched_dial_info(sequencing)).unwrap_or(false); - e2_can_do_ordered.cmp(&e1_can_do_ordered) - }) - }); - if !matches!(cmp_seq, Ordering::Equal) { - return cmp_seq; - } - } - - // always prioritize reliable nodes, but sort by oldest or fastest - let cmpout = entry1.with_inner(|e1| { - entry2.with_inner(|e2| match stability { - Stability::LowLatency => { - BucketEntryInner::cmp_fastest_reliable(cur_ts, e1, e2) - } - Stability::Reliable => { - BucketEntryInner::cmp_oldest_reliable(cur_ts, e1, e2) - } - }) - }); - cmpout - }; - - let routing_table = self.unlocked_inner.routing_table.clone(); - let transform = - |_rti: &RoutingTableInner, entry: Option>| -> NodeRef { - NodeRef::new(routing_table.clone(), entry.unwrap(), None) - }; - - // Pull the whole routing table in sorted order - let nodes:Vec = - rti.find_peers_with_sort_and_filter(usize::MAX, cur_ts, filters, compare, transform); - - // If we couldn't find enough nodes, wait until we have more nodes in the routing table - if nodes.len() < hop_count { - log_rtab!(debug "not enough nodes to construct route at this time"); - return Ok(None); - } - - // Get peer info for everything - let nodes_pi: Vec = nodes.iter().map(|nr| nr.locked(rti).make_peer_info(RoutingDomain::PublicInternet).unwrap()).collect(); - - // Now go through nodes and try to build a route we haven't seen yet - let mut perm_func = Box::new(|permutation: &[usize]| { - - // Get the hop cache key for a particular route permutation - // uses the same algorithm as RouteSetSpecDetail::make_cache_key - let route_permutation_to_hop_cache = |_rti: &RoutingTableInner, nodes: &[NodeRef], perm: &[usize]| -> Vec { - let mut cache: Vec = Vec::with_capacity(perm.len() * PUBLIC_KEY_LENGTH); - for n in perm { - cache.extend_from_slice(&nodes[*n].locked(rti).best_node_id().value.bytes) - } - cache - }; - let cache_key = route_permutation_to_hop_cache(rti, &nodes, permutation); - - // Skip routes we have already seen - if inner.cache.contains_route(&cache_key) { - return None; - } - - // Ensure the route doesn't contain both a node and its relay - let mut seen_nodes: HashSet = HashSet::new(); - for n in permutation { - let node = nodes.get(*n).unwrap(); - if !seen_nodes.insert(node.locked(rti).best_node_id()) { - // Already seen this node, should not be in the route twice - return None; - } - let opt_relay = match node.locked_mut(rti).relay(RoutingDomain::PublicInternet) { - Ok(r) => r, - Err(_) => { - // Not selecting a relay through ourselves - return None; - } - }; - if let Some(relay) = opt_relay { - let relay_id = relay.locked(rti).best_node_id(); - if !seen_nodes.insert(relay_id) { - // Already seen this node, should not be in the route twice - return None; - } - } - } - - // Ensure this route is viable by checking that each node can contact the next one - let mut can_do_sequenced = true; - if directions.contains(Direction::Outbound) { - let mut previous_node = &our_peer_info; - let mut reachable = true; - for n in permutation { - let current_node = nodes_pi.get(*n).unwrap(); - let cm = rti.get_contact_method( - RoutingDomain::PublicInternet, - previous_node, - current_node, - DialInfoFilter::all(), - sequencing, - None, - ); - if matches!(cm, ContactMethod::Unreachable) { - reachable = false; - break; - } - - // Check if we can do sequenced specifically - if can_do_sequenced { - let cm = rti.get_contact_method( - RoutingDomain::PublicInternet, - previous_node, - current_node, - DialInfoFilter::all(), - Sequencing::EnsureOrdered, - None, - ); - if matches!(cm, ContactMethod::Unreachable) { - can_do_sequenced = false; - } - } - - previous_node = current_node; - } - if !reachable { - return None; - } - } - if directions.contains(Direction::Inbound) { - let mut next_node = &our_peer_info; - let mut reachable = true; - for n in permutation.iter().rev() { - let current_node = nodes_pi.get(*n).unwrap(); - let cm = rti.get_contact_method( - RoutingDomain::PublicInternet, - next_node, - current_node, - DialInfoFilter::all(), - sequencing, - None, - ); - if matches!(cm, ContactMethod::Unreachable) { - reachable = false; - break; - } - - // Check if we can do sequenced specifically - if can_do_sequenced { - let cm = rti.get_contact_method( - RoutingDomain::PublicInternet, - next_node, - current_node, - DialInfoFilter::all(), - Sequencing::EnsureOrdered, - None, - ); - if matches!(cm, ContactMethod::Unreachable) { - can_do_sequenced = false; - } - } - next_node = current_node; - } - if !reachable { - return None; - } - } - // Keep this route - let route_nodes = permutation.to_vec(); - Some((route_nodes, can_do_sequenced)) - }) as PermFunc; - - let mut route_nodes: Vec = Vec::new(); - let mut can_do_sequenced: bool = true; - - for start in 0..(nodes.len() - hop_count) { - // Try the permutations available starting with 'start' - if let Some((rn, cds)) = with_route_permutations(hop_count, start, &mut perm_func) { - route_nodes = rn; - can_do_sequenced = cds; - break; - } - } - if route_nodes.is_empty() { - log_rtab!(debug "unable to find unique route at this time"); - return Ok(None); - } - - drop(perm_func); - - // Got a unique route, lets build the details, register it, and return it - let hop_node_refs:Vec = route_nodes - .iter() - .map(|k| nodes[*k].clone()) - .collect(); - let mut route_set = BTreeMap::::new(); - for crypto_kind in crypto_kinds.iter().copied() { - let vcrypto = self.unlocked_inner.routing_table.crypto().get(crypto_kind).unwrap(); - let keypair = vcrypto.generate_keypair(); - let hops: Vec = route_nodes.iter().map(|v| nodes[*v].locked(rti).node_ids().get(crypto_kind).unwrap().value).collect(); - - route_set.insert(keypair.key, RouteSpecDetail { - crypto_kind, - secret_key: keypair.secret, - hops, - }); - } - - let rssd = RouteSetSpecDetail::new( - cur_ts, - route_set, - hop_node_refs, - directions, - stability, - can_do_sequenced, - ); - - - // make id - let id = self.generate_allocated_route_id(&rssd)?; - - // Add to cache - inner.cache.add_to_cache(rti, &rssd); - - // Keep route in spec store - inner.content.add_detail(id.clone(), rssd); - - Ok(Some(id)) - } - - /// validate data using a private route's key and signature chain - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, data, callback), ret))] - pub fn with_signature_validated_route( - &self, - public_key: &TypedKey, - signatures: &[Signature], - data: &[u8], - last_hop_id: PublicKey, - callback: F, - ) -> Option - where F: FnOnce(&RouteSetSpecDetail, &RouteSpecDetail) -> R, - R: fmt::Debug, - { - let inner = &*self.inner.lock(); - let crypto = self.unlocked_inner.routing_table.crypto(); - let Some(vcrypto) = crypto.get(public_key.kind) else { - log_rpc!(debug "can't handle route with public key: {:?}", public_key); - return None; - }; - - let Some(rsid) = inner.content.get_id_by_key(&public_key.value) else { - log_rpc!(debug "route id does not exist: {:?}", public_key.value); - return None; - }; - let Some(rssd) = inner.content.get_detail(&rsid) else { - log_rpc!(debug "route detail does not exist: {:?}", rsid); - return None; - }; - let Some(rsd) = rssd.get_route_by_key(&public_key.value) else { - log_rpc!(debug "route set {:?} does not have key: {:?}", rsid, public_key.value); - return None; - }; - - // Ensure we have the right number of signatures - if signatures.len() != rsd.hops.len() - 1 { - // Wrong number of signatures - log_rpc!(debug "wrong number of signatures ({} should be {}) for routed operation on private route {}", signatures.len(), rsd.hops.len() - 1, public_key); - return None; - } - // Validate signatures to ensure the route was handled by the nodes and not messed with - // This is in private route (reverse) order as we are receiving over the route - for (hop_n, hop_public_key) in rsd.hops.iter().rev().enumerate() { - // The last hop is not signed, as the whole packet is signed - if hop_n == signatures.len() { - // Verify the node we received the routed operation from is the last hop in our route - if *hop_public_key != last_hop_id { - log_rpc!(debug "received routed operation from the wrong hop ({} should be {}) on private route {}", hop_public_key.encode(), last_hop_id.encode(), public_key); - return None; - } - } else { - // Verify a signature for a hop node along the route - if let Err(e) = vcrypto.verify(hop_public_key, data, &signatures[hop_n]) { - log_rpc!(debug "failed to verify signature for hop {} at {} on private route {}: {}", hop_n, hop_public_key, public_key, e); - return None; - } - } - } - // We got the correct signatures, return a key and response safety spec - Some(callback(rssd, rsd)) - } - - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self), ret, err))] - async fn test_allocated_route(&self, private_route_id: RouteId) -> EyreResult { - // Make loopback route to test with - let dest = { - - // Get the best private route for this id - let (key, hop_count) = { - let inner = &mut *self.inner.lock(); - let Some(rssd) = inner.content.get_detail(&private_route_id) else { - bail!("route id not allocated"); - }; - let Some(key) = rssd.get_best_route_set_key() else { - bail!("no best key to test allocated route"); - }; - // Match the private route's hop length for safety route length - let hop_count = rssd.hop_count(); - (key, hop_count) - }; - - // Get the private route to send to - let private_route = self.assemble_private_route(&key, None)?; - // Always test routes with safety routes that are more likely to succeed - let stability = Stability::Reliable; - // Routes should test with the most likely to succeed sequencing they are capable of - let sequencing = Sequencing::PreferOrdered; - - let safety_spec = SafetySpec { - preferred_route: Some(private_route_id), - hop_count, - stability, - sequencing, - }; - let safety_selection = SafetySelection::Safe(safety_spec); - - Destination::PrivateRoute { - private_route, - safety_selection, - } - }; - - // Test with double-round trip ping to self - let rpc_processor = self.unlocked_inner.routing_table.rpc_processor(); - let _res = match rpc_processor.rpc_call_status(dest).await? { - NetworkResult::Value(v) => v, - _ => { - // Did not error, but did not come back, just return false - return Ok(false); - } - }; - - Ok(true) - } - - #[instrument(level = "trace", skip(self), ret, err)] - async fn test_remote_route(&self, private_route_id: RouteId) -> EyreResult { - - // Make private route test - let dest = { - - // Get the route to test - let Some(private_route) = self.best_remote_private_route(&private_route_id) else { - bail!("no best key to test remote route"); - }; - - // Always test routes with safety routes that are more likely to succeed - let stability = Stability::Reliable; - // Routes should test with the most likely to succeed sequencing they are capable of - let sequencing = Sequencing::PreferOrdered; - - // Get a safety route that is good enough - let safety_spec = SafetySpec { - preferred_route: None, - hop_count: self.unlocked_inner.default_route_hop_count, - stability, - sequencing, - }; - - let safety_selection = SafetySelection::Safe(safety_spec); - - Destination::PrivateRoute { - private_route, - safety_selection, - } - }; - - // Test with double-round trip ping to self - let rpc_processor = self.unlocked_inner.routing_table.rpc_processor(); - let _res = match rpc_processor.rpc_call_status(dest).await? { - NetworkResult::Value(v) => v, - _ => { - // Did not error, but did not come back, just return false - return Ok(false); - } - }; - - Ok(true) - } - - /// Release an allocated route that is no longer in use - #[instrument(level = "trace", skip(self), ret)] - fn release_allocated_route(&self, id: RouteId) -> bool { - let mut inner = self.inner.lock(); - let Some(rssd) = inner.content.remove_detail(&id) else { - return false; - }; - - // Remove from hop cache - let rti = &*self.unlocked_inner.routing_table.inner.read(); - if !inner.cache.remove_from_cache(rti, id, &rssd) { - panic!("hop cache should have contained cache key"); - } - - true - } - - /// Check if a route id is remote or not - pub fn is_route_id_remote(&self, id: &RouteId) -> bool { - let inner = &mut *self.inner.lock(); - let cur_ts = get_aligned_timestamp(); - inner.cache.peek_remote_private_route_mut(cur_ts, &id).is_some() - } - - /// Test an allocated route for continuity - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self), ret, err))] - pub async fn test_route(&self, id: RouteId) -> EyreResult { - let is_remote = self.is_route_id_remote(&id); - if is_remote { - self.test_remote_route(id).await - } else { - self.test_allocated_route(id).await - } - } - - /// Release an allocated or remote route that is no longer in use - #[instrument(level = "trace", skip(self), ret)] - pub fn release_route(&self, id: RouteId) -> bool { - let is_remote = self.is_route_id_remote(&id); - if is_remote { - self.release_remote_private_route(id) - } else { - self.release_allocated_route(id) - } - } - - /// Find first matching unpublished route that fits into the selection criteria - /// Don't pick any routes that have failed and haven't been tested yet - fn first_available_route_inner<'a>( - inner: &'a RouteSpecStoreInner, - crypto_kind: CryptoKind, - min_hop_count: usize, - max_hop_count: usize, - stability: Stability, - sequencing: Sequencing, - directions: DirectionSet, - avoid_nodes: &[TypedKey], - ) -> Option { - let cur_ts = get_aligned_timestamp(); - - let mut routes = Vec::new(); - - // Get all valid routes, allow routes that need testing - // but definitely prefer routes that have been recently tested - for (id, rssd) in inner.content.iter_details() { - if rssd.get_stability() >= stability - && rssd.is_sequencing_match(sequencing) - && rssd.hop_count() >= min_hop_count - && rssd.hop_count() <= max_hop_count - && rssd.get_directions().is_superset(directions) - && rssd.get_route_set_keys().kinds().contains(&crypto_kind) - && !rssd.is_published() - && !rssd.contains_nodes(avoid_nodes) - { - routes.push((id, rssd)); - } - } - - // Sort the routes by preference - routes.sort_by(|a, b| { - let a_needs_testing = a.1.get_stats().needs_testing(cur_ts); - let b_needs_testing = b.1.get_stats().needs_testing(cur_ts); - if !a_needs_testing && b_needs_testing { - return cmp::Ordering::Less; - } - if !b_needs_testing && a_needs_testing { - return cmp::Ordering::Greater; - } - let a_latency = a.1.get_stats().latency_stats().average; - let b_latency = b.1.get_stats().latency_stats().average; - - a_latency.cmp(&b_latency) - }); - - // Return the best one if we got one - routes.first().map(|r| *r.0) - } - - /// List all allocated routes - pub fn list_allocated_routes(&self, mut filter: F) -> Vec - where - F: FnMut(&RouteId, &RouteSetSpecDetail) -> Option, - { - let inner = self.inner.lock(); - let mut out = Vec::with_capacity(inner.content.get_detail_count()); - for detail in inner.content.iter_details() { - if let Some(x) = filter(detail.0, detail.1) { - out.push(x); - } - } - out - } - - /// List all allocated routes - pub fn list_remote_routes(&self, mut filter: F) -> Vec - where - F: FnMut(&RouteId, &RemotePrivateRouteInfo) -> Option, - { - let inner = self.inner.lock(); - let mut out = Vec::with_capacity(inner.cache.get_remote_private_route_count()); - for info in inner.cache.iter_remote_private_routes() { - if let Some(x) = filter(info.0, info.1) { - out.push(x); - } - } - out - } - - /// Get the debug description of a route - pub fn debug_route(&self, id: &RouteId) -> Option { - let inner = &mut *self.inner.lock(); - let cur_ts = get_aligned_timestamp(); - if let Some(rpri) = inner.cache.peek_remote_private_route_mut(cur_ts, &id) { - return Some(format!("{:#?}", rpri)); - } - if let Some(rssd) = inner.content.get_detail(id) { - return Some(format!("{:#?}", rssd)); - } - None - } - - ////////////////////////////////////////////////////////////////////// - - /// Choose the best private route from a private route set to communicate with - pub fn best_remote_private_route(&self, id: &RouteId) -> Option { - let inner = &mut *self.inner.lock(); - let cur_ts = get_aligned_timestamp(); - let rpri = inner.cache.get_remote_private_route(cur_ts, id)?; - rpri.best_private_route() - } - - - /// Compiles a safety route to the private route, with caching - /// Returns an Err() if the parameters are wrong - /// Returns Ok(None) if no allocation could happen at this time (not an error) - pub fn compile_safety_route( - &self, - safety_selection: SafetySelection, - mut private_route: PrivateRoute, - ) -> EyreResult> { - // let profile_start_ts = get_timestamp(); - let inner = &mut *self.inner.lock(); - let routing_table = self.unlocked_inner.routing_table.clone(); - let rti = &mut *routing_table.inner.write(); - - // Get useful private route properties - let crypto_kind = private_route.crypto_kind(); - let crypto = routing_table.crypto(); - let Some(vcrypto) = crypto.get(crypto_kind) else { - bail!("crypto not supported for route"); - }; - let pr_pubkey = private_route.public_key.value; - let pr_hopcount = private_route.hop_count as usize; - let max_route_hop_count = self.unlocked_inner.max_route_hop_count; - - // Check private route hop count isn't larger than the max route hop count plus one for the 'first hop' header - if pr_hopcount > (max_route_hop_count + 1) { - bail!("private route hop count too long"); - } - // See if we are using a safety route, if not, short circuit this operation - let safety_spec = match safety_selection { - // Safety route spec to use - SafetySelection::Safe(safety_spec) => safety_spec, - // Safety route stub with the node's public key as the safety route key since it's the 0th hop - SafetySelection::Unsafe(sequencing) => { - let Some(pr_first_hop_node) = private_route.pop_first_hop() else { - bail!("compiled private route should have first hop"); - }; - - let opt_first_hop = match pr_first_hop_node { - RouteNode::NodeId(id) => rti.lookup_node_ref(routing_table.clone(), TypedKey::new(crypto_kind, id))?, - RouteNode::PeerInfo(pi) => { - Some(rti.register_node_with_peer_info( - routing_table.clone(), - RoutingDomain::PublicInternet, - pi, - false, - )?) - } - }; - if opt_first_hop.is_none() { - // Can't reach this private route any more - log_rtab!(debug "can't reach private route any more"); - return Ok(None); - } - let mut first_hop = opt_first_hop.unwrap(); - - // Set sequencing requirement - first_hop.set_sequencing(sequencing); - - // Return the compiled safety route - //println!("compile_safety_route profile (stub): {} us", (get_timestamp() - profile_start_ts)); - return Ok(Some(CompiledRoute { - safety_route: SafetyRoute::new_stub(routing_table.node_id(crypto_kind), private_route), - secret: routing_table.node_id_secret_key(crypto_kind), - first_hop, - })); - } - }; - - // If the safety route requested is also the private route, this is a loopback test, just accept it - let opt_private_route_id = inner.content.get_id_by_key(&pr_pubkey); - let sr_pubkey = if opt_private_route_id.is_some() && safety_spec.preferred_route == opt_private_route_id { - // Private route is also safety route during loopback test - pr_pubkey - } else { - let Some(avoid_node_id) = private_route.first_hop_node_id() else { - bail!("compiled private route should have first hop"); - }; - let Some(sr_pubkey) = self.get_route_for_safety_spec_inner(inner, rti, crypto_kind, &safety_spec, Direction::Outbound.into(), &[avoid_node_id])? else { - // No safety route could be found for this spec - return Ok(None); - }; - sr_pubkey - }; - - // Look up a few things from the safety route detail we want for the compiled route and don't borrow inner - let Some(safety_route_id) = inner.content.get_id_by_key(&sr_pubkey) else { - bail!("route id missing"); - }; - let Some(safety_rssd) = inner.content.get_detail(&safety_route_id) else { - bail!("route set detail missing"); - }; - let Some(safety_rsd) = safety_rssd.get_route_by_key(&sr_pubkey) else { - bail!("route detail missing"); - }; - - // We can optimize the peer info in this safety route if it has been successfully - // communicated over either via an outbound test, or used as a private route inbound - // and we are replying over the same route as our safety route outbound - let optimize = safety_rssd.get_stats().last_tested_ts.is_some() || safety_rssd.get_stats().last_received_ts.is_some(); - - // Get the first hop noderef of the safety route - let mut first_hop = safety_rssd.hop_node_ref(0).unwrap(); - - // Ensure sequencing requirement is set on first hop - first_hop.set_sequencing(safety_spec.sequencing); - - // Get the safety route secret key - let secret = safety_rsd.secret_key; - - // See if we have a cached route we can use - if optimize { - if let Some(safety_route) = inner.cache.lookup_compiled_route_cache(sr_pubkey, pr_pubkey) { - // Build compiled route - let compiled_route = CompiledRoute { - safety_route, - secret, - first_hop, - }; - // Return compiled route - //println!("compile_safety_route profile (cached): {} us", (get_timestamp() - profile_start_ts)); - return Ok(Some(compiled_route)); - } - } - - // Create hops - let hops = { - // start last blob-to-encrypt data off as private route - let mut blob_data = { - let mut pr_message = ::capnp::message::Builder::new_default(); - let mut pr_builder = pr_message.init_root::(); - encode_private_route(&private_route, &mut pr_builder)?; - let mut blob_data = builder_to_vec(pr_message)?; - - // append the private route tag so we know how to decode it later - blob_data.push(1u8); - blob_data - }; - - // Encode each hop from inside to outside - // skips the outermost hop since that's entering the - // safety route and does not include the dialInfo - // (outer hop is a RouteHopData, not a RouteHop). - // Each loop mutates 'nonce', and 'blob_data' - let mut nonce = vcrypto.random_nonce(); - // Forward order (safety route), but inside-out - for h in (1..safety_rsd.hops.len()).rev() { - // Get blob to encrypt for next hop - blob_data = { - // Encrypt the previous blob ENC(nonce, DH(PKhop,SKsr)) - let dh_secret = vcrypto - .cached_dh(&safety_rsd.hops[h], &safety_rsd.secret_key) - .wrap_err("dh failed")?; - let enc_msg_data = - vcrypto.encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None) - .wrap_err("encryption failed")?; - - // Make route hop data - let route_hop_data = RouteHopData { - nonce, - blob: enc_msg_data, - }; - - // Make route hop - let route_hop = RouteHop { - node: if optimize { - // Optimized, no peer info, just the dht key - RouteNode::NodeId(safety_rsd.hops[h]) - } else { - // Full peer info, required until we are sure the route has been fully established - let node_id = TypedKey::new(safety_rsd.crypto_kind, safety_rsd.hops[h]); - let pi = rti - .with_node_entry(node_id, |entry| { - entry.with(rti, |_rti, e| { - e.make_peer_info(RoutingDomain::PublicInternet) - }) - }) - .flatten(); - if pi.is_none() { - bail!("peer info should exist for route but doesn't"); - } - RouteNode::PeerInfo(pi.unwrap()) - }, - next_hop: Some(route_hop_data), - }; - - // Make next blob from route hop - let mut rh_message = ::capnp::message::Builder::new_default(); - let mut rh_builder = rh_message.init_root::(); - encode_route_hop(&route_hop, &mut rh_builder)?; - let mut blob_data = builder_to_vec(rh_message)?; - - // Append the route hop tag so we know how to decode it later - blob_data.push(0u8); - blob_data - }; - - // Make another nonce for the next hop - nonce = vcrypto.random_nonce(); - } - - // Encode first RouteHopData - let dh_secret = vcrypto - .cached_dh(&safety_rsd.hops[0], &safety_rsd.secret_key) - .map_err(RPCError::map_internal("dh failed"))?; - let enc_msg_data = vcrypto.encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None) - .map_err(RPCError::map_internal("encryption failed"))?; - - let route_hop_data = RouteHopData { - nonce, - blob: enc_msg_data, - }; - - SafetyRouteHops::Data(route_hop_data) - }; - - // Build safety route - let safety_route = SafetyRoute { - public_key: TypedKey::new(crypto_kind, sr_pubkey), - hop_count: safety_spec.hop_count as u8, - hops, - }; - - // Add to cache but only if we have an optimized route - if optimize { - inner.cache.add_to_compiled_route_cache( pr_pubkey, safety_route.clone()); - } - - // Build compiled route - let compiled_route = CompiledRoute { - safety_route, - secret, - first_hop, - }; - - // Return compiled route - //println!("compile_safety_route profile (uncached): {} us", (get_timestamp() - profile_start_ts)); - Ok(Some(compiled_route)) - } - - /// Get an allocated route that matches a particular safety spec - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, inner, rti), ret, err))] - fn get_route_for_safety_spec_inner( - &self, - inner: &mut RouteSpecStoreInner, - rti: &mut RoutingTableInner, - crypto_kind: CryptoKind, - safety_spec: &SafetySpec, - direction: DirectionSet, - avoid_nodes: &[TypedKey], - ) -> EyreResult> { - // Ensure the total hop count isn't too long for our config - let max_route_hop_count = self.unlocked_inner.max_route_hop_count; - if safety_spec.hop_count == 0 { - bail!("safety route hop count is zero"); - } - if safety_spec.hop_count > max_route_hop_count { - bail!("safety route hop count too long"); - } - - // See if the preferred route is here - if let Some(preferred_route) = safety_spec.preferred_route { - if let Some(preferred_rssd) = inner.content.get_detail(&preferred_route) { - // Only use the preferred route if it has the desired crypto kind - if let Some(preferred_key) = preferred_rssd.get_route_set_keys().get(crypto_kind) { - // Only use the preferred route if it doesn't contain the avoid nodes - if !preferred_rssd.contains_nodes(avoid_nodes) { - return Ok(Some(preferred_key.value)); - } - } - } - } - - // Select a safety route from the pool or make one if we don't have one that matches - let sr_route_id = if let Some(sr_route_id) = Self::first_available_route_inner( - inner, - crypto_kind, - safety_spec.hop_count, - safety_spec.hop_count, - safety_spec.stability, - safety_spec.sequencing, - direction, - avoid_nodes, - ) { - // Found a route to use - sr_route_id - } else { - // No route found, gotta allocate one - let Some(sr_route_id) = self - .allocate_route_inner( - inner, - rti, - &[crypto_kind], - safety_spec.stability, - safety_spec.sequencing, - safety_spec.hop_count, - direction, - avoid_nodes, - ) - .map_err(RPCError::internal)? - else { - return Ok(None); - }; - sr_route_id - }; - - let sr_pubkey = inner.content.get_detail(&sr_route_id).unwrap().get_route_set_keys().get(crypto_kind).unwrap().value; - - Ok(Some(sr_pubkey)) - } - - /// Get a private route to use for the answer to question - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self), ret, err))] - pub fn get_private_route_for_safety_spec( - &self, - crypto_kind: CryptoKind, - safety_spec: &SafetySpec, - avoid_nodes: &[TypedKey], - ) -> EyreResult> { - let inner = &mut *self.inner.lock(); - let routing_table = self.unlocked_inner.routing_table.clone(); - let rti = &mut *routing_table.inner.write(); - - Ok(self.get_route_for_safety_spec_inner( - inner, - rti, - crypto_kind, - safety_spec, - Direction::Inbound.into(), - avoid_nodes, - )?) - } - - fn assemble_private_route_inner(&self, key: &PublicKey, rsd: &RouteSpecDetail, optimized: bool) -> EyreResult - { - let routing_table = self.unlocked_inner.routing_table.clone(); - let rti = &*routing_table.inner.read(); - - // Ensure we get the crypto for it - let crypto = routing_table.network_manager().crypto(); - let Some(vcrypto) = crypto.get(rsd.crypto_kind) else { - bail!("crypto not supported for route"); - }; - - // Ensure our network class is valid before attempting to assemble any routes - if !rti.has_valid_network_class(RoutingDomain::PublicInternet) { - bail!("can't make private routes until our node info is valid"); - } - - // Make innermost route hop to our own node - let mut route_hop = RouteHop { - node: if optimized { - let Some(node_id) = routing_table.node_ids().get(rsd.crypto_kind) else { - bail!("missing node id for crypto kind"); - }; - RouteNode::NodeId(node_id.value) - } else { - let pi = rti.get_own_peer_info(RoutingDomain::PublicInternet); - RouteNode::PeerInfo(pi) - }, - next_hop: None, - }; - - // Loop for each hop - let hop_count = rsd.hops.len(); - // iterate hops in private route order (reverse, but inside out) - for h in 0..hop_count { - let nonce = vcrypto.random_nonce(); - - let blob_data = { - let mut rh_message = ::capnp::message::Builder::new_default(); - let mut rh_builder = rh_message.init_root::(); - encode_route_hop(&route_hop, &mut rh_builder)?; - builder_to_vec(rh_message)? - }; - - // Encrypt the previous blob ENC(nonce, DH(PKhop,SKpr)) - let dh_secret = vcrypto - .cached_dh(&rsd.hops[h], &rsd.secret_key) - .wrap_err("dh failed")?; - let enc_msg_data = vcrypto.encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None) - .wrap_err("encryption failed")?; - let route_hop_data = RouteHopData { - nonce, - blob: enc_msg_data, - }; - - route_hop = RouteHop { - node: if optimized { - // Optimized, no peer info, just the dht key - RouteNode::NodeId(rsd.hops[h]) - } else { - // Full peer info, required until we are sure the route has been fully established - let node_id = TypedKey::new(rsd.crypto_kind, rsd.hops[h]); - let pi = rti - .with_node_entry(node_id, |entry| { - entry.with(rti, |_rti, e| { - e.make_peer_info(RoutingDomain::PublicInternet) - }) - }) - .flatten(); - if pi.is_none() { - bail!("peer info should exist for route but doesn't",); - } - RouteNode::PeerInfo(pi.unwrap()) - }, - next_hop: Some(route_hop_data), - } - } - - let private_route = PrivateRoute { - public_key: TypedKey::new(rsd.crypto_kind, key.clone()), - // add hop for 'FirstHop' - hop_count: (hop_count + 1).try_into().unwrap(), - hops: PrivateRouteHops::FirstHop(route_hop), - }; - Ok(private_route) - } - - /// Assemble a single private route for publication - /// Returns a PrivateRoute object for an allocated private route key - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self), err))] - pub fn assemble_private_route( - &self, - key: &PublicKey, - optimized: Option, - ) -> EyreResult { - let inner = &*self.inner.lock(); - let Some(rsid) = inner.content.get_id_by_key(key) else { - bail!("route key does not exist"); - }; - let Some(rssd) = inner.content.get_detail(&rsid) else { - bail!("route id does not exist"); - }; - - // See if we can optimize this compilation yet - // We don't want to include full nodeinfo if we don't have to - let optimized = optimized - .unwrap_or(rssd.get_stats().last_tested_ts.is_some() || rssd.get_stats().last_received_ts.is_some()); - - let rsd = rssd.get_route_by_key(key).expect("route key index is broken"); - - self.assemble_private_route_inner(key, rsd, optimized) - } - - - /// Assemble private route set for publication - /// Returns a vec of PrivateRoute objects for an allocated private route - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self), err))] - pub fn assemble_private_routes( - &self, - id: &RouteId, - optimized: Option, - ) -> EyreResult> { - let inner = &*self.inner.lock(); - let Some(rssd) = inner.content.get_detail(id) else { - bail!("route id does not exist"); - }; - - // See if we can optimize this compilation yet - // We don't want to include full nodeinfo if we don't have to - let optimized = optimized - .unwrap_or(rssd.get_stats().last_tested_ts.is_some() || rssd.get_stats().last_received_ts.is_some()); - - let mut out = Vec::new(); - for (key, rsd) in rssd.iter_route_set() { - out.push(self.assemble_private_route_inner(key, rsd, optimized)?); - } - Ok(out) - } - - /// Import a remote private route for compilation - /// It is safe to import the same route more than once and it will return the same route id - /// Returns a route set id - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, blob), ret, err))] - pub fn import_remote_private_route(&self, blob: Vec) -> EyreResult { - let cur_ts = get_aligned_timestamp(); - - // decode the pr blob - let private_routes = RouteSpecStore::blob_to_private_routes(self.unlocked_inner.routing_table.crypto(), blob)?; - - // make the route id - let id = self.generate_remote_route_id(&private_routes)?; - - // validate the private routes - let inner = &mut *self.inner.lock(); - for private_route in &private_routes { - - // ensure private route has first hop - if !matches!(private_route.hops, PrivateRouteHops::FirstHop(_)) { - bail!("private route must have first hop"); - } - - // ensure this isn't also an allocated route - // if inner.content.get_id_by_key(&private_route.public_key.value).is_some() { - // bail!("should not import allocated route"); - // } - } - - inner.cache.cache_remote_private_route(cur_ts, id, private_routes); - - Ok(id) - } - - /// Release a remote private route that is no longer in use - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self), ret))] - pub fn release_remote_private_route(&self, id: RouteId) -> bool { - let inner = &mut *self.inner.lock(); - inner.cache.remove_remote_private_route(id) - } - - /// Get a route id for a route's public key - pub fn get_route_id_for_key(&self, key: &PublicKey) -> Option - { - let inner = &mut *self.inner.lock(); - // Check for local route - if let Some(id) = inner.content.get_id_by_key(key) { - return Some(id); - } - - // Check for remote route - if let Some(rrid) = inner.cache.get_remote_private_route_id_by_key(key) { - return Some(rrid); - } - - None - } - - /// Check to see if this remote (not ours) private route has seen our current node info yet - /// This happens when you communicate with a private route without a safety route - pub fn has_remote_private_route_seen_our_node_info(&self, key: &PublicKey) -> bool { - - let inner = &mut *self.inner.lock(); - - // Check for local route. If this is not a remote private route, - // we may be running a test and using our own local route as the destination private route. - // In that case we definitely have already seen our own node info - if let Some(_) = inner.content.get_id_by_key(key) { - return true; - } - - if let Some(rrid) = inner.cache.get_remote_private_route_id_by_key(key) { - let cur_ts = get_aligned_timestamp(); - if let Some(rpri) = inner.cache.peek_remote_private_route_mut(cur_ts, &rrid) - { - let our_node_info_ts = self.unlocked_inner.routing_table.get_own_node_info_ts(RoutingDomain::PublicInternet); - return rpri.has_seen_our_node_info_ts(our_node_info_ts); - } - } - - false - } - - /// Mark a remote private route as having seen our current node info - /// PRIVACY: - /// We do not accept node info timestamps from remote private routes because this would - /// enable a deanonymization attack, whereby a node could be 'pinged' with a doctored node_info with a - /// special 'timestamp', which then may be sent back over a private route, identifying that it - /// was that node that had the private route. - pub fn mark_remote_private_route_seen_our_node_info( - &self, - key: &PublicKey, - cur_ts: Timestamp, - ) -> EyreResult<()> { - let our_node_info_ts = self.unlocked_inner.routing_table.get_own_node_info_ts(RoutingDomain::PublicInternet); - - let inner = &mut *self.inner.lock(); - - // Check for local route. If this is not a remote private route - // then we just skip the recording. We may be running a test and using - // our own local route as the destination private route. - if let Some(_) = inner.content.get_id_by_key(key) { - return Ok(()); - } - - if let Some(rrid) = inner.cache.get_remote_private_route_id_by_key(key) { - if let Some(rpri) = inner.cache.peek_remote_private_route_mut(cur_ts, &rrid) - { - rpri.set_last_seen_our_node_info_ts(our_node_info_ts); - return Ok(()); - } - } - - bail!("private route is missing from store: {}", key); - } - - /// Get the route statistics for any route we know about, local or remote - pub fn with_route_stats(&self, cur_ts: Timestamp, key: &PublicKey, f: F) -> Option - where - F: FnOnce(&mut RouteStats) -> R, - { - let inner = &mut *self.inner.lock(); - - // Check for stub route - if self.unlocked_inner.routing_table.matches_own_node_id_key(key) { - return None; - } - - // Check for local route - if let Some(rsid) = inner.content.get_id_by_key(key) { - if let Some(rsd) = inner.content.get_detail_mut(&rsid) { - return Some(f(rsd.get_stats_mut())); - } - } - - // Check for remote route - if let Some(rrid) = inner.cache.get_remote_private_route_id_by_key(key) { - if let Some(rpri) = inner.cache.peek_remote_private_route_mut(cur_ts, &rrid) - { - return Some(f(rpri.get_stats_mut())); - } - } - - None - } - - /// Clear caches when local our local node info changes - #[instrument(level = "trace", skip(self))] - pub fn reset(&self) { - let inner = &mut *self.inner.lock(); - - // Clean up local allocated routes - inner.content.reset_details(); - - // Reset private route cache - inner.cache.reset_remote_private_routes(); - } - - /// Mark route as published - /// When first deserialized, routes must be re-published in order to ensure they remain - /// in the RouteSpecStore. - pub fn mark_route_published(&self, id: &RouteId, published: bool) -> EyreResult<()> { - let inner = &mut *self.inner.lock(); - let Some(rssd) = inner.content.get_detail_mut(id) else { - bail!("route does not exist"); - }; - rssd.set_published(published); - Ok(()) - } - - /// Process transfer statistics to get averages - pub fn roll_transfers(&self, last_ts: Timestamp, cur_ts: Timestamp) { - let inner = &mut *self.inner.lock(); - - // Roll transfers for locally allocated routes - inner.content.roll_transfers(last_ts, cur_ts); - - // Roll transfers for remote private routes - inner.cache.roll_transfers(last_ts, cur_ts); - } - - /// Convert private route list to binary blob - pub fn private_routes_to_blob(private_routes: &[PrivateRoute]) -> EyreResult> { - - let mut buffer = vec![]; - - // Serialize count - let pr_count = private_routes.len(); - if pr_count > MAX_CRYPTO_KINDS { - bail!("too many crypto kinds to encode blob"); - } - let pr_count = pr_count as u8; - buffer.push(pr_count); - - // Serialize stream of private routes - for private_route in private_routes { - let mut pr_message = ::capnp::message::Builder::new_default(); - let mut pr_builder = pr_message.init_root::(); - - encode_private_route(private_route, &mut pr_builder) - .wrap_err("failed to encode private route")?; - - capnp::serialize_packed::write_message(&mut buffer, &pr_message) - .map_err(RPCError::internal) - .wrap_err("failed to convert builder to vec")?; - } - Ok(buffer) - } - - /// Convert binary blob to private route - pub fn blob_to_private_routes(crypto: Crypto, blob: Vec) -> EyreResult> { - - // Deserialize count - if blob.is_empty() { - bail!("not deserializing empty private route blob"); - } - - let pr_count = blob[0] as usize; - if pr_count > MAX_CRYPTO_KINDS { - bail!("too many crypto kinds to decode blob"); - } - - // Deserialize stream of private routes - let mut pr_slice = &blob[1..]; - let mut out = Vec::with_capacity(pr_count); - for _ in 0..pr_count { - let reader = capnp::serialize_packed::read_message( - &mut pr_slice, - capnp::message::ReaderOptions::new(), - ) - .map_err(RPCError::internal) - .wrap_err("failed to make message reader")?; - - let pr_reader = reader - .get_root::() - .map_err(RPCError::internal) - .wrap_err("failed to make reader for private_route")?; - let private_route = decode_private_route(&pr_reader).wrap_err("failed to decode private route")?; - private_route.validate(crypto.clone()).wrap_err("failed to validate private route")?; - - out.push(private_route); - } - - // Don't trust the order of the blob - out.sort_by(|a,b| { - a.public_key.cmp(&b.public_key) - }); - - Ok(out) - } - - /// Generate RouteId from typed key set of route public keys - fn generate_allocated_route_id(&self, rssd: &RouteSetSpecDetail) -> EyreResult { - let route_set_keys = rssd.get_route_set_keys(); - let crypto = self.unlocked_inner.routing_table.crypto(); - - let mut idbytes = Vec::with_capacity(PUBLIC_KEY_LENGTH * route_set_keys.len()); - let mut best_kind : Option = None; - for tk in route_set_keys.iter() { - if best_kind.is_none() || compare_crypto_kind(&tk.kind, best_kind.as_ref().unwrap()) == cmp::Ordering::Less { - best_kind = Some(tk.kind); - } - idbytes.extend_from_slice(&tk.value.bytes); - } - let Some(best_kind) = best_kind else { - bail!("no compatible crypto kinds in route"); - }; - let vcrypto = crypto.get(best_kind).unwrap(); - - Ok(RouteId::new(vcrypto.generate_hash(&idbytes).bytes)) - - } - - /// Generate RouteId from set of private routes - fn generate_remote_route_id(&self, private_routes: &[PrivateRoute]) -> EyreResult { - let crypto = self.unlocked_inner.routing_table.crypto(); - - let mut idbytes = Vec::with_capacity(PUBLIC_KEY_LENGTH * private_routes.len()); - let mut best_kind : Option = None; - for private_route in private_routes { - if best_kind.is_none() || compare_crypto_kind(&private_route.public_key.kind, best_kind.as_ref().unwrap()) == cmp::Ordering::Less { - best_kind = Some(private_route.public_key.kind); - } - idbytes.extend_from_slice(&private_route.public_key.value.bytes); - } - let Some(best_kind) = best_kind else { - bail!("no compatible crypto kinds in route"); - }; - let vcrypto = crypto.get(best_kind).unwrap(); - - Ok(RouteId::new(vcrypto.generate_hash(&idbytes).bytes)) - } - -} diff --git a/veilid-core/src/routing_table/route_spec_store/route_spec_store_cache.rs b/veilid-core/src/routing_table/route_spec_store/route_spec_store_cache.rs index 1ad861a8..e7e44bbf 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_spec_store_cache.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_spec_store_cache.rs @@ -144,7 +144,7 @@ impl RouteSpecStoreCache { // also store in id by key table for private_route in rprinfo.get_private_routes() { self.remote_private_routes_by_key - .insert(private_route.public_key.value, id.clone()); + .insert(private_route.public_key.value, id); } let mut dead = None; diff --git a/veilid-core/src/routing_table/route_spec_store/route_spec_store_content.rs b/veilid-core/src/routing_table/route_spec_store/route_spec_store_content.rs index e1d003e3..d41a70fa 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_spec_store_content.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_spec_store_content.rs @@ -29,18 +29,20 @@ impl RouteSpecStoreContent { for (rsid, rssd) in content.details.iter_mut() { // Get best route since they all should resolve let Some(pk) = rssd.get_best_route_set_key() else { - dead_ids.push(rsid.clone()); + dead_ids.push(*rsid); continue; }; let Some(rsd) = rssd.get_route_by_key(&pk) else { - dead_ids.push(rsid.clone()); + dead_ids.push(*rsid); continue; }; // Go through best route and resolve noderefs let mut hop_node_refs = Vec::with_capacity(rsd.hops.len()); for h in &rsd.hops { - let Ok(Some(nr)) = routing_table.lookup_node_ref(TypedKey::new(rsd.crypto_kind, *h)) else { - dead_ids.push(rsid.clone()); + let Ok(Some(nr)) = + routing_table.lookup_node_ref(TypedKey::new(rsd.crypto_kind, *h)) + else { + dead_ids.push(*rsid); break; }; hop_node_refs.push(nr); @@ -72,14 +74,14 @@ impl RouteSpecStoreContent { // also store in id by key table for (pk, _) in detail.iter_route_set() { - self.id_by_key.insert(*pk, id.clone()); + self.id_by_key.insert(*pk, id); } - self.details.insert(id.clone(), detail); + self.details.insert(id, detail); } pub fn remove_detail(&mut self, id: &RouteId) -> Option { let detail = self.details.remove(id)?; for (pk, _) in detail.iter_route_set() { - self.id_by_key.remove(&pk).unwrap(); + self.id_by_key.remove(pk).unwrap(); } Some(detail) } @@ -106,7 +108,7 @@ impl RouteSpecStoreContent { /// Resets publication status and statistics for when our node info changes /// Routes must be republished pub fn reset_details(&mut self) { - for (_k, v) in &mut self.details { + for v in self.details.values_mut() { // Must republish route now v.set_published(false); // Restart stats for routes so we test the route again diff --git a/veilid-core/src/routing_table/routing_domain_editor.rs b/veilid-core/src/routing_table/routing_domain_editor.rs index 385fe4b5..6e015fe4 100644 --- a/veilid-core/src/routing_table/routing_domain_editor.rs +++ b/veilid-core/src/routing_table/routing_domain_editor.rs @@ -258,11 +258,9 @@ impl RoutingDomainEditor { } } // Clear the routespecstore cache if our PublicInternet dial info has changed - if changed { - if self.routing_domain == RoutingDomain::PublicInternet { - let rss = self.routing_table.route_spec_store(); - rss.reset(); - } + if changed && self.routing_domain == RoutingDomain::PublicInternet { + let rss = self.routing_table.route_spec_store(); + rss.reset(); } } } diff --git a/veilid-core/src/routing_table/routing_domains.rs b/veilid-core/src/routing_table/routing_domains.rs index ba120369..32eb0eec 100644 --- a/veilid-core/src/routing_table/routing_domains.rs +++ b/veilid-core/src/routing_table/routing_domains.rs @@ -250,7 +250,7 @@ pub trait RoutingDomainDetail { peer_b: &PeerInfo, dial_info_filter: DialInfoFilter, sequencing: Sequencing, - dif_sort: Option core::cmp::Ordering>>, + dif_sort: Option>, ) -> ContactMethod; } @@ -276,9 +276,9 @@ fn first_filtered_dial_info_detail_between_nodes( to_node: &NodeInfo, dial_info_filter: &DialInfoFilter, sequencing: Sequencing, - dif_sort: Option core::cmp::Ordering>> + dif_sort: Option> ) -> Option { - let dial_info_filter = dial_info_filter.clone().filtered( + let dial_info_filter = (*dial_info_filter).filtered( &DialInfoFilter::all() .with_address_type_set(from_node.address_types()) .with_protocol_type_set(from_node.outbound_protocols()), @@ -289,7 +289,7 @@ fn first_filtered_dial_info_detail_between_nodes( // based on an external preference table, for example the one kept by // AddressFilter to deprioritize dialinfo that have recently failed to connect let (ordered, dial_info_filter) = dial_info_filter.with_sequencing(sequencing); - let sort: Option core::cmp::Ordering>> = if ordered { + let sort: Option> = if ordered { if let Some(dif_sort) = dif_sort { Some(Box::new(move |a, b| { let mut ord = dif_sort(a,b); @@ -301,12 +301,10 @@ fn first_filtered_dial_info_detail_between_nodes( } else { Some(Box::new(move |a,b| { DialInfoDetail::ordered_sequencing_sort(a,b) })) } + } else if let Some(dif_sort) = dif_sort { + Some(Box::new(move |a,b| { dif_sort(a,b) })) } else { - if let Some(dif_sort) = dif_sort { - Some(Box::new(move |a,b| { dif_sort(a,b) })) - } else { - None - } + None }; // If the filter is dead then we won't be able to connect @@ -336,7 +334,7 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { peer_b: &PeerInfo, dial_info_filter: DialInfoFilter, sequencing: Sequencing, - dif_sort: Option core::cmp::Ordering>>, + dif_sort: Option>, ) -> ContactMethod { // Get the nodeinfos for convenience let node_a = peer_a.signed_node_info().node_info(); @@ -401,8 +399,8 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { dif_sort.clone() ) { // Ensure we aren't on the same public IP address (no hairpin nat) - if reverse_did.dial_info.to_ip_addr() - != target_did.dial_info.to_ip_addr() + if reverse_did.dial_info.ip_addr() + != target_did.dial_info.ip_addr() { // Can we receive a direct reverse connection? if !reverse_did.class.requires_signal() { @@ -418,7 +416,6 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { // Does node B have a direct udp dialinfo node A can reach? let udp_dial_info_filter = dial_info_filter - .clone() .filtered(&DialInfoFilter::all().with_protocol_type(ProtocolType::UDP)); if let Some(target_udp_did) = first_filtered_dial_info_detail_between_nodes( node_a, @@ -436,8 +433,8 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { dif_sort.clone(), ) { // Ensure we aren't on the same public IP address (no hairpin nat) - if reverse_udp_did.dial_info.to_ip_addr() - != target_udp_did.dial_info.to_ip_addr() + if reverse_udp_did.dial_info.ip_addr() + != target_udp_did.dial_info.ip_addr() { // The target and ourselves have a udp dialinfo that they can reach return ContactMethod::SignalHolePunch( @@ -473,7 +470,7 @@ impl RoutingDomainDetail for PublicInternetRoutingDomainDetail { // Can we reach the inbound relay? if first_filtered_dial_info_detail_between_nodes( node_a, - &node_b_relay, + node_b_relay, &dial_info_filter, sequencing, dif_sort.clone() @@ -554,7 +551,7 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail { &mut self.common } fn can_contain_address(&self, address: Address) -> bool { - let ip = address.to_ip_addr(); + let ip = address.ip_addr(); for localnet in &self.local_networks { if ipaddr_in_network(ip, localnet.0, localnet.1) { return true; @@ -570,7 +567,7 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail { peer_b: &PeerInfo, dial_info_filter: DialInfoFilter, sequencing: Sequencing, - dif_sort: Option core::cmp::Ordering>>, + dif_sort: Option>, ) -> ContactMethod { // Scope the filter down to protocols node A can do outbound let dial_info_filter = dial_info_filter.filtered( @@ -584,7 +581,7 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail { // based on an external preference table, for example the one kept by // AddressFilter to deprioritize dialinfo that have recently failed to connect let (ordered, dial_info_filter) = dial_info_filter.with_sequencing(sequencing); - let sort: Option core::cmp::Ordering>> = if ordered { + let sort: Option> = if ordered { if let Some(dif_sort) = dif_sort { Some(Box::new(move |a, b| { let mut ord = dif_sort(a,b); @@ -596,12 +593,10 @@ impl RoutingDomainDetail for LocalNetworkRoutingDomainDetail { } else { Some(Box::new(move |a,b| { DialInfoDetail::ordered_sequencing_sort(a,b) })) } + } else if let Some(dif_sort) = dif_sort { + Some(Box::new(move |a,b| { dif_sort(a,b) })) } else { - if let Some(dif_sort) = dif_sort { - Some(Box::new(move |a,b| { dif_sort(a,b) })) - } else { - None - } + None }; // If the filter is dead then we won't be able to connect diff --git a/veilid-core/src/routing_table/routing_table_inner.rs b/veilid-core/src/routing_table/routing_table_inner.rs index c2cfc291..0ec1224a 100644 --- a/veilid-core/src/routing_table/routing_table_inner.rs +++ b/veilid-core/src/routing_table/routing_table_inner.rs @@ -227,7 +227,7 @@ impl RoutingTableInner { peer_b: &PeerInfo, dial_info_filter: DialInfoFilter, sequencing: Sequencing, - dif_sort: Option core::cmp::Ordering>>, + dif_sort: Option>, ) -> ContactMethod { self.with_routing_domain(routing_domain, |rdd| { rdd.get_contact_method(self, peer_a, peer_b, dial_info_filter, sequencing, dif_sort) @@ -543,17 +543,16 @@ impl RoutingTableInner { // Collect all entries that are 'needs_ping' and have some node info making them reachable somehow let mut node_refs = Vec::::with_capacity(self.bucket_entry_count()); self.with_entries(cur_ts, BucketEntryState::Unreliable, |rti, entry| { - if entry.with_inner(|e| { + let entry_needs_ping = |e: &BucketEntryInner| { // If this entry isn't in the routing domain we are checking, don't include it if !e.exists_in_routing_domain(rti, routing_domain) { return false; } // If we don't have node status for this node, then we should ping it to get some node status - if e.has_node_info(routing_domain.into()) { - if e.node_status(routing_domain).is_none() { - return true; - } + if e.has_node_info(routing_domain.into()) && e.node_status(routing_domain).is_none() + { + return true; } // If this entry needs a ping because this node hasn't seen our latest node info, then do it @@ -567,7 +566,9 @@ impl RoutingTableInner { } false - }) { + }; + + if entry.with_inner(entry_needs_ping) { node_refs.push(NodeRef::new( outer_self.clone(), entry, @@ -983,7 +984,7 @@ impl RoutingTableInner { match entry { None => has_valid_own_node_info, Some(entry) => entry.with_inner(|e| { - e.signed_node_info(routing_domain.into()) + e.signed_node_info(routing_domain) .map(|sni| sni.has_any_signature()) .unwrap_or(false) }), @@ -1080,11 +1081,7 @@ impl RoutingTableInner { move |_rti: &RoutingTableInner, v: Option>| { if let Some(entry) = &v { // always filter out dead nodes - if entry.with_inner(|e| e.state(cur_ts) == BucketEntryState::Dead) { - false - } else { - true - } + !entry.with_inner(|e| e.state(cur_ts) == BucketEntryState::Dead) } else { // always filter out self peer, as it is irrelevant to the 'fastest nodes' search false @@ -1100,7 +1097,7 @@ impl RoutingTableInner { // same nodes are always the same if let Some(a_entry) = a_entry { if let Some(b_entry) = b_entry { - if Arc::ptr_eq(&a_entry, &b_entry) { + if Arc::ptr_eq(a_entry, b_entry) { return core::cmp::Ordering::Equal; } } @@ -1151,9 +1148,7 @@ impl RoutingTableInner { }) }; - let out = - self.find_peers_with_sort_and_filter(node_count, cur_ts, filters, sort, transform); - out + self.find_peers_with_sort_and_filter(node_count, cur_ts, filters, sort, transform) } pub fn find_preferred_closest_nodes( @@ -1194,7 +1189,7 @@ impl RoutingTableInner { // same nodes are always the same if let Some(a_entry) = a_entry { if let Some(b_entry) = b_entry { - if Arc::ptr_eq(&a_entry, &b_entry) { + if Arc::ptr_eq(a_entry, b_entry) { return core::cmp::Ordering::Equal; } } diff --git a/veilid-core/src/routing_table/tasks/bootstrap.rs b/veilid-core/src/routing_table/tasks/bootstrap.rs index f48a6011..e69a0524 100644 --- a/veilid-core/src/routing_table/tasks/bootstrap.rs +++ b/veilid-core/src/routing_table/tasks/bootstrap.rs @@ -46,7 +46,7 @@ impl RoutingTable { // Envelope support let mut envelope_support = Vec::new(); - for ess in records[1].split(",") { + for ess in records[1].split(',') { let ess = ess.trim(); let es = match ess.parse::() { Ok(v) => v, @@ -64,9 +64,9 @@ impl RoutingTable { // Node Id let mut node_ids = TypedKeyGroup::new(); - for node_id_str in records[2].split(",") { + for node_id_str in records[2].split(',') { let node_id_str = node_id_str.trim(); - let node_id = match TypedKey::from_str(&node_id_str) { + let node_id = match TypedKey::from_str(node_id_str) { Ok(v) => v, Err(e) => { bail!( @@ -89,7 +89,7 @@ impl RoutingTable { // Resolve each record and store in node dial infos list let mut dial_info_details = Vec::new(); - for rec in records[4].split(",") { + for rec in records[4].split(',') { let rec = rec.trim(); let dial_infos = match DialInfo::try_vec_from_short(rec, hostname_str) { Ok(dis) => dis, @@ -321,13 +321,13 @@ impl RoutingTable { // See if we are specifying a direct dialinfo for bootstrap, if so use the direct mechanism let mut bootstrap_dialinfos = Vec::::new(); for b in &bootstrap { - if let Ok(bootstrap_di_vec) = DialInfo::try_vec_from_url(&b) { + if let Ok(bootstrap_di_vec) = DialInfo::try_vec_from_url(b) { for bootstrap_di in bootstrap_di_vec { bootstrap_dialinfos.push(bootstrap_di); } } } - if bootstrap_dialinfos.len() > 0 { + if !bootstrap_dialinfos.is_empty() { return self .direct_bootstrap_task_routine(stop_token, bootstrap_dialinfos) .await; diff --git a/veilid-core/src/routing_table/tasks/ping_validator.rs b/veilid-core/src/routing_table/tasks/ping_validator.rs index bfc2fb24..54119ae3 100644 --- a/veilid-core/src/routing_table/tasks/ping_validator.rs +++ b/veilid-core/src/routing_table/tasks/ping_validator.rs @@ -8,6 +8,9 @@ use futures_util::stream::{FuturesUnordered, StreamExt}; use futures_util::FutureExt; use stop_token::future::FutureExt as StopFutureExt; +type PingValidatorFuture = + SendPinBoxFuture>>, RPCError>>; + impl RoutingTable { // Ping each node in the routing table if they need to be pinged // to determine their reliability @@ -16,9 +19,7 @@ impl RoutingTable { &self, cur_ts: Timestamp, relay_nr: NodeRef, - unord: &mut FuturesUnordered< - SendPinBoxFuture>>, RPCError>>, - >, + unord: &mut FuturesUnordered, ) -> EyreResult<()> { let rpc = self.rpc_processor(); // Get our publicinternet dial info @@ -123,9 +124,7 @@ impl RoutingTable { async fn ping_validator_public_internet( &self, cur_ts: Timestamp, - unord: &mut FuturesUnordered< - SendPinBoxFuture>>, RPCError>>, - >, + unord: &mut FuturesUnordered, ) -> EyreResult<()> { let rpc = self.rpc_processor(); @@ -161,9 +160,7 @@ impl RoutingTable { async fn ping_validator_local_network( &self, cur_ts: Timestamp, - unord: &mut FuturesUnordered< - SendPinBoxFuture>>, RPCError>>, - >, + unord: &mut FuturesUnordered, ) -> EyreResult<()> { let rpc = self.rpc_processor(); 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 dcf9fee1..2bab77de 100644 --- a/veilid-core/src/routing_table/tasks/private_route_management.rs +++ b/veilid-core/src/routing_table/tasks/private_route_management.rs @@ -78,8 +78,11 @@ impl RoutingTable { // Save up to N unpublished routes and test them let background_safety_route_count = self.get_background_safety_route_count(); - for x in 0..(usize::min(background_safety_route_count, unpublished_routes.len())) { - must_test_routes.push(unpublished_routes[x].0); + for unpublished_route in unpublished_routes.iter().take(usize::min( + background_safety_route_count, + unpublished_routes.len(), + )) { + must_test_routes.push(unpublished_route.0); } // Kill off all but N unpublished routes rather than testing them @@ -225,9 +228,9 @@ impl RoutingTable { let remote_routes_needing_testing = rss.list_remote_routes(|k, v| { let stats = v.get_stats(); if stats.needs_testing(cur_ts) { - return Some(*k); + Some(*k) } else { - return None; + None } }); if !remote_routes_needing_testing.is_empty() { diff --git a/veilid-core/src/routing_table/tests/test_serialize_routing_table.rs b/veilid-core/src/routing_table/tests/test_serialize_routing_table.rs index bbf2ac30..bd81e8c2 100644 --- a/veilid-core/src/routing_table/tests/test_serialize_routing_table.rs +++ b/veilid-core/src/routing_table/tests/test_serialize_routing_table.rs @@ -30,8 +30,8 @@ pub async fn test_routingtable_buckets_round_trip() { for crypto in routing_table_keys { // The same keys are present in the original and copy RoutingTables. - let original_buckets = original_inner.buckets.get(&crypto).unwrap(); - let copy_buckets = copy_inner.buckets.get(&crypto).unwrap(); + let original_buckets = original_inner.buckets.get(crypto).unwrap(); + let copy_buckets = copy_inner.buckets.get(crypto).unwrap(); // Recurse into RoutingTable.inner.buckets for (left_buckets, right_buckets) in original_buckets.iter().zip(copy_buckets.iter()) { diff --git a/veilid-core/src/routing_table/types/dial_info_detail.rs b/veilid-core/src/routing_table/types/dial_info_detail.rs index 4e59b1af..dc7db216 100644 --- a/veilid-core/src/routing_table/types/dial_info_detail.rs +++ b/veilid-core/src/routing_table/types/dial_info_detail.rs @@ -13,6 +13,8 @@ impl MatchesDialInfoFilter for DialInfoDetail { } } +pub type DialInfoDetailSort = dyn Fn(&DialInfoDetail, &DialInfoDetail) -> core::cmp::Ordering; + impl DialInfoDetail { pub fn ordered_sequencing_sort(a: &DialInfoDetail, b: &DialInfoDetail) -> core::cmp::Ordering { let c = DialInfo::ordered_sequencing_sort(&a.dial_info, &b.dial_info); diff --git a/veilid-core/src/routing_table/types/direction.rs b/veilid-core/src/routing_table/types/direction.rs index 76187032..9702d3ca 100644 --- a/veilid-core/src/routing_table/types/direction.rs +++ b/veilid-core/src/routing_table/types/direction.rs @@ -2,7 +2,7 @@ use super::*; -#[allow(clippy::derive_hash_xor_eq)] +#[allow(clippy::derived_hash_with_manual_eq)] #[derive(Debug, PartialOrd, Ord, Hash, EnumSetType, Serialize, Deserialize)] #[enumset(repr = "u8")] pub enum Direction { diff --git a/veilid-core/src/routing_table/types/peer_info.rs b/veilid-core/src/routing_table/types/peer_info.rs index 60e83430..7a143790 100644 --- a/veilid-core/src/routing_table/types/peer_info.rs +++ b/veilid-core/src/routing_table/types/peer_info.rs @@ -8,7 +8,7 @@ pub struct PeerInfo { impl PeerInfo { pub fn new(node_ids: TypedKeyGroup, signed_node_info: SignedNodeInfo) -> Self { - assert!(node_ids.len() > 0 && node_ids.len() <= MAX_CRYPTO_KINDS); + assert!(!node_ids.is_empty() && node_ids.len() <= MAX_CRYPTO_KINDS); Self { node_ids, signed_node_info, diff --git a/veilid-core/src/routing_table/types/routing_domain.rs b/veilid-core/src/routing_table/types/routing_domain.rs index 8947fd82..75edaca8 100644 --- a/veilid-core/src/routing_table/types/routing_domain.rs +++ b/veilid-core/src/routing_table/types/routing_domain.rs @@ -3,7 +3,7 @@ use super::*; // Routing domain here is listed in order of preference, keep in order -#[allow(clippy::derive_hash_xor_eq)] +#[allow(clippy::derived_hash_with_manual_eq)] #[derive(Debug, Ord, PartialOrd, Hash, EnumSetType, Serialize, Deserialize)] #[enumset(repr = "u8")] pub enum RoutingDomain { diff --git a/veilid-core/src/routing_table/types/signed_direct_node_info.rs b/veilid-core/src/routing_table/types/signed_direct_node_info.rs index 521b1bbe..ddc8198b 100644 --- a/veilid-core/src/routing_table/types/signed_direct_node_info.rs +++ b/veilid-core/src/routing_table/types/signed_direct_node_info.rs @@ -29,7 +29,7 @@ impl SignedDirectNodeInfo { // Verify the signatures that we can let validated_node_ids = crypto.verify_signatures(node_ids, &node_info_bytes, &self.signatures)?; - if validated_node_ids.len() == 0 { + if validated_node_ids.is_empty() { apibail_generic!("no valid node ids in direct node info"); } diff --git a/veilid-core/src/routing_table/types/signed_node_info.rs b/veilid-core/src/routing_table/types/signed_node_info.rs index 801de847..d56597ea 100644 --- a/veilid-core/src/routing_table/types/signed_node_info.rs +++ b/veilid-core/src/routing_table/types/signed_node_info.rs @@ -33,8 +33,8 @@ impl SignedNodeInfo { } pub fn node_info(&self) -> &NodeInfo { match self { - SignedNodeInfo::Direct(d) => &d.node_info(), - SignedNodeInfo::Relayed(r) => &r.node_info(), + SignedNodeInfo::Direct(d) => d.node_info(), + SignedNodeInfo::Relayed(r) => r.node_info(), } } pub fn relay_ids(&self) -> TypedKeyGroup { diff --git a/veilid-core/src/routing_table/types/signed_relayed_node_info.rs b/veilid-core/src/routing_table/types/signed_relayed_node_info.rs index 462ebb31..bed33ba0 100644 --- a/veilid-core/src/routing_table/types/signed_relayed_node_info.rs +++ b/veilid-core/src/routing_table/types/signed_relayed_node_info.rs @@ -55,7 +55,7 @@ impl SignedRelayedNodeInfo { )?; let validated_node_ids = crypto.verify_signatures(node_ids, &node_info_bytes, &self.signatures)?; - if validated_node_ids.len() == 0 { + if validated_node_ids.is_empty() { apibail_generic!("no valid node ids in relayed node info"); } Ok(validated_node_ids) diff --git a/veilid-core/src/rpc_processor/coders/node_info.rs b/veilid-core/src/rpc_processor/coders/node_info.rs index 288d55d7..a2ad79b8 100644 --- a/veilid-core/src/rpc_processor/coders/node_info.rs +++ b/veilid-core/src/rpc_processor/coders/node_info.rs @@ -16,7 +16,7 @@ pub fn encode_node_info( .reborrow() .init_envelope_support(node_info.envelope_support().len() as u32); if let Some(s) = es_builder.as_slice() { - s.clone_from_slice(&node_info.envelope_support()); + s.clone_from_slice(node_info.envelope_support()); } let mut cs_builder = builder @@ -100,7 +100,7 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result MAX_ENVELOPE_VERSIONS { return Err(RPCError::protocol("too many envelope versions")); } - if envelope_support.len() == 0 { + if envelope_support.is_empty() { return Err(RPCError::protocol("no envelope versions")); } @@ -129,7 +129,7 @@ pub fn decode_node_info(reader: &veilid_capnp::node_info::Reader) -> Result MAX_CRYPTO_KINDS { return Err(RPCError::protocol("too many crypto kinds")); } - if crypto_support.len() == 0 { + if crypto_support.is_empty() { return Err(RPCError::protocol("no crypto kinds")); } diff --git a/veilid-core/src/rpc_processor/coders/operations/answer.rs b/veilid-core/src/rpc_processor/coders/operations/answer.rs index 356d1ba5..5415345f 100644 --- a/veilid-core/src/rpc_processor/coders/operations/answer.rs +++ b/veilid-core/src/rpc_processor/coders/operations/answer.rs @@ -31,22 +31,22 @@ impl RPCAnswer { #[derive(Debug, Clone)] pub enum RPCAnswerDetail { - StatusA(RPCOperationStatusA), - FindNodeA(RPCOperationFindNodeA), - AppCallA(RPCOperationAppCallA), - GetValueA(RPCOperationGetValueA), - SetValueA(RPCOperationSetValueA), - WatchValueA(RPCOperationWatchValueA), + StatusA(Box), + FindNodeA(Box), + AppCallA(Box), + GetValueA(Box), + SetValueA(Box), + WatchValueA(Box), #[cfg(feature = "unstable-blockstore")] - SupplyBlockA(RPCOperationSupplyBlockA), + SupplyBlockA(Box), #[cfg(feature = "unstable-blockstore")] - FindBlockA(RPCOperationFindBlockA), + FindBlockA(Box), #[cfg(feature = "unstable-tunnels")] - StartTunnelA(RPCOperationStartTunnelA), + StartTunnelA(Box), #[cfg(feature = "unstable-tunnels")] - CompleteTunnelA(RPCOperationCompleteTunnelA), + CompleteTunnelA(Box), #[cfg(feature = "unstable-tunnels")] - CancelTunnelA(RPCOperationCancelTunnelA), + CancelTunnelA(Box), } impl RPCAnswerDetail { @@ -98,62 +98,62 @@ impl RPCAnswerDetail { veilid_capnp::answer::detail::StatusA(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationStatusA::decode(&op_reader)?; - RPCAnswerDetail::StatusA(out) + RPCAnswerDetail::StatusA(Box::new(out)) } veilid_capnp::answer::detail::FindNodeA(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationFindNodeA::decode(&op_reader)?; - RPCAnswerDetail::FindNodeA(out) + RPCAnswerDetail::FindNodeA(Box::new(out)) } veilid_capnp::answer::detail::AppCallA(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationAppCallA::decode(&op_reader)?; - RPCAnswerDetail::AppCallA(out) + RPCAnswerDetail::AppCallA(Box::new(out)) } veilid_capnp::answer::detail::GetValueA(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationGetValueA::decode(&op_reader)?; - RPCAnswerDetail::GetValueA(out) + RPCAnswerDetail::GetValueA(Box::new(out)) } veilid_capnp::answer::detail::SetValueA(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationSetValueA::decode(&op_reader)?; - RPCAnswerDetail::SetValueA(out) + RPCAnswerDetail::SetValueA(Box::new(out)) } veilid_capnp::answer::detail::WatchValueA(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationWatchValueA::decode(&op_reader)?; - RPCAnswerDetail::WatchValueA(out) + RPCAnswerDetail::WatchValueA(Box::new(out)) } #[cfg(feature = "unstable-blockstore")] veilid_capnp::answer::detail::SupplyBlockA(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationSupplyBlockA::decode(&op_reader)?; - RPCAnswerDetail::SupplyBlockA(out) + RPCAnswerDetail::SupplyBlockA(Box::new(out)) } #[cfg(feature = "unstable-blockstore")] veilid_capnp::answer::detail::FindBlockA(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationFindBlockA::decode(&op_reader)?; - RPCAnswerDetail::FindBlockA(out) + RPCAnswerDetail::FindBlockA(Box::new(out)) } #[cfg(feature = "unstable-tunnels")] veilid_capnp::answer::detail::StartTunnelA(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationStartTunnelA::decode(&op_reader)?; - RPCAnswerDetail::StartTunnelA(out) + RPCAnswerDetail::StartTunnelA(Box::new(out)) } #[cfg(feature = "unstable-tunnels")] veilid_capnp::answer::detail::CompleteTunnelA(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationCompleteTunnelA::decode(&op_reader)?; - RPCAnswerDetail::CompleteTunnelA(out) + RPCAnswerDetail::CompleteTunnelA(Box::new(out)) } #[cfg(feature = "unstable-tunnels")] veilid_capnp::answer::detail::CancelTunnelA(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationCancelTunnelA::decode(&op_reader)?; - RPCAnswerDetail::CancelTunnelA(out) + RPCAnswerDetail::CancelTunnelA(Box::new(out)) } }; Ok(out) diff --git a/veilid-core/src/rpc_processor/coders/operations/operation.rs b/veilid-core/src/rpc_processor/coders/operations/operation.rs index 9118c2cb..446ef7ea 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation.rs @@ -2,9 +2,9 @@ use super::*; #[derive(Debug, Clone)] pub enum RPCOperationKind { - Question(RPCQuestion), - Statement(RPCStatement), - Answer(RPCAnswer), + Question(Box), + Statement(Box), + Answer(Box), } impl RPCOperationKind { @@ -30,17 +30,17 @@ impl RPCOperationKind { veilid_capnp::operation::kind::Which::Question(r) => { let q_reader = r.map_err(RPCError::protocol)?; let out = RPCQuestion::decode(&q_reader)?; - RPCOperationKind::Question(out) + RPCOperationKind::Question(Box::new(out)) } veilid_capnp::operation::kind::Which::Statement(r) => { let q_reader = r.map_err(RPCError::protocol)?; let out = RPCStatement::decode(&q_reader)?; - RPCOperationKind::Statement(out) + RPCOperationKind::Statement(Box::new(out)) } veilid_capnp::operation::kind::Which::Answer(r) => { let q_reader = r.map_err(RPCError::protocol)?; let out = RPCAnswer::decode(&q_reader)?; - RPCOperationKind::Answer(out) + RPCOperationKind::Answer(Box::new(out)) } }; @@ -73,7 +73,7 @@ impl RPCOperation { op_id: OperationId::new(get_random_u64()), opt_sender_peer_info: sender_peer_info.opt_sender_peer_info, target_node_info_ts: sender_peer_info.target_node_info_ts, - kind: RPCOperationKind::Question(question), + kind: RPCOperationKind::Question(Box::new(question)), } } pub fn new_statement(statement: RPCStatement, sender_peer_info: SenderPeerInfo) -> Self { @@ -81,7 +81,7 @@ impl RPCOperation { op_id: OperationId::new(get_random_u64()), opt_sender_peer_info: sender_peer_info.opt_sender_peer_info, target_node_info_ts: sender_peer_info.target_node_info_ts, - kind: RPCOperationKind::Statement(statement), + kind: RPCOperationKind::Statement(Box::new(statement)), } } @@ -94,7 +94,7 @@ impl RPCOperation { op_id: request.op_id, opt_sender_peer_info: sender_peer_info.opt_sender_peer_info, target_node_info_ts: sender_peer_info.target_node_info_ts, - kind: RPCOperationKind::Answer(answer), + kind: RPCOperationKind::Answer(Box::new(answer)), } } @@ -163,7 +163,7 @@ impl RPCOperation { builder.set_op_id(self.op_id.as_u64()); if let Some(sender_peer_info) = &self.opt_sender_peer_info { let mut pi_builder = builder.reborrow().init_sender_peer_info(); - encode_peer_info(&sender_peer_info, &mut pi_builder)?; + encode_peer_info(sender_peer_info, &mut pi_builder)?; } builder.set_target_node_info_ts(self.target_node_info_ts.as_u64()); let mut k_builder = builder.reborrow().init_kind(); diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_set_value.rs b/veilid-core/src/rpc_processor/coders/operations/operation_set_value.rs index 3ff5c71f..2e2133a1 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_set_value.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_set_value.rs @@ -122,7 +122,7 @@ impl RPCOperationSetValueA { value: Option, peers: Vec, ) -> Result { - if peers.len() as usize > MAX_SET_VALUE_A_PEERS_LEN { + if peers.len() > MAX_SET_VALUE_A_PEERS_LEN { return Err(RPCError::protocol( "encoded SetValueA peers length too long", )); diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_status.rs b/veilid-core/src/rpc_processor/coders/operations/operation_status.rs index 99cb5985..4a5e991d 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_status.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_status.rs @@ -36,7 +36,7 @@ impl RPCOperationStatusQ { ) -> Result<(), RPCError> { if let Some(ns) = &self.node_status { let mut ns_builder = builder.reborrow().init_node_status(); - encode_node_status(&ns, &mut ns_builder)?; + encode_node_status(ns, &mut ns_builder)?; } Ok(()) } @@ -98,11 +98,11 @@ impl RPCOperationStatusA { ) -> Result<(), RPCError> { if let Some(ns) = &self.node_status { let mut ns_builder = builder.reborrow().init_node_status(); - encode_node_status(&ns, &mut ns_builder)?; + encode_node_status(ns, &mut ns_builder)?; } if let Some(si) = &self.sender_info { let mut si_builder = builder.reborrow().init_sender_info(); - encode_sender_info(&si, &mut si_builder)?; + encode_sender_info(si, &mut si_builder)?; } Ok(()) } diff --git a/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs b/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs index 1ef425ab..77eea5a5 100644 --- a/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs +++ b/veilid-core/src/rpc_processor/coders/operations/operation_watch_value.rs @@ -22,7 +22,12 @@ impl RPCOperationWatchValueQ { watcher: PublicKey, signature: Signature, ) -> Result { - if subkeys.len() as usize > MAX_WATCH_VALUE_Q_SUBKEYS_LEN { + #[cfg(target_arch = "wasm32")] + let subkeys_len = subkeys.len() as usize; + #[cfg(not(target_arch = "wasm32"))] + let subkeys_len = subkeys.len(); + + if subkeys_len > MAX_WATCH_VALUE_Q_SUBKEYS_LEN { return Err(RPCError::protocol("WatchValueQ subkeys length too long")); } Ok(Self { @@ -37,8 +42,12 @@ impl RPCOperationWatchValueQ { // signature covers: key, subkeys, expiration, count, using watcher key fn make_signature_data(&self) -> Vec { - let mut sig_data = - Vec::with_capacity(PUBLIC_KEY_LENGTH + 4 + (self.subkeys.len() as usize * 8) + 8 + 4); + #[cfg(target_arch = "wasm32")] + let subkeys_len = self.subkeys.len() as usize; + #[cfg(not(target_arch = "wasm32"))] + let subkeys_len = self.subkeys.len(); + + let mut sig_data = Vec::with_capacity(PUBLIC_KEY_LENGTH + 4 + (subkeys_len * 8) + 8 + 4); sig_data.extend_from_slice(&self.key.kind.0); sig_data.extend_from_slice(&self.key.value.bytes); for sk in self.subkeys.ranges() { diff --git a/veilid-core/src/rpc_processor/coders/operations/question.rs b/veilid-core/src/rpc_processor/coders/operations/question.rs index 12591220..0ecd7b8b 100644 --- a/veilid-core/src/rpc_processor/coders/operations/question.rs +++ b/veilid-core/src/rpc_processor/coders/operations/question.rs @@ -43,22 +43,22 @@ impl RPCQuestion { #[derive(Debug, Clone)] pub enum RPCQuestionDetail { - StatusQ(RPCOperationStatusQ), - FindNodeQ(RPCOperationFindNodeQ), - AppCallQ(RPCOperationAppCallQ), - GetValueQ(RPCOperationGetValueQ), - SetValueQ(RPCOperationSetValueQ), - WatchValueQ(RPCOperationWatchValueQ), + StatusQ(Box), + FindNodeQ(Box), + AppCallQ(Box), + GetValueQ(Box), + SetValueQ(Box), + WatchValueQ(Box), #[cfg(feature = "unstable-blockstore")] - SupplyBlockQ(RPCOperationSupplyBlockQ), + SupplyBlockQ(Box), #[cfg(feature = "unstable-blockstore")] - FindBlockQ(RPCOperationFindBlockQ), + FindBlockQ(Box), #[cfg(feature = "unstable-tunnels")] - StartTunnelQ(RPCOperationStartTunnelQ), + StartTunnelQ(Box), #[cfg(feature = "unstable-tunnels")] - CompleteTunnelQ(RPCOperationCompleteTunnelQ), + CompleteTunnelQ(Box), #[cfg(feature = "unstable-tunnels")] - CancelTunnelQ(RPCOperationCancelTunnelQ), + CancelTunnelQ(Box), } impl RPCQuestionDetail { @@ -111,62 +111,62 @@ impl RPCQuestionDetail { veilid_capnp::question::detail::StatusQ(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationStatusQ::decode(&op_reader)?; - RPCQuestionDetail::StatusQ(out) + RPCQuestionDetail::StatusQ(Box::new(out)) } veilid_capnp::question::detail::FindNodeQ(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationFindNodeQ::decode(&op_reader)?; - RPCQuestionDetail::FindNodeQ(out) + RPCQuestionDetail::FindNodeQ(Box::new(out)) } veilid_capnp::question::detail::Which::AppCallQ(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationAppCallQ::decode(&op_reader)?; - RPCQuestionDetail::AppCallQ(out) + RPCQuestionDetail::AppCallQ(Box::new(out)) } veilid_capnp::question::detail::GetValueQ(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationGetValueQ::decode(&op_reader)?; - RPCQuestionDetail::GetValueQ(out) + RPCQuestionDetail::GetValueQ(Box::new(out)) } veilid_capnp::question::detail::SetValueQ(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationSetValueQ::decode(&op_reader)?; - RPCQuestionDetail::SetValueQ(out) + RPCQuestionDetail::SetValueQ(Box::new(out)) } veilid_capnp::question::detail::WatchValueQ(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationWatchValueQ::decode(&op_reader)?; - RPCQuestionDetail::WatchValueQ(out) + RPCQuestionDetail::WatchValueQ(Box::new(out)) } #[cfg(feature = "unstable-blockstore")] veilid_capnp::question::detail::SupplyBlockQ(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationSupplyBlockQ::decode(&op_reader)?; - RPCQuestionDetail::SupplyBlockQ(out) + RPCQuestionDetail::SupplyBlockQ(Box::new(out)) } #[cfg(feature = "unstable-blockstore")] veilid_capnp::question::detail::FindBlockQ(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationFindBlockQ::decode(&op_reader)?; - RPCQuestionDetail::FindBlockQ(out) + RPCQuestionDetail::FindBlockQ(Box::new(out)) } #[cfg(feature = "unstable-tunnels")] veilid_capnp::question::detail::StartTunnelQ(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationStartTunnelQ::decode(&op_reader)?; - RPCQuestionDetail::StartTunnelQ(out) + RPCQuestionDetail::StartTunnelQ(Box::new(out)) } #[cfg(feature = "unstable-tunnels")] veilid_capnp::question::detail::CompleteTunnelQ(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationCompleteTunnelQ::decode(&op_reader)?; - RPCQuestionDetail::CompleteTunnelQ(out) + RPCQuestionDetail::CompleteTunnelQ(Box::new(out)) } #[cfg(feature = "unstable-tunnels")] veilid_capnp::question::detail::CancelTunnelQ(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationCancelTunnelQ::decode(&op_reader)?; - RPCQuestionDetail::CancelTunnelQ(out) + RPCQuestionDetail::CancelTunnelQ(Box::new(out)) } }; Ok(out) diff --git a/veilid-core/src/rpc_processor/coders/operations/statement.rs b/veilid-core/src/rpc_processor/coders/operations/statement.rs index 08241e24..b71edc00 100644 --- a/veilid-core/src/rpc_processor/coders/operations/statement.rs +++ b/veilid-core/src/rpc_processor/coders/operations/statement.rs @@ -34,12 +34,12 @@ impl RPCStatement { #[derive(Debug, Clone)] pub enum RPCStatementDetail { - ValidateDialInfo(RPCOperationValidateDialInfo), - Route(RPCOperationRoute), - ValueChanged(RPCOperationValueChanged), - Signal(RPCOperationSignal), - ReturnReceipt(RPCOperationReturnReceipt), - AppMessage(RPCOperationAppMessage), + ValidateDialInfo(Box), + Route(Box), + ValueChanged(Box), + Signal(Box), + ReturnReceipt(Box), + AppMessage(Box), } impl RPCStatementDetail { @@ -71,32 +71,32 @@ impl RPCStatementDetail { veilid_capnp::statement::detail::ValidateDialInfo(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationValidateDialInfo::decode(&op_reader)?; - RPCStatementDetail::ValidateDialInfo(out) + RPCStatementDetail::ValidateDialInfo(Box::new(out)) } veilid_capnp::statement::detail::Route(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationRoute::decode(&op_reader)?; - RPCStatementDetail::Route(out) + RPCStatementDetail::Route(Box::new(out)) } veilid_capnp::statement::detail::ValueChanged(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationValueChanged::decode(&op_reader)?; - RPCStatementDetail::ValueChanged(out) + RPCStatementDetail::ValueChanged(Box::new(out)) } veilid_capnp::statement::detail::Signal(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationSignal::decode(&op_reader)?; - RPCStatementDetail::Signal(out) + RPCStatementDetail::Signal(Box::new(out)) } veilid_capnp::statement::detail::ReturnReceipt(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationReturnReceipt::decode(&op_reader)?; - RPCStatementDetail::ReturnReceipt(out) + RPCStatementDetail::ReturnReceipt(Box::new(out)) } veilid_capnp::statement::detail::AppMessage(r) => { let op_reader = r.map_err(RPCError::protocol)?; let out = RPCOperationAppMessage::decode(&op_reader)?; - RPCStatementDetail::AppMessage(out) + RPCStatementDetail::AppMessage(Box::new(out)) } }; Ok(out) diff --git a/veilid-core/src/rpc_processor/coders/peer_info.rs b/veilid-core/src/rpc_processor/coders/peer_info.rs index 8a23589a..b8707994 100644 --- a/veilid-core/src/rpc_processor/coders/peer_info.rs +++ b/veilid-core/src/rpc_processor/coders/peer_info.rs @@ -41,7 +41,7 @@ pub fn decode_peer_info(reader: &veilid_capnp::peer_info::Reader) -> Result { let mut ni_builder = node_builder.init_node_id(); - encode_key256(&ni, &mut ni_builder); + encode_key256(ni, &mut ni_builder); } RouteNode::PeerInfo(pi) => { let mut pi_builder = node_builder.init_peer_info(); - encode_peer_info(&pi, &mut pi_builder)?; + encode_peer_info(pi, &mut pi_builder)?; } } if let Some(rhd) = &route_hop.next_hop { @@ -76,10 +76,10 @@ pub fn decode_route_hop(reader: &veilid_capnp::route_hop::Reader) -> Result { let pi_reader = pi.map_err(RPCError::protocol)?; - RouteNode::PeerInfo( + RouteNode::PeerInfo(Box::new( decode_peer_info(&pi_reader) .map_err(RPCError::map_protocol("invalid peer info in route hop"))?, - ) + )) } }; @@ -134,7 +134,7 @@ pub fn decode_private_route( let hops = match reader.get_hops().which().map_err(RPCError::protocol)? { veilid_capnp::private_route::hops::Which::FirstHop(rh_reader) => { let rh_reader = rh_reader.map_err(RPCError::protocol)?; - PrivateRouteHops::FirstHop(decode_route_hop(&rh_reader)?) + PrivateRouteHops::FirstHop(Box::new(decode_route_hop(&rh_reader)?)) } veilid_capnp::private_route::hops::Which::Data(rhd_reader) => { let rhd_reader = rhd_reader.map_err(RPCError::protocol)?; diff --git a/veilid-core/src/rpc_processor/destination.rs b/veilid-core/src/rpc_processor/destination.rs index 1ba68fec..732334d5 100644 --- a/veilid-core/src/rpc_processor/destination.rs +++ b/veilid-core/src/rpc_processor/destination.rs @@ -181,10 +181,17 @@ impl RPCProcessor { // Sent directly but with a safety route, respond to private route let crypto_kind = target.best_node_id().kind; let Some(pr_key) = rss - .get_private_route_for_safety_spec(crypto_kind, safety_spec, &target.node_ids()) - .map_err(RPCError::internal)? else { - return Ok(NetworkResult::no_connection_other("no private route for response at this time")); - }; + .get_private_route_for_safety_spec( + crypto_kind, + safety_spec, + &target.node_ids(), + ) + .map_err(RPCError::internal)? + else { + return Ok(NetworkResult::no_connection_other( + "no private route for response at this time", + )); + }; // Get the assembled route for response let private_route = rss @@ -211,9 +218,12 @@ impl RPCProcessor { avoid_nodes.add_all(&target.node_ids()); let Some(pr_key) = rss .get_private_route_for_safety_spec(crypto_kind, safety_spec, &avoid_nodes) - .map_err(RPCError::internal)? else { - return Ok(NetworkResult::no_connection_other("no private route for response at this time")); - }; + .map_err(RPCError::internal)? + else { + return Ok(NetworkResult::no_connection_other( + "no private route for response at this time", + )); + }; // Get the assembled route for response let private_route = rss @@ -228,7 +238,9 @@ impl RPCProcessor { safety_selection, } => { let Some(avoid_node_id) = private_route.first_hop_node_id() else { - return Err(RPCError::internal("destination private route must have first hop")); + return Err(RPCError::internal( + "destination private route must have first hop", + )); }; let crypto_kind = private_route.public_key.kind; @@ -250,7 +262,7 @@ impl RPCProcessor { } else { let own_peer_info = routing_table.get_own_peer_info(RoutingDomain::PublicInternet); - RouteNode::PeerInfo(own_peer_info) + RouteNode::PeerInfo(Box::new(own_peer_info)) }; Ok(NetworkResult::value(RespondTo::PrivateRoute( @@ -271,10 +283,17 @@ impl RPCProcessor { } else { // Get the private route to respond to that matches the safety route spec we sent the request with let Some(pr_key) = rss - .get_private_route_for_safety_spec(crypto_kind, safety_spec, &[avoid_node_id]) - .map_err(RPCError::internal)? else { - return Ok(NetworkResult::no_connection_other("no private route for response at this time")); - }; + .get_private_route_for_safety_spec( + crypto_kind, + safety_spec, + &[avoid_node_id], + ) + .map_err(RPCError::internal)? + else { + return Ok(NetworkResult::no_connection_other( + "no private route for response at this time", + )); + }; pr_key }; @@ -342,9 +361,9 @@ impl RPCProcessor { if let Some(sender_noderef) = res { NetworkResult::value(Destination::relay(peer_noderef, sender_noderef)) } else { - return NetworkResult::invalid_message( + NetworkResult::invalid_message( "not responding to sender that has no node info", - ); + ) } } } @@ -352,9 +371,9 @@ impl RPCProcessor { match &request.header.detail { RPCMessageHeaderDetail::Direct(_) => { // If this was sent directly, we should only ever respond directly - return NetworkResult::invalid_message( + NetworkResult::invalid_message( "not responding to private route from direct question", - ); + ) } RPCMessageHeaderDetail::SafetyRouted(detail) => { // If this was sent via a safety route, but not received over our private route, don't respond with a safety route, @@ -368,7 +387,7 @@ impl RPCProcessor { // If this was received over our private route, it's okay to respond to a private route via our safety route NetworkResult::value(Destination::private_route( pr.clone(), - SafetySelection::Safe(detail.safety_spec.clone()), + SafetySelection::Safe(detail.safety_spec), )) } } diff --git a/veilid-core/src/rpc_processor/fanout_call.rs b/veilid-core/src/rpc_processor/fanout_call.rs index 06656731..5a7971a5 100644 --- a/veilid-core/src/rpc_processor/fanout_call.rs +++ b/veilid-core/src/rpc_processor/fanout_call.rs @@ -60,6 +60,7 @@ where C: Fn(NodeRef) -> F, D: Fn(&[NodeRef]) -> Option, { + #[allow(clippy::too_many_arguments)] pub fn new( routing_table: RoutingTable, node_id: TypedKey, @@ -103,7 +104,7 @@ where fn add_to_fanout_queue(self: Arc, new_nodes: &[NodeRef]) { let ctx = &mut *self.context.lock(); let this = self.clone(); - ctx.fanout_queue.add(&new_nodes, |current_nodes| { + ctx.fanout_queue.add(new_nodes, |current_nodes| { let mut current_nodes_vec = this .routing_table .sort_and_clean_closest_noderefs(this.node_id, current_nodes); @@ -180,8 +181,10 @@ where let entry = opt_entry.unwrap(); // Filter entries - entry.with(rti, |_rti, e| { - let Some(signed_node_info) = e.signed_node_info(RoutingDomain::PublicInternet) else { + entry.with(rti, |_rti, e| { + let Some(signed_node_info) = + e.signed_node_info(RoutingDomain::PublicInternet) + else { return false; }; // Ensure only things that are valid/signed in the PublicInternet domain are returned diff --git a/veilid-core/src/rpc_processor/mod.rs b/veilid-core/src/rpc_processor/mod.rs index fbcf2bf2..ba68fe27 100644 --- a/veilid-core/src/rpc_processor/mod.rs +++ b/veilid-core/src/rpc_processor/mod.rs @@ -447,7 +447,7 @@ impl RPCProcessor { capabilities: &[Capability], ) -> bool { let routing_table = self.routing_table(); - routing_table.signed_node_info_is_valid_in_routing_domain(routing_domain, &signed_node_info) + routing_table.signed_node_info_is_valid_in_routing_domain(routing_domain, signed_node_info) && signed_node_info.node_info().has_capabilities(capabilities) } @@ -684,7 +684,7 @@ impl RPCProcessor { let ssni_route = self.get_sender_peer_info(&Destination::direct(compiled_route.first_hop.clone())); let operation = RPCOperation::new_statement( - RPCStatement::new(RPCStatementDetail::Route(route_operation)), + RPCStatement::new(RPCStatementDetail::Route(Box::new(route_operation))), ssni_route, ); @@ -812,7 +812,7 @@ impl RPCProcessor { }; let private_route = PrivateRoute::new_stub( destination_node_ref.best_node_id(), - RouteNode::PeerInfo(peer_info), + RouteNode::PeerInfo(Box::new(peer_info)), ); // Wrap with safety route @@ -1021,6 +1021,7 @@ impl RPCProcessor { } /// Record answer received from node or route + #[allow(clippy::too_many_arguments)] fn record_answer_received( &self, send_ts: Timestamp, @@ -1079,7 +1080,7 @@ impl RPCProcessor { // If we sent to a private route without a safety route // We need to mark our own node info as having been seen so we can optimize sending it - if let Err(e) = rss.mark_remote_private_route_seen_our_node_info(&rpr_pubkey, recv_ts) { + if let Err(e) = rss.mark_remote_private_route_seen_our_node_info(rpr_pubkey, recv_ts) { log_rpc!(error "private route missing: {}", e); } @@ -1116,7 +1117,6 @@ impl RPCProcessor { RPCMessageHeaderDetail::Direct(_) => { if let Some(sender_nr) = msg.opt_sender_nr.clone() { sender_nr.stats_question_rcvd(recv_ts, bytes); - return; } } // Process messages that arrived with no private route (private route stub) diff --git a/veilid-core/src/rpc_processor/rpc_app_call.rs b/veilid-core/src/rpc_processor/rpc_app_call.rs index 984e8d9b..37c21deb 100644 --- a/veilid-core/src/rpc_processor/rpc_app_call.rs +++ b/veilid-core/src/rpc_processor/rpc_app_call.rs @@ -17,7 +17,7 @@ impl RPCProcessor { let app_call_q = RPCOperationAppCallQ::new(message)?; let question = RPCQuestion::new( network_result_try!(self.get_destination_respond_to(&dest)?), - RPCQuestionDetail::AppCallQ(app_call_q), + RPCQuestionDetail::AppCallQ(Box::new(app_call_q)), ); // Send the app call question @@ -94,9 +94,9 @@ impl RPCProcessor { // Pass the call up through the update callback let message_q = app_call_q.destructure(); - (self.unlocked_inner.update_callback)(VeilidUpdate::AppCall(VeilidAppCall::new( + (self.unlocked_inner.update_callback)(VeilidUpdate::AppCall(Box::new(VeilidAppCall::new( sender, message_q, op_id, - ))); + )))); // Wait for an app call answer to come back from the app let res = self @@ -117,8 +117,11 @@ impl RPCProcessor { let app_call_a = RPCOperationAppCallA::new(message_a)?; // Send status answer - self.answer(msg, RPCAnswer::new(RPCAnswerDetail::AppCallA(app_call_a))) - .await + self.answer( + msg, + RPCAnswer::new(RPCAnswerDetail::AppCallA(Box::new(app_call_a))), + ) + .await } /// Exposed to API for apps to return app call answers diff --git a/veilid-core/src/rpc_processor/rpc_app_message.rs b/veilid-core/src/rpc_processor/rpc_app_message.rs index d8798ff1..9f6a3d89 100644 --- a/veilid-core/src/rpc_processor/rpc_app_message.rs +++ b/veilid-core/src/rpc_processor/rpc_app_message.rs @@ -13,7 +13,7 @@ impl RPCProcessor { message: Vec, ) -> Result, RPCError> { let app_message = RPCOperationAppMessage::new(message)?; - let statement = RPCStatement::new(RPCStatementDetail::AppMessage(app_message)); + let statement = RPCStatement::new(RPCStatementDetail::AppMessage(Box::new(app_message))); // Send the app message request self.statement(dest, statement).await @@ -58,8 +58,8 @@ impl RPCProcessor { // Pass the message up through the update callback let message = app_message.destructure(); - (self.unlocked_inner.update_callback)(VeilidUpdate::AppMessage(VeilidAppMessage::new( - sender, message, + (self.unlocked_inner.update_callback)(VeilidUpdate::AppMessage(Box::new( + VeilidAppMessage::new(sender, message), ))); Ok(NetworkResult::value(())) diff --git a/veilid-core/src/rpc_processor/rpc_error.rs b/veilid-core/src/rpc_processor/rpc_error.rs index 89c9eb11..f20f7102 100644 --- a/veilid-core/src/rpc_processor/rpc_error.rs +++ b/veilid-core/src/rpc_processor/rpc_error.rs @@ -38,7 +38,7 @@ impl RPCError { move |x| Self::Internal(format!("{}: {}", message.to_string(), x.to_string())) } pub fn else_internal(message: M) -> impl FnOnce() -> Self { - move || Self::Internal(format!("{}", message.to_string())) + move || Self::Internal(message.to_string()) } pub fn network(x: X) -> Self { Self::Network(x.to_string()) diff --git a/veilid-core/src/rpc_processor/rpc_find_node.rs b/veilid-core/src/rpc_processor/rpc_find_node.rs index d205081f..088b38b9 100644 --- a/veilid-core/src/rpc_processor/rpc_find_node.rs +++ b/veilid-core/src/rpc_processor/rpc_find_node.rs @@ -30,8 +30,9 @@ impl RPCProcessor { )); } - let find_node_q_detail = - RPCQuestionDetail::FindNodeQ(RPCOperationFindNodeQ::new(node_id, capabilities.clone())); + let find_node_q_detail = RPCQuestionDetail::FindNodeQ(Box::new( + 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, @@ -111,7 +112,10 @@ impl RPCProcessor { let find_node_a = RPCOperationFindNodeA::new(closest_nodes)?; // Send FindNode answer - self.answer(msg, RPCAnswer::new(RPCAnswerDetail::FindNodeA(find_node_a))) - .await + self.answer( + msg, + RPCAnswer::new(RPCAnswerDetail::FindNodeA(Box::new(find_node_a))), + ) + .await } } diff --git a/veilid-core/src/rpc_processor/rpc_get_value.rs b/veilid-core/src/rpc_processor/rpc_get_value.rs index b6137796..89c1aeff 100644 --- a/veilid-core/src/rpc_processor/rpc_get_value.rs +++ b/veilid-core/src/rpc_processor/rpc_get_value.rs @@ -65,7 +65,7 @@ impl RPCProcessor { let get_value_q = RPCOperationGetValueQ::new(key, subkey, last_descriptor.is_none()); let question = RPCQuestion::new( network_result_try!(self.get_destination_respond_to(&dest)?), - RPCQuestionDetail::GetValueQ(get_value_q), + RPCQuestionDetail::GetValueQ(Box::new(get_value_q)), ); let question_context = QuestionContext::GetValue(ValidateGetValueContext { @@ -268,7 +268,7 @@ impl RPCProcessor { )?; // Send GetValue answer - self.answer(msg, RPCAnswer::new(RPCAnswerDetail::GetValueA(get_value_a))) + self.answer(msg, RPCAnswer::new(RPCAnswerDetail::GetValueA(Box::new(get_value_a)))) .await } } diff --git a/veilid-core/src/rpc_processor/rpc_return_receipt.rs b/veilid-core/src/rpc_processor/rpc_return_receipt.rs index 66397c72..60c13cb2 100644 --- a/veilid-core/src/rpc_processor/rpc_return_receipt.rs +++ b/veilid-core/src/rpc_processor/rpc_return_receipt.rs @@ -15,7 +15,8 @@ impl RPCProcessor { let receipt = receipt.as_ref().to_vec(); let return_receipt = RPCOperationReturnReceipt::new(receipt)?; - let statement = RPCStatement::new(RPCStatementDetail::ReturnReceipt(return_receipt)); + let statement = + RPCStatement::new(RPCStatementDetail::ReturnReceipt(Box::new(return_receipt))); // Send the return_receipt request network_result_try!(self.statement(dest, statement).await?); diff --git a/veilid-core/src/rpc_processor/rpc_route.rs b/veilid-core/src/rpc_processor/rpc_route.rs index aa055de1..117a26a3 100644 --- a/veilid-core/src/rpc_processor/rpc_route.rs +++ b/veilid-core/src/rpc_processor/rpc_route.rs @@ -1,7 +1,10 @@ use super::*; impl RPCProcessor { - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip_all, err))] + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip_all, err) + )] async fn process_route_safety_route_hop( &self, routed_operation: RoutedOperation, @@ -26,7 +29,10 @@ impl RPCProcessor { } // Get next hop node ref - let Some(mut next_hop_nr) = route_hop.node.node_ref(self.routing_table.clone(), safety_route.public_key.kind) else { + let Some(mut next_hop_nr) = route_hop + .node + .node_ref(self.routing_table.clone(), safety_route.public_key.kind) + else { return Err(RPCError::network(format!( "could not get route node hop ref: {}", route_hop.node.describe(safety_route.public_key.kind) @@ -45,14 +51,18 @@ impl RPCProcessor { }, routed_operation, ); - let next_hop_route_stmt = RPCStatement::new(RPCStatementDetail::Route(next_hop_route)); + let next_hop_route_stmt = + RPCStatement::new(RPCStatementDetail::Route(Box::new(next_hop_route))); // Send the next route statement self.statement(Destination::direct(next_hop_nr), next_hop_route_stmt) .await } - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip_all, err))] + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip_all, err) + )] async fn process_route_private_route_hop( &self, routed_operation: RoutedOperation, @@ -68,7 +78,9 @@ impl RPCProcessor { } // Get next hop node ref - let Some(mut next_hop_nr) = next_route_node.node_ref(self.routing_table.clone(), safety_route_public_key.kind) else { + let Some(mut next_hop_nr) = + next_route_node.node_ref(self.routing_table.clone(), safety_route_public_key.kind) + else { return Err(RPCError::network(format!( "could not get route node hop ref: {}", next_route_node.describe(safety_route_public_key.kind) @@ -87,7 +99,8 @@ impl RPCProcessor { }, routed_operation, ); - let next_hop_route_stmt = RPCStatement::new(RPCStatementDetail::Route(next_hop_route)); + let next_hop_route_stmt = + RPCStatement::new(RPCStatementDetail::Route(Box::new(next_hop_route))); // Send the next route statement self.statement(Destination::direct(next_hop_nr), next_hop_route_stmt) @@ -99,7 +112,10 @@ impl RPCProcessor { /// Note: it is important that we never respond with a safety route to questions that come /// in without a private route. Giving away a safety route when the node id is known is /// a privacy violation! - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip_all, err))] + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip_all, err) + )] fn process_safety_routed_operation( &self, detail: RPCMessageHeaderDetailDirect, @@ -111,7 +127,9 @@ impl RPCProcessor { // xxx: punish nodes that send messages that fail to decrypt eventually? How to do this for safety routes? let node_id_secret = self.routing_table.node_id_secret_key(remote_sr_pubkey.kind); let Ok(dh_secret) = vcrypto.cached_dh(&remote_sr_pubkey.value, &node_id_secret) else { - return Ok(NetworkResult::invalid_message("dh failed for remote safety route for safety routed operation")); + return Ok(NetworkResult::invalid_message( + "dh failed for remote safety route for safety routed operation", + )); }; let body = match vcrypto.decrypt_aead( routed_operation.data(), @@ -141,7 +159,10 @@ impl RPCProcessor { } /// Process a routed operation that came in over both a safety route and a private route - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip_all, err))] + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip_all, err) + )] fn process_private_routed_operation( &self, detail: RPCMessageHeaderDetailDirect, @@ -152,49 +173,54 @@ impl RPCProcessor { ) -> Result, RPCError> { // Get sender id of the peer with the crypto kind of the route let Some(sender_id) = detail.peer_noderef.node_ids().get(pr_pubkey.kind) else { - return Ok(NetworkResult::invalid_message("route node doesnt have a required crypto kind for routed operation")); + return Ok(NetworkResult::invalid_message( + "route node doesnt have a required crypto kind for routed operation", + )); }; // Look up the private route and ensure it's one in our spec store // Ensure the route is validated, and construct a return safetyspec that matches the inbound preferences let rss = self.routing_table.route_spec_store(); let preferred_route = rss.get_route_id_for_key(&pr_pubkey.value); - let Some((secret_key, safety_spec)) = rss - .with_signature_validated_route( - &pr_pubkey, - routed_operation.signatures(), - routed_operation.data(), - sender_id.value, - |rssd, rsd| { - ( - rsd.secret_key, - SafetySpec { - preferred_route, - hop_count: rssd.hop_count(), - stability: rssd.get_stability(), - sequencing: routed_operation.sequencing(), - }, - ) - } - ) - else { - return Ok(NetworkResult::invalid_message("signatures did not validate for private route")); - }; + let Some((secret_key, safety_spec)) = rss.with_signature_validated_route( + &pr_pubkey, + routed_operation.signatures(), + routed_operation.data(), + sender_id.value, + |rssd, rsd| { + ( + rsd.secret_key, + SafetySpec { + preferred_route, + hop_count: rssd.hop_count(), + stability: rssd.get_stability(), + sequencing: routed_operation.sequencing(), + }, + ) + }, + ) else { + return Ok(NetworkResult::invalid_message( + "signatures did not validate for private route", + )); + }; // Now that things are valid, decrypt the routed operation with DEC(nonce, DH(the SR's public key, the PR's (or node's) secret) // xxx: punish nodes that send messages that fail to decrypt eventually. How to do this for private routes? let Ok(dh_secret) = vcrypto.cached_dh(&remote_sr_pubkey.value, &secret_key) else { - return Ok(NetworkResult::invalid_message("dh failed for remote safety route for private routed operation")); + return Ok(NetworkResult::invalid_message( + "dh failed for remote safety route for private routed operation", + )); + }; + let Ok(body) = vcrypto.decrypt_aead( + routed_operation.data(), + routed_operation.nonce(), + &dh_secret, + None, + ) else { + return Ok(NetworkResult::invalid_message( + "decryption of routed operation failed", + )); }; - let Ok(body) = vcrypto - .decrypt_aead( - routed_operation.data(), - routed_operation.nonce(), - &dh_secret, - None, - ) else { - return Ok(NetworkResult::invalid_message("decryption of routed operation failed")); - }; // Pass message to RPC system self.enqueue_private_routed_message( @@ -209,7 +235,10 @@ impl RPCProcessor { Ok(NetworkResult::value(())) } - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip_all, err))] + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip_all, err) + )] fn process_routed_operation( &self, detail: RPCMessageHeaderDetailDirect, @@ -239,7 +268,10 @@ impl RPCProcessor { ) } } - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip_all, err))] + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip_all, err) + )] pub(crate) async fn process_private_route_first_hop( &self, mut routed_operation: RoutedOperation, @@ -247,14 +279,18 @@ impl RPCProcessor { mut private_route: PrivateRoute, ) -> Result, RPCError> { let Some(pr_first_hop) = private_route.pop_first_hop() else { - return Ok(NetworkResult::invalid_message("switching from safety route to private route requires first hop")); + return Ok(NetworkResult::invalid_message( + "switching from safety route to private route requires first hop", + )); }; // Check for loopback test where private route is the same as safety route if sr_pubkey == private_route.public_key { // If so, we're going to turn this thing right around without transiting the network let PrivateRouteHops::Data(route_hop_data) = private_route.hops else { - return Ok(NetworkResult::invalid_message("Loopback test requires hops")); + return Ok(NetworkResult::invalid_message( + "Loopback test requires hops", + )); }; // Decrypt route hop data @@ -282,7 +318,7 @@ impl RPCProcessor { hop_count: private_route.hop_count - 1, hops: route_hop .next_hop - .map(|rhd| PrivateRouteHops::Data(rhd)) + .map(PrivateRouteHops::Data) .unwrap_or(PrivateRouteHops::Empty), }, ) @@ -342,9 +378,11 @@ impl RPCProcessor { .map_err(RPCError::protocol)?; decode_route_hop(&rh_reader)? }; - + // Validate the RouteHop - route_hop.validate(self.crypto.clone()).map_err(RPCError::protocol)?; + route_hop + .validate(self.crypto.clone()) + .map_err(RPCError::protocol)?; // Sign the operation if this is not our last hop // as the last hop is already signed by the envelope @@ -360,7 +398,10 @@ impl RPCProcessor { Ok(NetworkResult::value(route_hop)) } - #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self), ret, err))] + #[cfg_attr( + feature = "verbose-tracing", + instrument(level = "trace", skip(self), ret, err) + )] pub(crate) async fn process_route( &self, msg: RPCMessage, @@ -374,16 +415,10 @@ impl RPCProcessor { } let opi = routing_table.get_own_peer_info(msg.header.routing_domain()); - if !opi - .signed_node_info() - .node_info() - .has_capability(CAP_ROUTE) - { - return Ok(NetworkResult::service_unavailable( - "route is not available", - )); + if !opi.signed_node_info().node_info().has_capability(CAP_ROUTE) { + return Ok(NetworkResult::service_unavailable("route is not available")); } - + // Get header detail, must be direct and not inside a route itself let detail = match msg.header.detail { RPCMessageHeaderDetail::Direct(detail) => detail, @@ -395,7 +430,7 @@ impl RPCProcessor { }; // Get the statement - let (_,_,_,kind) = msg.operation.destructure(); + let (_, _, _, kind) = msg.operation.destructure(); let route = match kind { RPCOperationKind::Statement(s) => match s.destructure() { RPCStatementDetail::Route(s) => s, @@ -419,19 +454,22 @@ impl RPCProcessor { SafetyRouteHops::Data(ref route_hop_data) => { // Decrypt the blob with DEC(nonce, DH(the SR's public key, this hop's secret) let node_id_secret = self.routing_table.node_id_secret_key(crypto_kind); - let Ok(dh_secret) = vcrypto - .cached_dh(&safety_route.public_key.value, &node_id_secret) else { - return Ok(NetworkResult::invalid_message("dh failed for safety route hop")); + let Ok(dh_secret) = + vcrypto.cached_dh(&safety_route.public_key.value, &node_id_secret) + else { + return Ok(NetworkResult::invalid_message( + "dh failed for safety route hop", + )); }; - let Ok(mut dec_blob_data) = vcrypto - .decrypt_aead( - &route_hop_data.blob, - &route_hop_data.nonce, - &dh_secret, - None, - ) - else { - return Ok(NetworkResult::invalid_message("failed to decrypt route hop data for safety route hop")); + let Ok(mut dec_blob_data) = vcrypto.decrypt_aead( + &route_hop_data.blob, + &route_hop_data.nonce, + &dh_secret, + None, + ) else { + return Ok(NetworkResult::invalid_message( + "failed to decrypt route hop data for safety route hop", + )); }; // See if this is last hop in safety route, if so, we're decoding a PrivateRoute not a RouteHop @@ -440,26 +478,35 @@ impl RPCProcessor { }; let Ok(dec_blob_reader) = RPCMessageData::new(dec_blob_data).get_reader() else { - return Ok(NetworkResult::invalid_message("Failed to decode RPCMessageData from blob")); + return Ok(NetworkResult::invalid_message( + "Failed to decode RPCMessageData from blob", + )); }; // Decode the blob appropriately if dec_blob_tag == 1 { // PrivateRoute let private_route = { - let Ok(pr_reader) = dec_blob_reader - .get_root::() else { - return Ok(NetworkResult::invalid_message("failed to get private route reader for blob")); + let Ok(pr_reader) = + dec_blob_reader.get_root::() + else { + return Ok(NetworkResult::invalid_message( + "failed to get private route reader for blob", + )); }; let Ok(private_route) = decode_private_route(&pr_reader) else { - return Ok(NetworkResult::invalid_message("failed to decode private route")); + return Ok(NetworkResult::invalid_message( + "failed to decode private route", + )); }; private_route }; - + // Validate the private route - if let Err(_) = private_route.validate(self.crypto.clone()) { - return Ok(NetworkResult::invalid_message("failed to validate private route")); + if private_route.validate(self.crypto.clone()).is_err() { + return Ok(NetworkResult::invalid_message( + "failed to validate private route", + )); } // Switching from full safety route to private route first hop @@ -474,19 +521,26 @@ impl RPCProcessor { } else if dec_blob_tag == 0 { // RouteHop let route_hop = { - let Ok(rh_reader) = dec_blob_reader - .get_root::() else { - return Ok(NetworkResult::invalid_message("failed to get route hop reader for blob")); + let Ok(rh_reader) = + dec_blob_reader.get_root::() + else { + return Ok(NetworkResult::invalid_message( + "failed to get route hop reader for blob", + )); }; let Ok(route_hop) = decode_route_hop(&rh_reader) else { - return Ok(NetworkResult::invalid_message("failed to decode route hop")); + return Ok(NetworkResult::invalid_message( + "failed to decode route hop", + )); }; route_hop }; // Validate the route hop - if let Err(_) = route_hop.validate(self.crypto.clone()) { - return Ok(NetworkResult::invalid_message("failed to validate route hop")); + if route_hop.validate(self.crypto.clone()).is_err() { + return Ok(NetworkResult::invalid_message( + "failed to validate route hop", + )); } // Continue the full safety route with another hop @@ -543,7 +597,7 @@ impl RPCProcessor { hop_count: private_route.hop_count - 1, hops: route_hop .next_hop - .map(|rhd| PrivateRouteHops::Data(rhd)) + .map(PrivateRouteHops::Data) .unwrap_or(PrivateRouteHops::Empty), }, ) diff --git a/veilid-core/src/rpc_processor/rpc_set_value.rs b/veilid-core/src/rpc_processor/rpc_set_value.rs index 48f7563b..b69a8ff5 100644 --- a/veilid-core/src/rpc_processor/rpc_set_value.rs +++ b/veilid-core/src/rpc_processor/rpc_set_value.rs @@ -80,7 +80,7 @@ impl RPCProcessor { ); let question = RPCQuestion::new( network_result_try!(self.get_destination_respond_to(&dest)?), - RPCQuestionDetail::SetValueQ(set_value_q), + RPCQuestionDetail::SetValueQ(Box::new(set_value_q)), ); let question_context = QuestionContext::SetValue(ValidateSetValueContext { descriptor, @@ -292,7 +292,7 @@ impl RPCProcessor { let set_value_a = RPCOperationSetValueA::new(set, new_value, closer_to_key_peers)?; // Send SetValue answer - self.answer(msg, RPCAnswer::new(RPCAnswerDetail::SetValueA(set_value_a))) + self.answer(msg, RPCAnswer::new(RPCAnswerDetail::SetValueA(Box::new(set_value_a)))) .await } } diff --git a/veilid-core/src/rpc_processor/rpc_signal.rs b/veilid-core/src/rpc_processor/rpc_signal.rs index 3226ae00..df041188 100644 --- a/veilid-core/src/rpc_processor/rpc_signal.rs +++ b/veilid-core/src/rpc_processor/rpc_signal.rs @@ -26,7 +26,7 @@ impl RPCProcessor { } let signal = RPCOperationSignal::new(signal_info); - let statement = RPCStatement::new(RPCStatementDetail::Signal(signal)); + let statement = RPCStatement::new(RPCStatementDetail::Signal(Box::new(signal))); // Send the signal request self.statement(dest, statement).await diff --git a/veilid-core/src/rpc_processor/rpc_status.rs b/veilid-core/src/rpc_processor/rpc_status.rs index cf4401ab..0160cced 100644 --- a/veilid-core/src/rpc_processor/rpc_status.rs +++ b/veilid-core/src/rpc_processor/rpc_status.rs @@ -101,7 +101,7 @@ impl RPCProcessor { let status_q = RPCOperationStatusQ::new(node_status); let question = RPCQuestion::new( network_result_try!(self.get_destination_respond_to(&dest)?), - RPCQuestionDetail::StatusQ(status_q), + RPCQuestionDetail::StatusQ(Box::new(status_q)), ); let debug_string = format!("Status => {}", dest); @@ -249,7 +249,10 @@ impl RPCProcessor { let status_a = RPCOperationStatusA::new(node_status, sender_info); // Send status answer - self.answer(msg, RPCAnswer::new(RPCAnswerDetail::StatusA(status_a))) - .await + self.answer( + msg, + RPCAnswer::new(RPCAnswerDetail::StatusA(Box::new(status_a))), + ) + .await } } 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 bd9823fd..2a97d88a 100644 --- a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs +++ b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs @@ -21,7 +21,9 @@ impl RPCProcessor { .map_err(RPCError::internal)?; let validate_dial_info = RPCOperationValidateDialInfo::new(dial_info, receipt, redirect)?; - let statement = RPCStatement::new(RPCStatementDetail::ValidateDialInfo(validate_dial_info)); + let statement = RPCStatement::new(RPCStatementDetail::ValidateDialInfo(Box::new( + validate_dial_info, + ))); // Send the validate_dial_info request // This can only be sent directly, as relays can not validate dial info @@ -153,8 +155,9 @@ impl RPCProcessor { // Make a copy of the request, without the redirect flag let validate_dial_info = RPCOperationValidateDialInfo::new(dial_info.clone(), receipt.clone(), false)?; - let statement = - RPCStatement::new(RPCStatementDetail::ValidateDialInfo(validate_dial_info)); + let statement = RPCStatement::new(RPCStatementDetail::ValidateDialInfo(Box::new( + validate_dial_info, + ))); // Send the validate_dial_info request // This can only be sent directly, as relays can not validate dial info diff --git a/veilid-core/src/storage_manager/debug.rs b/veilid-core/src/storage_manager/debug.rs index ba2c43c3..8a8328b5 100644 --- a/veilid-core/src/storage_manager/debug.rs +++ b/veilid-core/src/storage_manager/debug.rs @@ -23,7 +23,7 @@ impl StorageManager { let reclaimed = local_record_store .reclaim_space(reclaim.unwrap_or(usize::MAX)) .await; - return format!("Local records purged: reclaimed {} bytes", reclaimed); + format!("Local records purged: reclaimed {} bytes", reclaimed) } pub(crate) async fn purge_remote_records(&self, reclaim: Option) -> String { let mut inner = self.inner.lock().await; @@ -33,7 +33,7 @@ impl StorageManager { let reclaimed = remote_record_store .reclaim_space(reclaim.unwrap_or(usize::MAX)) .await; - return format!("Remote records purged: reclaimed {} bytes", reclaimed); + format!("Remote records purged: reclaimed {} bytes", reclaimed) } pub(crate) async fn debug_local_record_subkey_info( &self, diff --git a/veilid-core/src/storage_manager/limited_size.rs b/veilid-core/src/storage_manager/limited_size.rs index 22a4a93b..d7b8f9cf 100644 --- a/veilid-core/src/storage_manager/limited_size.rs +++ b/veilid-core/src/storage_manager/limited_size.rs @@ -103,10 +103,10 @@ impl LimitedSize { if let Some(uv) = self.uncommitted_value.take() { log_stor!(debug "Rollback ({}): {} (drop {})", self.description, self.value, uv); } - return self.value; + self.value } pub fn get(&self) -> T { - return self.value; + self.value } } diff --git a/veilid-core/src/storage_manager/mod.rs b/veilid-core/src/storage_manager/mod.rs index fe80c4ad..0b28b71b 100644 --- a/veilid-core/src/storage_manager/mod.rs +++ b/veilid-core/src/storage_manager/mod.rs @@ -133,9 +133,7 @@ impl StorageManager { } fn online_writes_ready_inner(inner: &StorageManagerInner) -> Option { - if let Some(rpc_processor) = { - inner.rpc_processor.clone() - } { + if let Some(rpc_processor) = { inner.rpc_processor.clone() } { if let Some(network_class) = rpc_processor .routing_table() .get_network_class(RoutingDomain::PublicInternet) @@ -158,12 +156,12 @@ impl StorageManager { async fn online_writes_ready(&self) -> EyreResult> { let inner = self.lock().await?; - return Ok(Self::online_writes_ready_inner(&*inner)); + Ok(Self::online_writes_ready_inner(&inner)) } async fn has_offline_subkey_writes(&self) -> EyreResult { let inner = self.lock().await?; - Ok(inner.offline_subkey_writes.len() != 0) + Ok(!inner.offline_subkey_writes.is_empty()) } /// Create a local record from scratch with a new owner key, open it, and return the opened descriptor @@ -394,7 +392,7 @@ impl StorageManager { // Make new subkey data let value_data = if let Some(last_signed_value_data) = last_subkey_result.value { - if last_signed_value_data.value_data().data() == &data + if last_signed_value_data.value_data().data() == data && last_signed_value_data.value_data().writer() == &writer.key { // Data and writer is the same, nothing is changing, @@ -433,13 +431,17 @@ impl StorageManager { log_stor!(debug "Writing subkey offline: {}:{} len={}", key, subkey, signed_value_data.value_data().data().len() ); // Add to offline writes to flush - inner.offline_subkey_writes.entry(key) - .and_modify(|x| { x.subkeys.insert(subkey); } ) - .or_insert(OfflineSubkeyWrite{ - safety_selection, - subkeys: ValueSubkeyRangeSet::single(subkey) + inner + .offline_subkey_writes + .entry(key) + .and_modify(|x| { + x.subkeys.insert(subkey); + }) + .or_insert(OfflineSubkeyWrite { + safety_selection, + subkeys: ValueSubkeyRangeSet::single(subkey), }); - return Ok(None) + return Ok(None); }; // Drop the lock for network access @@ -474,21 +476,21 @@ impl StorageManager { pub async fn watch_values( &self, - key: TypedKey, - subkeys: ValueSubkeyRangeSet, - expiration: Timestamp, - count: u32, + _key: TypedKey, + _subkeys: ValueSubkeyRangeSet, + _expiration: Timestamp, + _count: u32, ) -> VeilidAPIResult { - let inner = self.lock().await?; + let _inner = self.lock().await?; unimplemented!(); } pub async fn cancel_watch_values( &self, - key: TypedKey, - subkeys: ValueSubkeyRangeSet, + _key: TypedKey, + _subkeys: ValueSubkeyRangeSet, ) -> VeilidAPIResult { - let inner = self.lock().await?; + let _inner = self.lock().await?; unimplemented!(); } } diff --git a/veilid-core/src/storage_manager/record_store.rs b/veilid-core/src/storage_manager/record_store.rs index 8a52d93a..401d855d 100644 --- a/veilid-core/src/storage_manager/record_store.rs +++ b/veilid-core/src/storage_manager/record_store.rs @@ -65,7 +65,7 @@ where D: fmt::Debug + Clone + Serialize + for<'d> Deserialize<'d>, { pub fn new(table_store: TableStore, name: &str, limits: RecordStoreLimits) -> Self { - let subkey_cache_size = limits.subkey_cache_size as usize; + let subkey_cache_size = limits.subkey_cache_size; let limit_subkey_cache_total_size = limits .max_subkey_cache_memory_mb .map(|mb| mb * 1_048_576usize); @@ -104,7 +104,7 @@ where .await?; let subkey_table = self .table_store - .open(&&format!("{}_subkeys", self.name), 1) + .open(&format!("{}_subkeys", self.name), 1) .await?; // Pull record index from table into a vector to ensure we sort them @@ -126,7 +126,7 @@ where self.total_storage_space .add((mem::size_of::() + ri.1.total_size()) as u64) .unwrap(); - if let Err(_) = self.total_storage_space.commit() { + if self.total_storage_space.commit().is_err() { // Revert the total storage space because the commit failed self.total_storage_space.rollback(); @@ -449,11 +449,15 @@ where ) -> VeilidAPIResult> { // Get record from index let Some((subkey_count, has_subkey, opt_descriptor)) = self.with_record(key, |record| { - (record.subkey_count(), record.stored_subkeys().contains(subkey), if want_descriptor { - Some(record.descriptor().clone()) - } else { - None - }) + ( + record.subkey_count(), + record.stored_subkeys().contains(subkey), + if want_descriptor { + Some(record.descriptor().clone()) + } else { + None + }, + ) }) else { // Record not available return Ok(None); @@ -492,19 +496,20 @@ where let Some(record_data) = subkey_table .load_json::(0, &stk.bytes()) .await - .map_err(VeilidAPIError::internal)? else { - apibail_internal!("failed to get subkey that was stored"); - }; + .map_err(VeilidAPIError::internal)? + else { + apibail_internal!("failed to get subkey that was stored"); + }; let out = record_data.signed_value_data().clone(); // Add to cache, do nothing with lru out self.add_to_subkey_cache(stk, record_data); - return Ok(Some(SubkeyResult { + Ok(Some(SubkeyResult { value: Some(out), descriptor: opt_descriptor, - })); + })) } pub(crate) async fn peek_subkey( @@ -515,11 +520,15 @@ where ) -> VeilidAPIResult> { // record from index let Some((subkey_count, has_subkey, opt_descriptor)) = self.peek_record(key, |record| { - (record.subkey_count(), record.stored_subkeys().contains(subkey), if want_descriptor { - Some(record.descriptor().clone()) - } else { - None - }) + ( + record.subkey_count(), + record.stored_subkeys().contains(subkey), + if want_descriptor { + Some(record.descriptor().clone()) + } else { + None + }, + ) }) else { // Record not available return Ok(None); @@ -558,16 +567,17 @@ where let Some(record_data) = subkey_table .load_json::(0, &stk.bytes()) .await - .map_err(VeilidAPIError::internal)? else { - apibail_internal!("failed to peek subkey that was stored"); - }; + .map_err(VeilidAPIError::internal)? + else { + apibail_internal!("failed to peek subkey that was stored"); + }; let out = record_data.signed_value_data().clone(); - return Ok(Some(SubkeyResult { + Ok(Some(SubkeyResult { value: Some(out), descriptor: opt_descriptor, - })); + })) } pub async fn set_subkey( @@ -692,7 +702,7 @@ where for (rik, rec) in &self.record_index { out += &format!( " {} age={} len={} subkeys={}\n", - rik.key.to_string(), + rik.key, debug_duration(get_timestamp() - rec.last_touched().as_u64()), rec.record_data_size(), rec.stored_subkeys(), @@ -706,11 +716,11 @@ where out += &format!("Total Storage Space: {}\n", self.total_storage_space.get()); out += &format!("Dead Records: {}\n", self.dead_records.len()); for dr in &self.dead_records { - out += &format!(" {}\n", dr.key.key.to_string()); + out += &format!(" {}\n", dr.key.key); } out += &format!("Changed Records: {}\n", self.changed_records.len()); for cr in &self.changed_records { - out += &format!(" {}\n", cr.key.to_string()); + out += &format!(" {}\n", cr.key); } out diff --git a/veilid-core/src/storage_manager/storage_manager_inner.rs b/veilid-core/src/storage_manager/storage_manager_inner.rs index 9a8815da..8eb8c0b6 100644 --- a/veilid-core/src/storage_manager/storage_manager_inner.rs +++ b/veilid-core/src/storage_manager/storage_manager_inner.rs @@ -37,9 +37,7 @@ fn local_limits_from_config(config: VeilidConfig) -> RecordStoreLimits { max_subkey_size: MAX_SUBKEY_SIZE, max_record_total_size: MAX_RECORD_DATA_SIZE, max_records: None, - max_subkey_cache_memory_mb: Some( - c.network.dht.local_max_subkey_cache_memory_mb as usize, - ), + max_subkey_cache_memory_mb: Some(c.network.dht.local_max_subkey_cache_memory_mb as usize), max_storage_space_mb: None, } } @@ -51,9 +49,7 @@ fn remote_limits_from_config(config: VeilidConfig) -> RecordStoreLimits { max_subkey_size: MAX_SUBKEY_SIZE, max_record_total_size: MAX_RECORD_DATA_SIZE, max_records: Some(c.network.dht.remote_max_records as usize), - max_subkey_cache_memory_mb: Some( - c.network.dht.remote_max_subkey_cache_memory_mb as usize, - ), + max_subkey_cache_memory_mb: Some(c.network.dht.remote_max_subkey_cache_memory_mb as usize), max_storage_space_mb: Some(c.network.dht.remote_max_storage_space_mb as usize), } } @@ -74,8 +70,8 @@ impl StorageManagerInner { } pub async fn init(&mut self, outer_self: StorageManager) -> EyreResult<()> { - - let metadata_db = self.unlocked_inner + let metadata_db = self + .unlocked_inner .table_store .open(STORAGE_MANAGER_METADATA, 1) .await?; @@ -120,7 +116,6 @@ impl StorageManagerInner { } pub async fn terminate(&mut self) { - // Stop ticker let tick_future = self.tick_future.take(); if let Some(f) = tick_future { @@ -130,19 +125,19 @@ impl StorageManagerInner { // Final flush on record stores if let Some(mut local_record_store) = self.local_record_store.take() { if let Err(e) = local_record_store.tick().await { - log_stor!(error "termination local record store tick failed: {}", e); + log_stor!(error "termination local record store tick failed: {}", e); } } if let Some(mut remote_record_store) = self.remote_record_store.take() { if let Err(e) = remote_record_store.tick().await { - log_stor!(error "termination remote record store tick failed: {}", e); + log_stor!(error "termination remote record store tick failed: {}", e); } } // Save metadata if self.metadata_db.is_some() { if let Err(e) = self.save_metadata().await { - log_stor!(error "termination metadata save failed: {}", e); + log_stor!(error "termination metadata save failed: {}", e); } self.metadata_db = None; } @@ -152,7 +147,7 @@ impl StorageManagerInner { self.initialized = false; } - async fn save_metadata(&mut self) -> EyreResult<()>{ + async fn save_metadata(&mut self) -> EyreResult<()> { if let Some(metadata_db) = &self.metadata_db { let tx = metadata_db.transact(); tx.store_json(0, OFFLINE_SUBKEY_WRITES, &self.offline_subkey_writes)?; @@ -163,7 +158,8 @@ impl StorageManagerInner { async fn load_metadata(&mut self) -> EyreResult<()> { if let Some(metadata_db) = &self.metadata_db { - self.offline_subkey_writes = match metadata_db.load_json(0, OFFLINE_SUBKEY_WRITES).await { + self.offline_subkey_writes = match metadata_db.load_json(0, OFFLINE_SUBKEY_WRITES).await + { Ok(v) => v.unwrap_or_default(), Err(_) => { if let Err(e) = metadata_db.delete(0, OFFLINE_SUBKEY_WRITES).await { @@ -218,13 +214,16 @@ impl StorageManagerInner { Ok((dht_key, owner)) } - async fn move_remote_record_to_local(&mut self, key: TypedKey, safety_selection: SafetySelection) -> VeilidAPIResult> - { + async fn move_remote_record_to_local( + &mut self, + key: TypedKey, + safety_selection: SafetySelection, + ) -> VeilidAPIResult> { // Get local record store let Some(local_record_store) = self.local_record_store.as_mut() else { apibail_not_initialized!(); }; - + // Get remote record store let Some(remote_record_store) = self.remote_record_store.as_mut() else { apibail_not_initialized!(); @@ -241,31 +240,36 @@ impl StorageManagerInner { // Make local record let cur_ts = get_aligned_timestamp(); - let local_record = Record::new(cur_ts, remote_record.descriptor().clone(), LocalRecordDetail { - safety_selection - })?; + let local_record = Record::new( + cur_ts, + remote_record.descriptor().clone(), + LocalRecordDetail { safety_selection }, + )?; local_record_store.new_record(key, local_record).await?; // Move copy subkey data from remote to local store for subkey in remote_record.stored_subkeys().iter() { - let Some(subkey_result) = remote_record_store.get_subkey(key, subkey, false).await? else { + let Some(subkey_result) = remote_record_store.get_subkey(key, subkey, false).await? + else { // Subkey was missing - warn!("Subkey was missing: {} #{}",key, subkey); + warn!("Subkey was missing: {} #{}", key, subkey); continue; }; let Some(subkey_data) = subkey_result.value else { // Subkey was missing - warn!("Subkey data was missing: {} #{}",key, subkey); + warn!("Subkey data was missing: {} #{}", key, subkey); continue; }; - local_record_store.set_subkey(key, subkey, subkey_data).await?; + local_record_store + .set_subkey(key, subkey, subkey_data) + .await?; } // Delete remote record from store remote_record_store.delete_record(key).await?; // Return record information as transferred to local record - Ok(Some((remote_record.owner().clone(), remote_record.schema()))) + Ok(Some((*remote_record.owner(), remote_record.schema()))) } pub async fn open_existing_record( @@ -292,14 +296,17 @@ impl StorageManagerInner { r.detail_mut().safety_selection = safety_selection; // Return record details - (r.owner().clone(), r.schema()) + (*r.owner(), r.schema()) }; - let (owner, schema) = match local_record_store.with_record_mut(key, cb){ + let (owner, schema) = match local_record_store.with_record_mut(key, cb) { Some(v) => v, None => { // If we don't have a local record yet, check to see if we have a remote record // if so, migrate it to a local record - let Some(v) = self.move_remote_record_to_local(key, safety_selection).await? else { + let Some(v) = self + .move_remote_record_to_local(key, safety_selection) + .await? + else { // No remote record either return Ok(None); }; @@ -348,7 +355,7 @@ impl StorageManagerInner { apibail_generic!("no descriptor"); }; // Get owner - let owner = signed_value_descriptor.owner().clone(); + let owner = *signed_value_descriptor.owner(); // If the writer we chose is also the owner, we have the owner secret // Otherwise this is just another subkey writer @@ -410,7 +417,10 @@ impl StorageManagerInner { let Some(local_record_store) = self.local_record_store.as_mut() else { apibail_not_initialized!(); }; - if let Some(subkey_result) = local_record_store.get_subkey(key, subkey, want_descriptor).await? { + if let Some(subkey_result) = local_record_store + .get_subkey(key, subkey, want_descriptor) + .await? + { return Ok(subkey_result); } @@ -428,7 +438,7 @@ impl StorageManagerInner { ) -> VeilidAPIResult<()> { // See if it's in the local record store let Some(local_record_store) = self.local_record_store.as_mut() else { - apibail_not_initialized!(); + apibail_not_initialized!(); }; // Write subkey to local store @@ -449,7 +459,10 @@ impl StorageManagerInner { let Some(remote_record_store) = self.remote_record_store.as_mut() else { apibail_not_initialized!(); }; - if let Some(subkey_result) = remote_record_store.get_subkey(key, subkey, want_descriptor).await? { + if let Some(subkey_result) = remote_record_store + .get_subkey(key, subkey, want_descriptor) + .await? + { return Ok(subkey_result); } @@ -472,12 +485,15 @@ impl StorageManagerInner { }; // See if we have a remote record already or not - if remote_record_store.with_record(key, |_|{}).is_none() { + if remote_record_store.with_record(key, |_| {}).is_none() { // record didn't exist, make it let cur_ts = get_aligned_timestamp(); - let remote_record_detail = RemoteRecordDetail { }; - let record = - Record::::new(cur_ts, signed_value_descriptor, remote_record_detail)?; + let remote_record_detail = RemoteRecordDetail {}; + let record = Record::::new( + cur_ts, + signed_value_descriptor, + remote_record_detail, + )?; remote_record_store.new_record(key, record).await? }; diff --git a/veilid-core/src/storage_manager/types/signed_value_data.rs b/veilid-core/src/storage_manager/types/signed_value_data.rs index 5724f0c3..8a8fb0d5 100644 --- a/veilid-core/src/storage_manager/types/signed_value_data.rs +++ b/veilid-core/src/storage_manager/types/signed_value_data.rs @@ -24,7 +24,7 @@ impl SignedValueData { ) -> VeilidAPIResult<()> { let node_info_bytes = Self::make_signature_bytes(&self.value_data, owner, subkey)?; // validate signature - vcrypto.verify(&self.value_data.writer(), &node_info_bytes, &self.signature) + vcrypto.verify(self.value_data.writer(), &node_info_bytes, &self.signature) } pub fn make_signature( @@ -37,7 +37,7 @@ impl SignedValueData { let node_info_bytes = Self::make_signature_bytes(&value_data, owner, subkey)?; // create signature - let signature = vcrypto.sign(&value_data.writer(), &writer_secret, &node_info_bytes)?; + let signature = vcrypto.sign(value_data.writer(), &writer_secret, &node_info_bytes)?; Ok(Self { value_data, signature, diff --git a/veilid-core/src/table_store/mod.rs b/veilid-core/src/table_store/mod.rs index 1e2f27e8..8d623ea8 100644 --- a/veilid-core/src/table_store/mod.rs +++ b/veilid-core/src/table_store/mod.rs @@ -1,9 +1,7 @@ use super::*; mod table_db; -mod table_store; pub use table_db::*; -pub use table_store::*; pub mod tests; @@ -15,3 +13,582 @@ use wasm::*; mod native; #[cfg(not(target_arch = "wasm32"))] use native::*; + +use keyvaluedb::*; + +const ALL_TABLE_NAMES: &[u8] = b"all_table_names"; + +struct TableStoreInner { + opened: BTreeMap>, + encryption_key: Option, + all_table_names: HashMap, + all_tables_db: Option, + crypto: Option, +} + +/// Veilid Table Storage +/// Database for storing key value pairs persistently and securely across runs +#[derive(Clone)] +pub struct TableStore { + config: VeilidConfig, + protected_store: ProtectedStore, + table_store_driver: TableStoreDriver, + inner: Arc>, // Sync mutex here because TableDB drops can happen at any time + async_lock: Arc>, // Async mutex for operations +} + +impl TableStore { + fn new_inner() -> TableStoreInner { + TableStoreInner { + opened: BTreeMap::new(), + encryption_key: None, + all_table_names: HashMap::new(), + all_tables_db: None, + crypto: None, + } + } + pub(crate) fn new(config: VeilidConfig, protected_store: ProtectedStore) -> Self { + let inner = Self::new_inner(); + let table_store_driver = TableStoreDriver::new(config.clone()); + + Self { + config, + protected_store, + inner: Arc::new(Mutex::new(inner)), + table_store_driver, + async_lock: Arc::new(AsyncMutex::new(())), + } + } + + pub(crate) fn set_crypto(&self, crypto: Crypto) { + let mut inner = self.inner.lock(); + inner.crypto = Some(crypto); + } + + // Flush internal control state (must not use crypto) + async fn flush(&self) { + let (all_table_names_value, all_tables_db) = { + let inner = self.inner.lock(); + let all_table_names_value = serialize_json_bytes(&inner.all_table_names); + (all_table_names_value, inner.all_tables_db.clone().unwrap()) + }; + let mut dbt = DBTransaction::new(); + dbt.put(0, ALL_TABLE_NAMES, &all_table_names_value); + if let Err(e) = all_tables_db.write(dbt).await { + error!("failed to write all tables db: {}", e); + } + } + + // Internal naming support + // Adds rename capability and ensures names of tables are totally unique and valid + + fn namespaced_name(&self, table: &str) -> VeilidAPIResult { + if !table + .chars() + .all(|c| char::is_alphanumeric(c) || c == '_' || c == '-') + { + apibail_invalid_argument!("table name is invalid", "table", table); + } + let c = self.config.get(); + let namespace = c.namespace.clone(); + Ok(if namespace.is_empty() { + table.to_string() + } else { + format!("_ns_{}_{}", namespace, table) + }) + } + + async fn name_get_or_create(&self, table: &str) -> VeilidAPIResult { + let name = self.namespaced_name(table)?; + + let mut inner = self.inner.lock(); + // Do we have this name yet? + if let Some(real_name) = inner.all_table_names.get(&name) { + return Ok(real_name.clone()); + } + + // If not, make a new low level name mapping + let mut real_name_bytes = [0u8; 32]; + random_bytes(&mut real_name_bytes); + let real_name = data_encoding::BASE64URL_NOPAD.encode(&real_name_bytes); + + if inner + .all_table_names + .insert(name.to_owned(), real_name.clone()) + .is_some() + { + panic!("should not have had some value"); + }; + + Ok(real_name) + } + + async fn name_delete(&self, table: &str) -> VeilidAPIResult> { + let name = self.namespaced_name(table)?; + let mut inner = self.inner.lock(); + let real_name = inner.all_table_names.remove(&name); + Ok(real_name) + } + + async fn name_get(&self, table: &str) -> VeilidAPIResult> { + let name = self.namespaced_name(table)?; + let inner = self.inner.lock(); + let real_name = inner.all_table_names.get(&name).cloned(); + Ok(real_name) + } + + async fn name_rename(&self, old_table: &str, new_table: &str) -> VeilidAPIResult<()> { + let old_name = self.namespaced_name(old_table)?; + let new_name = self.namespaced_name(new_table)?; + + let mut inner = self.inner.lock(); + // Ensure new name doesn't exist + if inner.all_table_names.contains_key(&new_name) { + return Err(VeilidAPIError::generic("new table already exists")); + } + // Do we have this name yet? + let Some(real_name) = inner.all_table_names.remove(&old_name) else { + return Err(VeilidAPIError::generic("table does not exist")); + }; + // Insert with new name + inner.all_table_names.insert(new_name.to_owned(), real_name); + + Ok(()) + } + + /// Delete all known tables + async fn delete_all(&self) { + // Get all tables + let real_names = { + let mut inner = self.inner.lock(); + let real_names = inner + .all_table_names + .values() + .cloned() + .collect::>(); + inner.all_table_names.clear(); + real_names + }; + + // Delete all tables + for table_name in real_names { + if let Err(e) = self.table_store_driver.delete(&table_name).await { + error!("error deleting table: {}", e); + } + } + self.flush().await; + } + + pub(crate) fn maybe_unprotect_device_encryption_key( + &self, + dek_bytes: &[u8], + device_encryption_key_password: &str, + ) -> EyreResult { + // Ensure the key is at least as long as necessary if unencrypted + if dek_bytes.len() < (4 + SHARED_SECRET_LENGTH) { + bail!("device encryption key is not valid"); + } + + // Get cryptosystem + let kind = FourCC::try_from(&dek_bytes[0..4]).unwrap(); + let crypto = self.inner.lock().crypto.as_ref().unwrap().clone(); + let Some(vcrypto) = crypto.get(kind) else { + bail!("unsupported cryptosystem"); + }; + + if !device_encryption_key_password.is_empty() { + if dek_bytes.len() + != (4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead() + NONCE_LENGTH) + { + bail!("password protected device encryption key is not valid"); + } + let protected_key = &dek_bytes[4..(4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead())]; + let nonce = + Nonce::try_from(&dek_bytes[(4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead())..]) + .wrap_err("invalid nonce")?; + + let shared_secret = vcrypto + .derive_shared_secret(device_encryption_key_password.as_bytes(), &nonce.bytes) + .wrap_err("failed to derive shared secret")?; + let unprotected_key = vcrypto + .decrypt_aead(protected_key, &nonce, &shared_secret, None) + .wrap_err("failed to decrypt device encryption key")?; + return Ok(TypedSharedSecret::new( + kind, + SharedSecret::try_from(unprotected_key.as_slice()) + .wrap_err("invalid shared secret")?, + )); + } + + if dek_bytes.len() != (4 + SHARED_SECRET_LENGTH) { + bail!("password protected device encryption key is not valid"); + } + + Ok(TypedSharedSecret::new( + kind, + SharedSecret::try_from(&dek_bytes[4..])?, + )) + } + + pub(crate) fn maybe_protect_device_encryption_key( + &self, + dek: TypedSharedSecret, + device_encryption_key_password: &str, + ) -> EyreResult> { + // Check if we are to protect the key + if device_encryption_key_password.is_empty() { + debug!("no dek password"); + // Return the unprotected key bytes + let mut out = Vec::with_capacity(4 + SHARED_SECRET_LENGTH); + out.extend_from_slice(&dek.kind.0); + out.extend_from_slice(&dek.value.bytes); + return Ok(out); + } + + // Get cryptosystem + let crypto = self.inner.lock().crypto.as_ref().unwrap().clone(); + let Some(vcrypto) = crypto.get(dek.kind) else { + bail!("unsupported cryptosystem"); + }; + + let nonce = vcrypto.random_nonce(); + let shared_secret = vcrypto + .derive_shared_secret(device_encryption_key_password.as_bytes(), &nonce.bytes) + .wrap_err("failed to derive shared secret")?; + let mut protected_key = vcrypto + .encrypt_aead(&dek.value.bytes, &nonce, &shared_secret, None) + .wrap_err("failed to decrypt device encryption key")?; + let mut out = + Vec::with_capacity(4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead() + NONCE_LENGTH); + out.extend_from_slice(&dek.kind.0); + out.append(&mut protected_key); + out.extend_from_slice(&nonce.bytes); + assert!(out.len() == 4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead() + NONCE_LENGTH); + Ok(out) + } + + async fn load_device_encryption_key(&self) -> EyreResult> { + let dek_bytes: Option> = self + .protected_store + .load_user_secret("device_encryption_key") + .await?; + let Some(dek_bytes) = dek_bytes else { + debug!("no device encryption key"); + return Ok(None); + }; + + // Get device encryption key protection password if we have it + let device_encryption_key_password = { + let c = self.config.get(); + c.protected_store.device_encryption_key_password.clone() + }; + + Ok(Some(self.maybe_unprotect_device_encryption_key( + &dek_bytes, + &device_encryption_key_password, + )?)) + } + async fn save_device_encryption_key( + &self, + device_encryption_key: Option, + ) -> EyreResult<()> { + let Some(device_encryption_key) = device_encryption_key else { + // Remove the device encryption key + let existed = self + .protected_store + .remove_user_secret("device_encryption_key") + .await?; + debug!("removed device encryption key. existed: {}", existed); + return Ok(()); + }; + + // Get new device encryption key protection password if we are changing it + let new_device_encryption_key_password = { + let c = self.config.get(); + c.protected_store.new_device_encryption_key_password.clone() + }; + let device_encryption_key_password = + if let Some(new_device_encryption_key_password) = new_device_encryption_key_password { + // Change password + debug!("changing dek password"); + self.config + .with_mut(|c| { + c.protected_store.device_encryption_key_password = + new_device_encryption_key_password.clone(); + Ok(new_device_encryption_key_password) + }) + .unwrap() + } else { + // Get device encryption key protection password if we have it + debug!("saving with existing dek password"); + let c = self.config.get(); + c.protected_store.device_encryption_key_password.clone() + }; + + let dek_bytes = self.maybe_protect_device_encryption_key( + device_encryption_key, + &device_encryption_key_password, + )?; + + // Save the new device encryption key + let existed = self + .protected_store + .save_user_secret("device_encryption_key", &dek_bytes) + .await?; + debug!("saving device encryption key. existed: {}", existed); + Ok(()) + } + + pub(crate) async fn init(&self) -> EyreResult<()> { + let _async_guard = self.async_lock.lock().await; + + // Get device encryption key from protected store + let mut device_encryption_key = self.load_device_encryption_key().await?; + let mut device_encryption_key_changed = false; + if let Some(device_encryption_key) = device_encryption_key { + // If encryption in current use is not the best encryption, then run table migration + let best_kind = best_crypto_kind(); + if device_encryption_key.kind != best_kind { + // XXX: Run migration. See issue #209 + } + } else { + // If we don't have an encryption key yet, then make one with the best cryptography and save it + let best_kind = best_crypto_kind(); + let mut shared_secret = SharedSecret::default(); + random_bytes(&mut shared_secret.bytes); + + device_encryption_key = Some(TypedSharedSecret::new(best_kind, shared_secret)); + device_encryption_key_changed = true; + } + + // Check for password change + let changing_password = self + .config + .get() + .protected_store + .new_device_encryption_key_password + .is_some(); + + // Save encryption key if it has changed or if the protecting password wants to change + if device_encryption_key_changed || changing_password { + self.save_device_encryption_key(device_encryption_key) + .await?; + } + + // Deserialize all table names + let all_tables_db = self + .table_store_driver + .open("__veilid_all_tables", 1) + .await + .wrap_err("failed to create all tables table")?; + match all_tables_db.get(0, ALL_TABLE_NAMES).await { + Ok(Some(v)) => match deserialize_json_bytes::>(&v) { + Ok(all_table_names) => { + let mut inner = self.inner.lock(); + inner.all_table_names = all_table_names; + } + Err(e) => { + error!("could not deserialize __veilid_all_tables: {}", e); + } + }, + Ok(None) => { + // No table names yet, that's okay + trace!("__veilid_all_tables is empty"); + } + Err(e) => { + error!("could not get __veilid_all_tables: {}", e); + } + }; + + { + let mut inner = self.inner.lock(); + inner.encryption_key = device_encryption_key; + inner.all_tables_db = Some(all_tables_db); + } + + let do_delete = { + let c = self.config.get(); + c.table_store.delete + }; + + if do_delete { + self.delete_all().await; + } + + Ok(()) + } + + pub(crate) async fn terminate(&self) { + let _async_guard = self.async_lock.lock().await; + + self.flush().await; + + let mut inner = self.inner.lock(); + if !inner.opened.is_empty() { + panic!( + "all open databases should have been closed: {:?}", + inner.opened + ); + } + inner.all_tables_db = None; + inner.all_table_names.clear(); + inner.encryption_key = None; + } + + pub(crate) fn on_table_db_drop(&self, table: String) { + log_rtab!("dropping table db: {}", table); + let mut inner = self.inner.lock(); + if inner.opened.remove(&table).is_none() { + unreachable!("should have removed an item"); + } + } + + /// Get or create a TableDB database table. If the column count is greater than an + /// existing TableDB's column count, the database will be upgraded to add the missing columns + pub async fn open(&self, name: &str, column_count: u32) -> VeilidAPIResult { + let _async_guard = self.async_lock.lock().await; + + // If we aren't initialized yet, bail + { + let inner = self.inner.lock(); + if inner.all_tables_db.is_none() { + apibail_not_initialized!(); + } + } + + let table_name = self.name_get_or_create(name).await?; + + // See if this table is already opened, if so the column count must be the same + { + let mut inner = self.inner.lock(); + if let Some(table_db_weak_inner) = inner.opened.get(&table_name) { + match TableDB::try_new_from_weak_inner(table_db_weak_inner.clone(), column_count) { + Some(tdb) => { + // Ensure column count isnt bigger + let existing_col_count = tdb.get_column_count()?; + if column_count > existing_col_count { + return Err(VeilidAPIError::generic(format!( + "database must be closed before increasing column count {} -> {}", + existing_col_count, column_count, + ))); + } + + return Ok(tdb); + } + None => { + inner.opened.remove(&table_name); + } + }; + } + } + + // Open table db using platform-specific driver + let mut db = match self + .table_store_driver + .open(&table_name, column_count) + .await + { + Ok(db) => db, + Err(e) => { + self.name_delete(name).await.expect("cleanup failed"); + self.flush().await; + return Err(e); + } + }; + + // Flush table names to disk + self.flush().await; + + // If more columns are available, open the low level db with the max column count but restrict the tabledb object to the number requested + let existing_col_count = db.num_columns().map_err(VeilidAPIError::from)?; + if existing_col_count > column_count { + drop(db); + db = match self + .table_store_driver + .open(&table_name, existing_col_count) + .await + { + Ok(db) => db, + Err(e) => { + self.name_delete(name).await.expect("cleanup failed"); + self.flush().await; + return Err(e); + } + }; + } + + // Wrap low-level Database in TableDB object + let mut inner = self.inner.lock(); + let table_db = TableDB::new( + table_name.clone(), + self.clone(), + inner.crypto.as_ref().unwrap().clone(), + db, + inner.encryption_key, + inner.encryption_key, + column_count, + ); + + // Keep track of opened DBs + inner + .opened + .insert(table_name.clone(), table_db.weak_inner()); + + Ok(table_db) + } + + /// Delete a TableDB table by name + pub async fn delete(&self, name: &str) -> VeilidAPIResult { + let _async_guard = self.async_lock.lock().await; + // If we aren't initialized yet, bail + { + let inner = self.inner.lock(); + if inner.all_tables_db.is_none() { + apibail_not_initialized!(); + } + } + + let Some(table_name) = self.name_get(name).await? else { + // Did not exist in name table + return Ok(false); + }; + + // See if this table is opened + { + let inner = self.inner.lock(); + if inner.opened.contains_key(&table_name) { + apibail_generic!("Not deleting table that is still opened"); + } + } + + // Delete table db using platform-specific driver + let deleted = self.table_store_driver.delete(&table_name).await?; + if !deleted { + // Table missing? Just remove name + warn!( + "table existed in name table but not in storage: {} : {}", + name, table_name + ); + } + self.name_delete(name).await.expect("failed to delete name"); + self.flush().await; + + Ok(true) + } + + /// Rename a TableDB table + pub async fn rename(&self, old_name: &str, new_name: &str) -> VeilidAPIResult<()> { + let _async_guard = self.async_lock.lock().await; + // If we aren't initialized yet, bail + { + let inner = self.inner.lock(); + if inner.all_tables_db.is_none() { + apibail_not_initialized!(); + } + } + trace!("TableStore::rename {} -> {}", old_name, new_name); + self.name_rename(old_name, new_name).await?; + self.flush().await; + Ok(()) + } +} diff --git a/veilid-core/src/table_store/native.rs b/veilid-core/src/table_store/native.rs index 9e1186d5..fadbc4ef 100644 --- a/veilid-core/src/table_store/native.rs +++ b/veilid-core/src/table_store/native.rs @@ -22,7 +22,7 @@ impl TableStoreDriver { } pub async fn open(&self, table_name: &str, column_count: u32) -> VeilidAPIResult { - let dbpath = self.get_dbpath(&table_name)?; + let dbpath = self.get_dbpath(table_name)?; // Ensure permissions are correct ensure_file_private_owner(&dbpath).map_err(VeilidAPIError::internal)?; @@ -43,7 +43,7 @@ impl TableStoreDriver { } pub async fn delete(&self, table_name: &str) -> VeilidAPIResult { - let dbpath = self.get_dbpath(&table_name)?; + let dbpath = self.get_dbpath(table_name)?; if !dbpath.exists() { return Ok(false); } diff --git a/veilid-core/src/table_store/table_store.rs b/veilid-core/src/table_store/table_store.rs deleted file mode 100644 index 3f5e9821..00000000 --- a/veilid-core/src/table_store/table_store.rs +++ /dev/null @@ -1,589 +0,0 @@ -use super::*; -use keyvaluedb::*; - -const ALL_TABLE_NAMES: &[u8] = b"all_table_names"; - -struct TableStoreInner { - opened: BTreeMap>, - encryption_key: Option, - all_table_names: HashMap, - all_tables_db: Option, - crypto: Option, -} - -/// Veilid Table Storage -/// Database for storing key value pairs persistently and securely across runs -#[derive(Clone)] -pub struct TableStore { - config: VeilidConfig, - protected_store: ProtectedStore, - table_store_driver: TableStoreDriver, - inner: Arc>, // Sync mutex here because TableDB drops can happen at any time - async_lock: Arc>, // Async mutex for operations -} - -impl TableStore { - fn new_inner() -> TableStoreInner { - TableStoreInner { - opened: BTreeMap::new(), - encryption_key: None, - all_table_names: HashMap::new(), - all_tables_db: None, - crypto: None, - } - } - pub(crate) fn new(config: VeilidConfig, protected_store: ProtectedStore) -> Self { - let inner = Self::new_inner(); - let table_store_driver = TableStoreDriver::new(config.clone()); - - Self { - config, - protected_store, - inner: Arc::new(Mutex::new(inner)), - table_store_driver, - async_lock: Arc::new(AsyncMutex::new(())), - } - } - - pub(crate) fn set_crypto(&self, crypto: Crypto) { - let mut inner = self.inner.lock(); - inner.crypto = Some(crypto); - } - - // Flush internal control state (must not use crypto) - async fn flush(&self) { - let (all_table_names_value, all_tables_db) = { - let inner = self.inner.lock(); - let all_table_names_value = serialize_json_bytes(&inner.all_table_names); - (all_table_names_value, inner.all_tables_db.clone().unwrap()) - }; - let mut dbt = DBTransaction::new(); - dbt.put(0, ALL_TABLE_NAMES, &all_table_names_value); - if let Err(e) = all_tables_db.write(dbt).await { - error!("failed to write all tables db: {}", e); - } - } - - // Internal naming support - // Adds rename capability and ensures names of tables are totally unique and valid - - fn namespaced_name(&self, table: &str) -> VeilidAPIResult { - if !table - .chars() - .all(|c| char::is_alphanumeric(c) || c == '_' || c == '-') - { - apibail_invalid_argument!("table name is invalid", "table", table); - } - let c = self.config.get(); - let namespace = c.namespace.clone(); - Ok(if namespace.is_empty() { - table.to_string() - } else { - format!("_ns_{}_{}", namespace, table) - }) - } - - async fn name_get_or_create(&self, table: &str) -> VeilidAPIResult { - let name = self.namespaced_name(table)?; - - let mut inner = self.inner.lock(); - // Do we have this name yet? - if let Some(real_name) = inner.all_table_names.get(&name) { - return Ok(real_name.clone()); - } - - // If not, make a new low level name mapping - let mut real_name_bytes = [0u8; 32]; - random_bytes(&mut real_name_bytes); - let real_name = data_encoding::BASE64URL_NOPAD.encode(&real_name_bytes); - - if inner - .all_table_names - .insert(name.to_owned(), real_name.clone()) - .is_some() - { - panic!("should not have had some value"); - }; - - Ok(real_name) - } - - async fn name_delete(&self, table: &str) -> VeilidAPIResult> { - let name = self.namespaced_name(table)?; - let mut inner = self.inner.lock(); - let real_name = inner.all_table_names.remove(&name); - Ok(real_name) - } - - async fn name_get(&self, table: &str) -> VeilidAPIResult> { - let name = self.namespaced_name(table)?; - let inner = self.inner.lock(); - let real_name = inner.all_table_names.get(&name).cloned(); - Ok(real_name) - } - - async fn name_rename(&self, old_table: &str, new_table: &str) -> VeilidAPIResult<()> { - let old_name = self.namespaced_name(old_table)?; - let new_name = self.namespaced_name(new_table)?; - - let mut inner = self.inner.lock(); - // Ensure new name doesn't exist - if inner.all_table_names.contains_key(&new_name) { - return Err(VeilidAPIError::generic("new table already exists")); - } - // Do we have this name yet? - let Some(real_name) = inner.all_table_names.remove(&old_name) else { - return Err(VeilidAPIError::generic("table does not exist")); - }; - // Insert with new name - inner.all_table_names.insert(new_name.to_owned(), real_name); - - Ok(()) - } - - /// Delete all known tables - async fn delete_all(&self) { - // Get all tables - let real_names = { - let mut inner = self.inner.lock(); - let real_names = inner - .all_table_names - .values() - .cloned() - .collect::>(); - inner.all_table_names.clear(); - real_names - }; - - // Delete all tables - for table_name in real_names { - if let Err(e) = self.table_store_driver.delete(&table_name).await { - error!("error deleting table: {}", e); - } - } - self.flush().await; - } - - pub(crate) fn maybe_unprotect_device_encryption_key( - &self, - dek_bytes: &[u8], - device_encryption_key_password: &str, - ) -> EyreResult { - // Ensure the key is at least as long as necessary if unencrypted - if dek_bytes.len() < (4 + SHARED_SECRET_LENGTH) { - bail!("device encryption key is not valid"); - } - - // Get cryptosystem - let kind = FourCC::try_from(&dek_bytes[0..4]).unwrap(); - let crypto = self.inner.lock().crypto.as_ref().unwrap().clone(); - let Some(vcrypto) = crypto.get(kind) else { - bail!("unsupported cryptosystem"); - }; - - if !device_encryption_key_password.is_empty() { - if dek_bytes.len() - != (4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead() + NONCE_LENGTH) - { - bail!("password protected device encryption key is not valid"); - } - let protected_key = &dek_bytes[4..(4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead())]; - let nonce = &dek_bytes[(4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead())..]; - - let shared_secret = vcrypto - .derive_shared_secret(device_encryption_key_password.as_bytes(), &nonce) - .wrap_err("failed to derive shared secret")?; - let unprotected_key = vcrypto - .decrypt_aead( - &protected_key, - &Nonce::try_from(nonce).wrap_err("invalid nonce")?, - &shared_secret, - None, - ) - .wrap_err("failed to decrypt device encryption key")?; - return Ok(TypedSharedSecret::new( - kind, - SharedSecret::try_from(unprotected_key.as_slice()) - .wrap_err("invalid shared secret")?, - )); - } - - if dek_bytes.len() != (4 + SHARED_SECRET_LENGTH) { - bail!("password protected device encryption key is not valid"); - } - - Ok(TypedSharedSecret::new( - kind, - SharedSecret::try_from(&dek_bytes[4..])?, - )) - } - - pub(crate) fn maybe_protect_device_encryption_key( - &self, - dek: TypedSharedSecret, - device_encryption_key_password: &str, - ) -> EyreResult> { - // Check if we are to protect the key - if device_encryption_key_password.is_empty() { - debug!("no dek password"); - // Return the unprotected key bytes - let mut out = Vec::with_capacity(4 + SHARED_SECRET_LENGTH); - out.extend_from_slice(&dek.kind.0); - out.extend_from_slice(&dek.value.bytes); - return Ok(out); - } - - // Get cryptosystem - let crypto = self.inner.lock().crypto.as_ref().unwrap().clone(); - let Some(vcrypto) = crypto.get(dek.kind) else { - bail!("unsupported cryptosystem"); - }; - - let nonce = vcrypto.random_nonce(); - let shared_secret = vcrypto - .derive_shared_secret(device_encryption_key_password.as_bytes(), &nonce.bytes) - .wrap_err("failed to derive shared secret")?; - let mut protected_key = vcrypto - .encrypt_aead( - &dek.value.bytes, - &Nonce::try_from(nonce).wrap_err("invalid nonce")?, - &shared_secret, - None, - ) - .wrap_err("failed to decrypt device encryption key")?; - let mut out = - Vec::with_capacity(4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead() + NONCE_LENGTH); - out.extend_from_slice(&dek.kind.0); - out.append(&mut protected_key); - out.extend_from_slice(&nonce.bytes); - assert!(out.len() == 4 + SHARED_SECRET_LENGTH + vcrypto.aead_overhead() + NONCE_LENGTH); - Ok(out) - } - - async fn load_device_encryption_key(&self) -> EyreResult> { - let dek_bytes: Option> = self - .protected_store - .load_user_secret("device_encryption_key") - .await?; - let Some(dek_bytes) = dek_bytes else { - debug!("no device encryption key"); - return Ok(None); - }; - - // Get device encryption key protection password if we have it - let device_encryption_key_password = { - let c = self.config.get(); - c.protected_store.device_encryption_key_password.clone() - }; - - Ok(Some(self.maybe_unprotect_device_encryption_key( - &dek_bytes, - &device_encryption_key_password, - )?)) - } - async fn save_device_encryption_key( - &self, - device_encryption_key: Option, - ) -> EyreResult<()> { - let Some(device_encryption_key) = device_encryption_key else { - // Remove the device encryption key - let existed = self - .protected_store - .remove_user_secret("device_encryption_key") - .await?; - debug!("removed device encryption key. existed: {}", existed); - return Ok(()); - }; - - // Get new device encryption key protection password if we are changing it - let new_device_encryption_key_password = { - let c = self.config.get(); - c.protected_store.new_device_encryption_key_password.clone() - }; - let device_encryption_key_password = - if let Some(new_device_encryption_key_password) = new_device_encryption_key_password { - // Change password - debug!("changing dek password"); - self.config - .with_mut(|c| { - c.protected_store.device_encryption_key_password = - new_device_encryption_key_password.clone(); - Ok(new_device_encryption_key_password) - }) - .unwrap() - } else { - // Get device encryption key protection password if we have it - debug!("saving with existing dek password"); - let c = self.config.get(); - c.protected_store.device_encryption_key_password.clone() - }; - - let dek_bytes = self.maybe_protect_device_encryption_key( - device_encryption_key, - &device_encryption_key_password, - )?; - - // Save the new device encryption key - let existed = self - .protected_store - .save_user_secret("device_encryption_key", &dek_bytes) - .await?; - debug!("saving device encryption key. existed: {}", existed); - Ok(()) - } - - pub(crate) async fn init(&self) -> EyreResult<()> { - let _async_guard = self.async_lock.lock().await; - - // Get device encryption key from protected store - let mut device_encryption_key = self.load_device_encryption_key().await?; - let mut device_encryption_key_changed = false; - if let Some(device_encryption_key) = device_encryption_key { - // If encryption in current use is not the best encryption, then run table migration - let best_kind = best_crypto_kind(); - if device_encryption_key.kind != best_kind { - // XXX: Run migration. See issue #209 - } - } else { - // If we don't have an encryption key yet, then make one with the best cryptography and save it - let best_kind = best_crypto_kind(); - let mut shared_secret = SharedSecret::default(); - random_bytes(&mut shared_secret.bytes); - - device_encryption_key = Some(TypedSharedSecret::new(best_kind, shared_secret)); - device_encryption_key_changed = true; - } - - // Check for password change - let changing_password = self - .config - .get() - .protected_store - .new_device_encryption_key_password - .is_some(); - - // Save encryption key if it has changed or if the protecting password wants to change - if device_encryption_key_changed || changing_password { - self.save_device_encryption_key(device_encryption_key) - .await?; - } - - // Deserialize all table names - let all_tables_db = self - .table_store_driver - .open("__veilid_all_tables", 1) - .await - .wrap_err("failed to create all tables table")?; - match all_tables_db.get(0, ALL_TABLE_NAMES).await { - Ok(Some(v)) => match deserialize_json_bytes::>(&v) { - Ok(all_table_names) => { - let mut inner = self.inner.lock(); - inner.all_table_names = all_table_names; - } - Err(e) => { - error!("could not deserialize __veilid_all_tables: {}", e); - } - }, - Ok(None) => { - // No table names yet, that's okay - trace!("__veilid_all_tables is empty"); - } - Err(e) => { - error!("could not get __veilid_all_tables: {}", e); - } - }; - - { - let mut inner = self.inner.lock(); - inner.encryption_key = device_encryption_key; - inner.all_tables_db = Some(all_tables_db); - } - - let do_delete = { - let c = self.config.get(); - c.table_store.delete - }; - - if do_delete { - self.delete_all().await; - } - - Ok(()) - } - - pub(crate) async fn terminate(&self) { - let _async_guard = self.async_lock.lock().await; - - self.flush().await; - - let mut inner = self.inner.lock(); - if !inner.opened.is_empty() { - panic!( - "all open databases should have been closed: {:?}", - inner.opened - ); - } - inner.all_tables_db = None; - inner.all_table_names.clear(); - inner.encryption_key = None; - } - - pub(crate) fn on_table_db_drop(&self, table: String) { - log_rtab!("dropping table db: {}", table); - let mut inner = self.inner.lock(); - if inner.opened.remove(&table).is_none() { - unreachable!("should have removed an item"); - } - } - - /// Get or create a TableDB database table. If the column count is greater than an - /// existing TableDB's column count, the database will be upgraded to add the missing columns - pub async fn open(&self, name: &str, column_count: u32) -> VeilidAPIResult { - let _async_guard = self.async_lock.lock().await; - - // If we aren't initialized yet, bail - { - let inner = self.inner.lock(); - if inner.all_tables_db.is_none() { - apibail_not_initialized!(); - } - } - - let table_name = self.name_get_or_create(name).await?; - - // See if this table is already opened, if so the column count must be the same - { - let mut inner = self.inner.lock(); - if let Some(table_db_weak_inner) = inner.opened.get(&table_name) { - match TableDB::try_new_from_weak_inner(table_db_weak_inner.clone(), column_count) { - Some(tdb) => { - // Ensure column count isnt bigger - let existing_col_count = tdb.get_column_count()?; - if column_count > existing_col_count { - return Err(VeilidAPIError::generic(format!( - "database must be closed before increasing column count {} -> {}", - existing_col_count, column_count, - ))); - } - - return Ok(tdb); - } - None => { - inner.opened.remove(&table_name); - } - }; - } - } - - // Open table db using platform-specific driver - let mut db = match self - .table_store_driver - .open(&table_name, column_count) - .await - { - Ok(db) => db, - Err(e) => { - self.name_delete(name).await.expect("cleanup failed"); - self.flush().await; - return Err(e); - } - }; - - // Flush table names to disk - self.flush().await; - - // If more columns are available, open the low level db with the max column count but restrict the tabledb object to the number requested - let existing_col_count = db.num_columns().map_err(VeilidAPIError::from)?; - if existing_col_count > column_count { - drop(db); - db = match self - .table_store_driver - .open(&table_name, existing_col_count) - .await - { - Ok(db) => db, - Err(e) => { - self.name_delete(name).await.expect("cleanup failed"); - self.flush().await; - return Err(e); - } - }; - } - - // Wrap low-level Database in TableDB object - let mut inner = self.inner.lock(); - let table_db = TableDB::new( - table_name.clone(), - self.clone(), - inner.crypto.as_ref().unwrap().clone(), - db, - inner.encryption_key.clone(), - inner.encryption_key.clone(), - column_count, - ); - - // Keep track of opened DBs - inner - .opened - .insert(table_name.clone(), table_db.weak_inner()); - - Ok(table_db) - } - - /// Delete a TableDB table by name - pub async fn delete(&self, name: &str) -> VeilidAPIResult { - let _async_guard = self.async_lock.lock().await; - // If we aren't initialized yet, bail - { - let inner = self.inner.lock(); - if inner.all_tables_db.is_none() { - apibail_not_initialized!(); - } - } - - let Some(table_name) = self.name_get(name).await? else { - // Did not exist in name table - return Ok(false); - }; - - // See if this table is opened - { - let inner = self.inner.lock(); - if inner.opened.contains_key(&table_name) { - apibail_generic!("Not deleting table that is still opened"); - } - } - - // Delete table db using platform-specific driver - let deleted = self.table_store_driver.delete(&table_name).await?; - if !deleted { - // Table missing? Just remove name - warn!( - "table existed in name table but not in storage: {} : {}", - name, table_name - ); - } - self.name_delete(&name) - .await - .expect("failed to delete name"); - self.flush().await; - - Ok(true) - } - - /// Rename a TableDB table - pub async fn rename(&self, old_name: &str, new_name: &str) -> VeilidAPIResult<()> { - let _async_guard = self.async_lock.lock().await; - // If we aren't initialized yet, bail - { - let inner = self.inner.lock(); - if inner.all_tables_db.is_none() { - apibail_not_initialized!(); - } - } - trace!("TableStore::rename {} -> {}", old_name, new_name); - self.name_rename(old_name, new_name).await?; - self.flush().await; - Ok(()) - } -} diff --git a/veilid-core/src/table_store/tests/test_table_store.rs b/veilid-core/src/table_store/tests/test_table_store.rs index 9bb503b3..85821339 100644 --- a/veilid-core/src/table_store/tests/test_table_store.rs +++ b/veilid-core/src/table_store/tests/test_table_store.rs @@ -18,7 +18,7 @@ async fn shutdown(api: VeilidAPI) { pub async fn test_delete_open_delete(ts: TableStore) { trace!("test_delete_open_delete"); - let _ = ts.delete("test"); + let _ = ts.delete("test").await; let db = ts.open("test", 3).await.expect("should have opened"); assert!( ts.delete("test").await.is_err(), @@ -50,7 +50,7 @@ pub async fn test_delete_open_delete(ts: TableStore) { pub async fn test_store_delete_load(ts: TableStore) { trace!("test_store_delete_load"); - let _ = ts.delete("test"); + let _ = ts.delete("test").await; let db = ts.open("test", 3).await.expect("should have opened"); assert!( ts.delete("test").await.is_err(), @@ -135,7 +135,7 @@ pub async fn test_store_delete_load(ts: TableStore) { pub async fn test_transaction(ts: TableStore) { trace!("test_transaction"); - let _ = ts.delete("test"); + let _ = ts.delete("test").await; let db = ts.open("test", 3).await.expect("should have opened"); assert!( ts.delete("test").await.is_err(), @@ -165,7 +165,7 @@ pub async fn test_transaction(ts: TableStore) { pub async fn test_json(vcrypto: CryptoSystemVersion, ts: TableStore) { trace!("test_json"); - let _ = ts.delete("test"); + let _ = ts.delete("test").await; let db = ts.open("test", 3).await.expect("should have opened"); let keypair = vcrypto.generate_keypair(); @@ -229,10 +229,10 @@ pub async fn test_protect_unprotect(vcrypto: CryptoSystemVersion, ts: TableStore for password in passwords { let dek_bytes = ts .maybe_protect_device_encryption_key(dek, password) - .expect(&format!("protect: dek: '{}' pw: '{}'", dek, password)); + .unwrap_or_else(|_| panic!("protect: dek: '{}' pw: '{}'", dek, password)); let unprotected = ts .maybe_unprotect_device_encryption_key(&dek_bytes, password) - .expect(&format!("unprotect: dek: '{}' pw: '{}'", dek, password)); + .unwrap_or_else(|_| panic!("unprotect: dek: '{}' pw: '{}'", dek, password)); assert_eq!(unprotected, dek); let invalid_password = format!("{}x", password); let _ = ts @@ -241,7 +241,7 @@ pub async fn test_protect_unprotect(vcrypto: CryptoSystemVersion, ts: TableStore "invalid_password: dek: '{}' pw: '{}'", dek, &invalid_password )); - if password != "" { + if !password.is_empty() { let _ = ts .maybe_unprotect_device_encryption_key(&dek_bytes, "") .expect_err(&format!("empty_password: dek: '{}' pw: ''", dek)); diff --git a/veilid-core/src/tests/common/test_protected_store.rs b/veilid-core/src/tests/common/test_protected_store.rs index 49d56d4e..ac6bf5ec 100644 --- a/veilid-core/src/tests/common/test_protected_store.rs +++ b/veilid-core/src/tests/common/test_protected_store.rs @@ -23,14 +23,12 @@ pub async fn test_protected_store(ps: ProtectedStore) { let d1: [u8; 0] = []; - assert_eq!( - ps.save_user_secret("_test_key", &[2u8, 3u8, 4u8]) - .await - .unwrap(), - false - ); + assert!(!ps + .save_user_secret("_test_key", &[2u8, 3u8, 4u8]) + .await + .unwrap()); info!("testing saving user secret"); - assert_eq!(ps.save_user_secret("_test_key", &d1).await.unwrap(), true); + assert!(ps.save_user_secret("_test_key", &d1).await.unwrap()); info!("testing loading user secret"); assert_eq!( ps.load_user_secret("_test_key").await.unwrap(), @@ -46,23 +44,21 @@ pub async fn test_protected_store(ps: ProtectedStore) { info!("testing loading broken user secret again"); assert_eq!(ps.load_user_secret("_test_broken").await.unwrap(), None); info!("testing remove user secret"); - assert_eq!(ps.remove_user_secret("_test_key").await.unwrap(), true); + assert!(ps.remove_user_secret("_test_key").await.unwrap()); info!("testing remove user secret again"); - assert_eq!(ps.remove_user_secret("_test_key").await.unwrap(), false); + assert!(!ps.remove_user_secret("_test_key").await.unwrap()); info!("testing remove broken user secret"); - assert_eq!(ps.remove_user_secret("_test_broken").await.unwrap(), false); + assert!(!ps.remove_user_secret("_test_broken").await.unwrap()); info!("testing remove broken user secret again"); - assert_eq!(ps.remove_user_secret("_test_broken").await.unwrap(), false); + assert!(!ps.remove_user_secret("_test_broken").await.unwrap()); let d2: [u8; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - assert_eq!( - ps.save_user_secret("_test_key", &[2u8, 3u8, 4u8]) - .await - .unwrap(), - false - ); - assert_eq!(ps.save_user_secret("_test_key", &d2).await.unwrap(), true); + assert!(!ps + .save_user_secret("_test_key", &[2u8, 3u8, 4u8]) + .await + .unwrap()); + assert!(ps.save_user_secret("_test_key", &d2).await.unwrap()); assert_eq!( ps.load_user_secret("_test_key").await.unwrap(), Some(d2.to_vec()) @@ -73,10 +69,10 @@ pub async fn test_protected_store(ps: ProtectedStore) { ); assert_eq!(ps.load_user_secret("_test_broken").await.unwrap(), None); assert_eq!(ps.load_user_secret("_test_broken").await.unwrap(), None); - assert_eq!(ps.remove_user_secret("_test_key").await.unwrap(), true); - assert_eq!(ps.remove_user_secret("_test_key").await.unwrap(), false); - assert_eq!(ps.remove_user_secret("_test_broken").await.unwrap(), false); - assert_eq!(ps.remove_user_secret("_test_broken").await.unwrap(), false); + assert!(ps.remove_user_secret("_test_key").await.unwrap()); + assert!(!ps.remove_user_secret("_test_key").await.unwrap()); + assert!(!ps.remove_user_secret("_test_key").await.unwrap()); + assert!(!ps.remove_user_secret("_test_broken").await.unwrap()); let _ = ps.remove_user_secret("_test_key").await; let _ = ps.remove_user_secret("_test_broken").await; diff --git a/veilid-core/src/tests/common/test_veilid_config.rs b/veilid-core/src/tests/common/test_veilid_config.rs index f6b2f844..77484ad7 100644 --- a/veilid-core/src/tests/common/test_veilid_config.rs +++ b/veilid-core/src/tests/common/test_veilid_config.rs @@ -1,5 +1,3 @@ -#![allow(clippy::bool_assert_comparison)] - use crate::*; cfg_if! { @@ -168,7 +166,7 @@ fn config_callback(key: String) -> ConfigCallbackReturn { match key.as_str() { "program_name" => Ok(Box::new(String::from("VeilidCoreTests"))), "namespace" => Ok(Box::::default()), - "capabilities.disable" => Ok(Box::>::default()), + "capabilities.disable" => Ok(Box::>::default()), "table_store.directory" => Ok(Box::new(get_table_store_path())), "table_store.delete" => Ok(Box::new(true)), "block_store.directory" => Ok(Box::new(get_block_store_path())), @@ -193,7 +191,7 @@ fn config_callback(key: String) -> ConfigCallbackReturn { "network.network_key_password" => Ok(Box::new(Option::::None)), "network.routing_table.node_id" => Ok(Box::new(TypedKeyGroup::new())), "network.routing_table.node_id_secret" => Ok(Box::new(TypedSecretGroup::new())), - "network.routing_table.bootstrap" => Ok(Box::>::default()), + "network.routing_table.bootstrap" => Ok(Box::>::default()), "network.routing_table.limit_over_attached" => Ok(Box::new(64u32)), "network.routing_table.limit_fully_attached" => Ok(Box::new(32u32)), "network.routing_table.limit_attached_strong" => Ok(Box::new(16u32)), @@ -295,13 +293,13 @@ pub async fn test_config() { assert_eq!(inner.namespace, String::from("")); assert_eq!(inner.capabilities.disable, Vec::::new()); assert_eq!(inner.table_store.directory, get_table_store_path()); - assert_eq!(inner.table_store.delete, true); + assert!(inner.table_store.delete); assert_eq!(inner.block_store.directory, get_block_store_path()); - assert_eq!(inner.block_store.delete, true); - assert_eq!(inner.protected_store.allow_insecure_fallback, true); - assert_eq!(inner.protected_store.always_use_insecure_storage, false); + assert!(inner.block_store.delete); + assert!(inner.protected_store.allow_insecure_fallback); + assert!(!inner.protected_store.always_use_insecure_storage); assert_eq!(inner.protected_store.directory, get_protected_store_path()); - assert_eq!(inner.protected_store.delete, true); + assert!(inner.protected_store.delete); assert_eq!( inner.protected_store.device_encryption_key_password, "".to_owned() @@ -351,38 +349,38 @@ pub async fn test_config() { 5_000u32 ); - assert_eq!(inner.network.upnp, false); - assert_eq!(inner.network.detect_address_changes, true); + assert!(!inner.network.upnp); + assert!(inner.network.detect_address_changes); assert_eq!(inner.network.restricted_nat_retries, 3u32); assert_eq!(inner.network.tls.certificate_path, get_certfile_path()); assert_eq!(inner.network.tls.private_key_path, get_keyfile_path()); assert_eq!(inner.network.tls.connection_initial_timeout_ms, 2_000u32); - assert_eq!(inner.network.application.https.enabled, false); + assert!(!inner.network.application.https.enabled); assert_eq!(inner.network.application.https.listen_address, ""); assert_eq!(inner.network.application.https.path, "app"); assert_eq!(inner.network.application.https.url, None); - assert_eq!(inner.network.application.http.enabled, false); + assert!(!inner.network.application.http.enabled); assert_eq!(inner.network.application.http.listen_address, ""); assert_eq!(inner.network.application.http.path, "app"); assert_eq!(inner.network.application.http.url, None); - assert_eq!(inner.network.protocol.udp.enabled, true); + assert!(inner.network.protocol.udp.enabled); assert_eq!(inner.network.protocol.udp.socket_pool_size, 16u32); assert_eq!(inner.network.protocol.udp.listen_address, ""); assert_eq!(inner.network.protocol.udp.public_address, None); - assert_eq!(inner.network.protocol.tcp.connect, true); - assert_eq!(inner.network.protocol.tcp.listen, true); + assert!(inner.network.protocol.tcp.connect); + assert!(inner.network.protocol.tcp.listen); assert_eq!(inner.network.protocol.tcp.max_connections, 32u32); assert_eq!(inner.network.protocol.tcp.listen_address, ""); assert_eq!(inner.network.protocol.tcp.public_address, None); - assert_eq!(inner.network.protocol.ws.connect, false); - assert_eq!(inner.network.protocol.ws.listen, false); + assert!(!inner.network.protocol.ws.connect); + assert!(!inner.network.protocol.ws.listen); assert_eq!(inner.network.protocol.ws.max_connections, 16u32); assert_eq!(inner.network.protocol.ws.listen_address, ""); assert_eq!(inner.network.protocol.ws.path, "ws"); assert_eq!(inner.network.protocol.ws.url, None); - assert_eq!(inner.network.protocol.wss.connect, false); - assert_eq!(inner.network.protocol.wss.listen, false); + assert!(!inner.network.protocol.wss.connect); + assert!(!inner.network.protocol.wss.listen); assert_eq!(inner.network.protocol.wss.max_connections, 16u32); assert_eq!(inner.network.protocol.wss.listen_address, ""); assert_eq!(inner.network.protocol.wss.path, "ws"); diff --git a/veilid-core/src/veilid_api/api.rs b/veilid-core/src/veilid_api/api.rs index 6fcc5b6b..e2139e49 100644 --- a/veilid-core/src/veilid_api/api.rs +++ b/veilid-core/src/veilid_api/api.rs @@ -263,7 +263,7 @@ impl VeilidAPI { let rss = self.routing_table()?.route_spec_store(); let r = rss .allocate_route( - &crypto_kinds, + crypto_kinds, stability, sequencing, default_route_hop_count, @@ -275,7 +275,7 @@ impl VeilidAPI { apibail_generic!("unable to allocate route"); }; if !rss - .test_route(route_id.clone()) + .test_route(route_id) .await .map_err(VeilidAPIError::no_connection)? { diff --git a/veilid-core/src/veilid_api/debug.rs b/veilid-core/src/veilid_api/debug.rs index 5d633868..7cbd3ce6 100644 --- a/veilid-core/src/veilid_api/debug.rs +++ b/veilid-core/src/veilid_api/debug.rs @@ -61,9 +61,9 @@ fn get_string(text: &str) -> Option { } fn get_data(text: &str) -> Option> { - if text.starts_with("#") { - hex::decode(&text[1..]).ok() - } else if text.starts_with("\"") || text.starts_with("'") { + if let Some(stripped_text) = text.strip_prefix('#') { + hex::decode(stripped_text).ok() + } else if text.starts_with('"') || text.starts_with('\'') { json::parse(text) .ok()? .as_str() @@ -86,7 +86,7 @@ fn get_route_id( allow_allocated: bool, allow_remote: bool, ) -> impl Fn(&str) -> Option { - return move |text: &str| { + move |text: &str| { if text.is_empty() { return None; } @@ -127,7 +127,7 @@ fn get_route_id( } } None - }; + } } fn get_dht_schema(text: &str) -> Option { @@ -140,7 +140,7 @@ fn get_safety_selection(routing_table: RoutingTable) -> impl Fn(&str) -> Option< let default_route_hop_count = routing_table.with_config(|c| c.network.rpc.default_route_hop_count as usize); - if text.len() != 0 && &text[0..1] == "-" { + if !text.is_empty() && &text[0..1] == "-" { // Unsafe let text = &text[1..]; let seq = get_sequencing(text).unwrap_or_default(); @@ -151,7 +151,7 @@ fn get_safety_selection(routing_table: RoutingTable) -> impl Fn(&str) -> Option< let mut hop_count = default_route_hop_count; let mut stability = Stability::default(); let mut sequencing = Sequencing::default(); - for x in text.split(",") { + for x in text.split(',') { let x = x.trim(); if let Some(pr) = get_route_id(rss.clone(), true, false)(x) { preferred_route = Some(pr) @@ -179,7 +179,7 @@ fn get_safety_selection(routing_table: RoutingTable) -> impl Fn(&str) -> Option< fn get_node_ref_modifiers(mut node_ref: NodeRef) -> impl FnOnce(&str) -> Option { move |text| { - for m in text.split("/") { + for m in text.split('/') { if let Some(pt) = get_protocol_type(m) { node_ref.merge_filter(NodeRefFilter::new().with_protocol_type(pt)); } else if let Some(at) = get_address_type(m) { @@ -207,7 +207,7 @@ fn get_destination( } else { (text.as_str(), None) }; - if text.len() == 0 { + if text.is_empty() { return None; } if &text[0..1] == "#" { @@ -219,19 +219,19 @@ fn get_destination( let private_route = if let Some(prid) = get_route_id(rss.clone(), false, true)(text) { let Some(private_route) = rss.best_remote_private_route(&prid) else { - return None; - }; + return None; + }; private_route } else { let mut dc = DEBUG_CACHE.lock(); let n = get_number(text)?; - let prid = dc.imported_routes.get(n)?.clone(); + let prid = *dc.imported_routes.get(n)?; let Some(private_route) = rss.best_remote_private_route(&prid) else { - // Remove imported route - dc.imported_routes.remove(n); - info!("removed dead imported route {}", n); - return None; - }; + // Remove imported route + dc.imported_routes.remove(n); + info!("removed dead imported route {}", n); + return None; + }; private_route }; @@ -312,7 +312,7 @@ fn get_dht_key( } else { (text, None) }; - if text.len() == 0 { + if text.is_empty() { return None; } @@ -528,13 +528,13 @@ pub fn print_data(data: &[u8], truncate_len: Option) -> String { let (data, truncated) = if truncate_len.is_some() && data.len() > truncate_len.unwrap() { (&data[0..64], true) } else { - (&data[..], false) + (data, false) }; let strdata = if printable { - format!("{}", String::from_utf8_lossy(&data).to_string()) + String::from_utf8_lossy(data).to_string() } else { - let sw = shell_words::quote(&String::from_utf8_lossy(&data).to_string()).to_string(); + let sw = shell_words::quote(String::from_utf8_lossy(data).as_ref()).to_string(); let h = hex::encode(data); if h.len() < sw.len() { h @@ -1019,8 +1019,8 @@ impl VeilidAPI { let netman = self.network_manager()?; let rpc = netman.rpc_processor(); - let (call_id, data) = if args.starts_with("#") { - let (arg, rest) = args[1..].split_once(' ').unwrap_or((&args, "")); + let (call_id, data) = if let Some(stripped_args) = args.strip_prefix('#') { + let (arg, rest) = stripped_args.split_once(' ').unwrap_or((&args, "")); let call_id = OperationId::new(u64::from_str_radix(arg, 16).map_err(VeilidAPIError::generic)?); let rest = rest.trim_start().to_owned(); @@ -1097,7 +1097,7 @@ impl VeilidAPI { &[], ) { Ok(Some(v)) => format!("{}", v), - Ok(None) => format!(""), + Ok(None) => "".to_string(), Err(e) => { format!("Route allocation failed: {}", e) } @@ -1270,7 +1270,7 @@ impl VeilidAPI { let out = format!("Private route #{} imported: {}", n, route_id); dc.imported_routes.push(route_id); - return Ok(out); + Ok(out) } async fn debug_route_test(&self, args: Vec) -> VeilidAPIResult { @@ -1298,7 +1298,7 @@ impl VeilidAPI { "FAILED".to_owned() }; - return Ok(out); + Ok(out) } async fn debug_route(&self, args: String) -> VeilidAPIResult { @@ -1334,18 +1334,18 @@ impl VeilidAPI { let scope = get_debug_argument_at(&args, 1, "debug_record_list", "scope", get_string)?; let out = match scope.as_str() { "local" => { - let mut out = format!("Local Records:\n"); + let mut out = "Local Records:\n".to_string(); out += &storage_manager.debug_local_records().await; out } "remote" => { - let mut out = format!("Remote Records:\n"); + let mut out = "Remote Records:\n".to_string(); out += &storage_manager.debug_remote_records().await; out } _ => "Invalid scope\n".to_owned(), }; - return Ok(out); + Ok(out) } async fn debug_record_purge(&self, args: Vec) -> VeilidAPIResult { @@ -1359,7 +1359,7 @@ impl VeilidAPI { "remote" => storage_manager.purge_remote_records(bytes).await, _ => "Invalid scope\n".to_owned(), }; - return Ok(out); + Ok(out) } async fn debug_record_create(&self, args: Vec) -> VeilidAPIResult { @@ -1397,11 +1397,10 @@ impl VeilidAPI { // Get routing context with optional privacy let rc = self.routing_context(); let rc = if let Some(ss) = ss { - let rcp = match rc.with_custom_privacy(ss) { + match rc.with_custom_privacy(ss) { Err(e) => return Ok(format!("Can't use safety selection: {}", e)), Ok(v) => v, - }; - rcp + } } else { rc }; @@ -1416,7 +1415,7 @@ impl VeilidAPI { Ok(v) => v, }; debug!("DHT Record Created:\n{:#?}", record); - return Ok(format!("{:?}", record)); + Ok(format!("{:?}", record)) } async fn debug_record_get(&self, args: Vec) -> VeilidAPIResult { @@ -1456,11 +1455,10 @@ impl VeilidAPI { // Get routing context with optional privacy let rc = self.routing_context(); let rc = if let Some(ss) = ss { - let rcp = match rc.with_custom_privacy(ss) { + match rc.with_custom_privacy(ss) { Err(e) => return Ok(format!("Can't use safety selection: {}", e)), Ok(v) => v, - }; - rcp + } } else { rc }; @@ -1497,7 +1495,7 @@ impl VeilidAPI { Err(e) => return Ok(format!("Can't close DHT record: {}", e)), Ok(v) => v, }; - return Ok(out); + Ok(out) } async fn debug_record_set(&self, args: Vec) -> VeilidAPIResult { @@ -1518,11 +1516,10 @@ impl VeilidAPI { // Get routing context with optional privacy let rc = self.routing_context(); let rc = if let Some(ss) = ss { - let rcp = match rc.with_custom_privacy(ss) { + match rc.with_custom_privacy(ss) { Err(e) => return Ok(format!("Can't use safety selection: {}", e)), Ok(v) => v, - }; - rcp + } } else { rc }; @@ -1556,7 +1553,7 @@ impl VeilidAPI { Err(e) => return Ok(format!("Can't close DHT record: {}", e)), Ok(v) => v, }; - return Ok(out); + Ok(out) } async fn debug_record_delete(&self, args: Vec) -> VeilidAPIResult { @@ -1568,7 +1565,7 @@ impl VeilidAPI { Err(e) => return Ok(format!("Can't delete DHT record: {}", e)), Ok(v) => v, }; - Ok(format!("DHT record deleted")) + Ok("DHT record deleted".to_string()) } async fn debug_record_info(&self, args: Vec) -> VeilidAPIResult { @@ -1595,7 +1592,7 @@ impl VeilidAPI { let ri = storage_manager.debug_remote_record_info(key).await; format!("Local Info:\n{}\n\nRemote Info:\n{}\n", li, ri) }; - return Ok(out); + Ok(out) } async fn debug_record(&self, args: String) -> VeilidAPIResult { @@ -1629,7 +1626,7 @@ impl VeilidAPI { let address_filter = network_manager.address_filter(); let out = format!("Address Filter Punishments:\n{:#?}", address_filter); - return Ok(out); + Ok(out) } async fn debug_punish(&self, args: String) -> VeilidAPIResult { diff --git a/veilid-core/src/veilid_api/json_api/mod.rs b/veilid-core/src/veilid_api/json_api/mod.rs index 5b04a4bd..b35e1ea2 100644 --- a/veilid-core/src/veilid_api/json_api/mod.rs +++ b/veilid-core/src/veilid_api/json_api/mod.rs @@ -140,7 +140,7 @@ pub enum ResponseOp { }, GetState { #[serde(flatten)] - result: ApiResult, + result: ApiResult>, }, Attach { #[serde(flatten)] @@ -175,7 +175,7 @@ pub enum ResponseOp { NewRoutingContext { value: u32, }, - RoutingContext(RoutingContextResponse), + RoutingContext(Box), // TableDb OpenTableDb { #[serde(flatten)] diff --git a/veilid-core/src/veilid_api/json_api/process.rs b/veilid-core/src/veilid_api/json_api/process.rs index 8e1264df..a84be568 100644 --- a/veilid-core/src/veilid_api/json_api/process.rs +++ b/veilid-core/src/veilid_api/json_api/process.rs @@ -87,10 +87,10 @@ impl JsonRequestProcessor { let Some(routing_context) = inner.routing_contexts.get(&rc_id).cloned() else { return Err(Response { id, - op: ResponseOp::RoutingContext(RoutingContextResponse { + op: ResponseOp::RoutingContext(Box::new(RoutingContextResponse { rc_id, - rc_op: RoutingContextResponseOp::InvalidId - }) + rc_op: RoutingContextResponseOp::InvalidId, + })), }); }; Ok(routing_context) @@ -100,7 +100,7 @@ impl JsonRequestProcessor { if inner.routing_contexts.remove(&id).is_none() { return 0; } - return 1; + 1 } // TableDB @@ -120,8 +120,8 @@ impl JsonRequestProcessor { id, op: ResponseOp::TableDb(TableDbResponse { db_id, - db_op: TableDbResponseOp::InvalidId - }) + db_op: TableDbResponseOp::InvalidId, + }), }); }; Ok(table_db) @@ -131,7 +131,7 @@ impl JsonRequestProcessor { if inner.table_dbs.remove(&id).is_none() { return 0; } - return 1; + 1 } // TableDBTransaction @@ -155,8 +155,8 @@ impl JsonRequestProcessor { id, op: ResponseOp::TableDbTransaction(TableDbTransactionResponse { tx_id, - tx_op: TableDbTransactionResponseOp::InvalidId - }) + tx_op: TableDbTransactionResponseOp::InvalidId, + }), }); }; Ok(table_db_transaction) @@ -166,7 +166,7 @@ impl JsonRequestProcessor { if inner.table_db_transactions.remove(&id).is_none() { return 0; } - return 1; + 1 } // CryptoSystem @@ -186,8 +186,8 @@ impl JsonRequestProcessor { id, op: ResponseOp::CryptoSystem(CryptoSystemResponse { cs_id, - cs_op: CryptoSystemResponseOp::InvalidId - }) + cs_op: CryptoSystemResponseOp::InvalidId, + }), }); }; Ok(crypto_system) @@ -197,7 +197,7 @@ impl JsonRequestProcessor { if inner.crypto_systems.remove(&id).is_none() { return 0; } - return 1; + 1 } // Target @@ -280,13 +280,21 @@ impl JsonRequestProcessor { RoutingContextRequestOp::CreateDhtRecord { schema, kind } => { RoutingContextResponseOp::CreateDhtRecord { result: to_json_api_result( - routing_context.create_dht_record(schema, kind).await, + routing_context + .create_dht_record(schema, kind) + .await + .map(Box::new), ), } } RoutingContextRequestOp::OpenDhtRecord { key, writer } => { RoutingContextResponseOp::OpenDhtRecord { - result: to_json_api_result(routing_context.open_dht_record(key, writer).await), + result: to_json_api_result( + routing_context + .open_dht_record(key, writer) + .await + .map(Box::new), + ), } } RoutingContextRequestOp::CloseDhtRecord { key } => { @@ -508,7 +516,7 @@ impl JsonRequestProcessor { &body, &nonce, &shared_secret, - associated_data.as_ref().map(|ad| ad.as_slice()), + associated_data.as_deref(), )), }, CryptoSystemRequestOp::EncryptAead { @@ -521,7 +529,7 @@ impl JsonRequestProcessor { &body, &nonce, &shared_secret, - associated_data.as_ref().map(|ad| ad.as_slice()), + associated_data.as_deref(), )), }, CryptoSystemRequestOp::CryptNoAuth { @@ -548,7 +556,7 @@ impl JsonRequestProcessor { ))), }, RequestOp::GetState => ResponseOp::GetState { - result: to_json_api_result(self.api.get_state().await), + result: to_json_api_result(self.api.get_state().await.map(Box::new)), }, RequestOp::Attach => ResponseOp::Attach { result: to_json_api_result(self.api.attach().await), @@ -596,10 +604,10 @@ impl JsonRequestProcessor { Ok(v) => v, Err(e) => return e, }; - ResponseOp::RoutingContext( + ResponseOp::RoutingContext(Box::new( self.process_routing_context_request(routing_context, rcr) .await, - ) + )) } RequestOp::OpenTableDb { name, column_count } => { let table_store = match self.api.table_store() { diff --git a/veilid-core/src/veilid_api/json_api/routing_context.rs b/veilid-core/src/veilid_api/json_api/routing_context.rs index dc4ac323..18b1e329 100644 --- a/veilid-core/src/veilid_api/json_api/routing_context.rs +++ b/veilid-core/src/veilid_api/json_api/routing_context.rs @@ -110,11 +110,11 @@ pub enum RoutingContextResponseOp { }, CreateDhtRecord { #[serde(flatten)] - result: ApiResult, + result: ApiResult>, }, OpenDhtRecord { #[serde(flatten)] - result: ApiResult, + result: ApiResult>, }, CloseDhtRecord { #[serde(flatten)] diff --git a/veilid-core/src/veilid_api/serialize_helpers/compression.rs b/veilid-core/src/veilid_api/serialize_helpers/compression.rs index cb45ed30..627b21fe 100644 --- a/veilid-core/src/veilid_api/serialize_helpers/compression.rs +++ b/veilid-core/src/veilid_api/serialize_helpers/compression.rs @@ -19,5 +19,5 @@ pub fn decompress_size_prepended( )); } } - Ok(block::decompress(input, uncompressed_size).map_err(VeilidAPIError::generic)?) + block::decompress(input, uncompressed_size).map_err(VeilidAPIError::generic) } diff --git a/veilid-core/src/veilid_api/serialize_helpers/serialize_json.rs b/veilid-core/src/veilid_api/serialize_helpers/serialize_json.rs index 1f41ebf4..f30e6deb 100644 --- a/veilid-core/src/veilid_api/serialize_helpers/serialize_json.rs +++ b/veilid-core/src/veilid_api/serialize_helpers/serialize_json.rs @@ -93,7 +93,7 @@ pub mod as_human_base64 { let base64 = String::deserialize(d)?; BASE64URL_NOPAD .decode(base64.as_bytes()) - .map_err(|e| serde::de::Error::custom(e)) + .map_err(serde::de::Error::custom) } else { Vec::::deserialize(d) } @@ -106,7 +106,7 @@ pub mod as_human_opt_base64 { pub fn serialize(v: &Option>, s: S) -> Result { if s.is_human_readable() { - let base64 = v.as_ref().map(|x| BASE64URL_NOPAD.encode(&x)); + let base64 = v.as_ref().map(|x| BASE64URL_NOPAD.encode(x)); Option::::serialize(&base64, s) } else { Option::>::serialize(v, s) @@ -120,7 +120,7 @@ pub mod as_human_opt_base64 { .map(|x| { BASE64URL_NOPAD .decode(x.as_bytes()) - .map_err(|e| serde::de::Error::custom(e)) + .map_err(serde::de::Error::custom) }) .transpose() } else { diff --git a/veilid-core/src/veilid_api/tests/test_types.rs b/veilid-core/src/veilid_api/tests/test_types.rs index 7cbe27bb..3a9b38bf 100644 --- a/veilid-core/src/veilid_api/tests/test_types.rs +++ b/veilid-core/src/veilid_api/tests/test_types.rs @@ -43,21 +43,21 @@ pub async fn test_fourcc() { pub async fn test_sequencing() { let orig = Sequencing::PreferOrdered; - let copy = deserialize_json(&serialize_json(&orig)).unwrap(); + let copy = deserialize_json(&serialize_json(orig)).unwrap(); assert_eq!(orig, copy); } pub async fn test_stability() { let orig = Stability::Reliable; - let copy = deserialize_json(&serialize_json(&orig)).unwrap(); + let copy = deserialize_json(&serialize_json(orig)).unwrap(); assert_eq!(orig, copy); } pub async fn test_safetyselection() { let orig = SafetySelection::Unsafe(Sequencing::EnsureOrdered); - let copy = deserialize_json(&serialize_json(&orig)).unwrap(); + let copy = deserialize_json(&serialize_json(orig)).unwrap(); assert_eq!(orig, copy); } @@ -178,7 +178,7 @@ pub async fn test_partialtunnel() { pub async fn test_veilidloglevel() { let orig = VeilidLogLevel::Info; - let copy = deserialize_json(&serialize_json(&orig)).unwrap(); + let copy = deserialize_json(&serialize_json(orig)).unwrap(); assert_eq!(orig, copy); } @@ -198,7 +198,7 @@ pub async fn test_veilidlog() { pub async fn test_attachmentstate() { let orig = AttachmentState::FullyAttached; - let copy = deserialize_json(&serialize_json(&orig)).unwrap(); + let copy = deserialize_json(&serialize_json(orig)).unwrap(); assert_eq!(orig, copy); } @@ -260,7 +260,7 @@ pub async fn test_veilidvaluechange() { } pub async fn test_veilidupdate() { - let orig = VeilidUpdate::ValueChange(fix_veilidvaluechange()); + let orig = VeilidUpdate::ValueChange(Box::new(fix_veilidvaluechange())); let copy = deserialize_json(&serialize_json(&orig)).unwrap(); assert_eq!(orig, copy); @@ -268,20 +268,20 @@ pub async fn test_veilidupdate() { pub async fn test_veilidstate() { let orig = VeilidState { - attachment: VeilidStateAttachment { + attachment: Box::new(VeilidStateAttachment { state: AttachmentState::OverAttached, public_internet_ready: true, local_network_ready: false, - }, - network: VeilidStateNetwork { + }), + network: Box::new(VeilidStateNetwork { started: true, bps_down: AlignedU64::from(14_400), bps_up: AlignedU64::from(1200), peers: vec![fix_peertabledata()], - }, - config: VeilidStateConfig { + }), + config: Box::new(VeilidStateConfig { config: fix_veilidconfiginner(), - }, + }), }; let copy = deserialize_json(&serialize_json(&orig)).unwrap(); diff --git a/veilid-core/src/veilid_api/types/dht/schema/dflt.rs b/veilid-core/src/veilid_api/types/dht/schema/dflt.rs index bb4e7c6e..144d6075 100644 --- a/veilid-core/src/veilid_api/types/dht/schema/dflt.rs +++ b/veilid-core/src/veilid_api/types/dht/schema/dflt.rs @@ -61,7 +61,7 @@ impl TryFrom<&[u8]> for DHTSchemaDFLT { if b.len() != Self::FIXED_SIZE { apibail_generic!("invalid size"); } - if &b[0..4] != &Self::FCC { + if b[0..4] != Self::FCC { apibail_generic!("wrong fourcc"); } diff --git a/veilid-core/src/veilid_api/types/dht/schema/smpl.rs b/veilid-core/src/veilid_api/types/dht/schema/smpl.rs index b14ed67c..c1511a4b 100644 --- a/veilid-core/src/veilid_api/types/dht/schema/smpl.rs +++ b/veilid-core/src/veilid_api/types/dht/schema/smpl.rs @@ -101,7 +101,7 @@ impl TryFrom<&[u8]> for DHTSchemaSMPL { if b.len() < Self::FIXED_SIZE { apibail_generic!("invalid size"); } - if &b[0..4] != &Self::FCC { + if b[0..4] != Self::FCC { apibail_generic!("wrong fourcc"); } if (b.len() - Self::FIXED_SIZE) % (PUBLIC_KEY_LENGTH + 2) != 0 { diff --git a/veilid-core/src/veilid_api/types/dht/value_subkey_range_set.rs b/veilid-core/src/veilid_api/types/dht/value_subkey_range_set.rs index 9e7eb970..99ba294b 100644 --- a/veilid-core/src/veilid_api/types/dht/value_subkey_range_set.rs +++ b/veilid-core/src/veilid_api/types/dht/value_subkey_range_set.rs @@ -32,10 +32,13 @@ impl FromStr for ValueSubkeyRangeSet { fn from_str(value: &str) -> Result { let mut data = RangeSetBlaze::::new(); - for r in value.split(",") { + for r in value.split(',') { let r = r.trim(); let Some((ss, es)) = r.split_once("..=") else { - return Err(VeilidAPIError::parse_error("can not parse ValueSubkeyRangeSet", r)); + return Err(VeilidAPIError::parse_error( + "can not parse ValueSubkeyRangeSet", + r, + )); }; let sn = ValueSubkey::from_str(ss) .map_err(|e| VeilidAPIError::parse_error("could not parse ValueSubkey", e))?; diff --git a/veilid-core/src/veilid_api/types/veilid_state.rs b/veilid-core/src/veilid_api/types/veilid_state.rs index 49b180c9..4f2d441d 100644 --- a/veilid-core/src/veilid_api/types/veilid_state.rs +++ b/veilid-core/src/veilid_api/types/veilid_state.rs @@ -108,14 +108,14 @@ pub struct VeilidValueChange { #[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(into_wasm_abi))] #[serde(tag = "kind")] pub enum VeilidUpdate { - Log(VeilidLog), - AppMessage(VeilidAppMessage), - AppCall(VeilidAppCall), - Attachment(VeilidStateAttachment), - Network(VeilidStateNetwork), - Config(VeilidStateConfig), - RouteChange(VeilidRouteChange), - ValueChange(VeilidValueChange), + Log(Box), + AppMessage(Box), + AppCall(Box), + Attachment(Box), + Network(Box), + Config(Box), + RouteChange(Box), + ValueChange(Box), Shutdown, } from_impl_to_jsvalue!(VeilidUpdate); @@ -123,8 +123,8 @@ from_impl_to_jsvalue!(VeilidUpdate); #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[cfg_attr(target_arch = "wasm32", derive(Tsify), tsify(into_wasm_abi))] pub struct VeilidState { - pub attachment: VeilidStateAttachment, - pub network: VeilidStateNetwork, - pub config: VeilidStateConfig, + pub attachment: Box, + pub network: Box, + pub config: Box, } from_impl_to_jsvalue!(VeilidState); diff --git a/veilid-core/src/veilid_config.rs b/veilid-core/src/veilid_config.rs index f8a5cf88..2877eea6 100644 --- a/veilid-core/src/veilid_config.rs +++ b/veilid-core/src/veilid_config.rs @@ -565,11 +565,11 @@ impl VeilidConfig { }) } - pub fn get_veilid_state(&self) -> VeilidStateConfig { + pub fn get_veilid_state(&self) -> Box { let inner = self.inner.read(); - VeilidStateConfig { + Box::new(VeilidStateConfig { config: inner.clone(), - } + }) } pub fn get(&self) -> RwLockReadGuard { @@ -612,7 +612,7 @@ impl VeilidConfig { // Make changes let out = f(&mut editedinner)?; // Validate - Self::validate(&mut editedinner)?; + Self::validate(&editedinner)?; // See if things have changed if *inner == editedinner { // No changes, return early @@ -626,7 +626,9 @@ impl VeilidConfig { // Send configuration update to clients if let Some(update_cb) = &self.update_cb { let safe_cfg = self.safe_config_inner(); - update_cb(VeilidUpdate::Config(VeilidStateConfig { config: safe_cfg })); + update_cb(VeilidUpdate::Config(Box::new(VeilidStateConfig { + config: safe_cfg, + }))); } Ok(out) @@ -680,7 +682,7 @@ impl VeilidConfig { // Replace subkey let mut out = &mut jvc; for k in objkeypath { - if !out.has_key(*k) { + if !out.has_key(k) { apibail_parse_error!(format!("invalid subkey in key '{}'", key), k); } out = &mut out[*k]; diff --git a/veilid-flutter/rust/src/dart_ffi.rs b/veilid-flutter/rust/src/dart_ffi.rs index 319d4e02..812f8ab9 100644 --- a/veilid-flutter/rust/src/dart_ffi.rs +++ b/veilid-flutter/rust/src/dart_ffi.rs @@ -358,7 +358,7 @@ pub extern "C" fn routing_context(port: i64) { let veilid_api = get_veilid_api().await?; let routing_context = veilid_api.routing_context(); let mut rc = ROUTING_CONTEXTS.lock(); - let new_id = add_routing_context(&mut *rc, routing_context); + let new_id = add_routing_context(&mut rc, routing_context); APIResult::Ok(new_id) }); } @@ -369,7 +369,7 @@ pub extern "C" fn release_routing_context(id: u32) -> i32 { if rc.remove(&id).is_none() { return 0; } - return 1; + 1 } #[no_mangle] @@ -381,8 +381,8 @@ pub extern "C" fn routing_context_with_privacy(id: u32) -> u32 { let Ok(routing_context) = routing_context.clone().with_privacy() else { return 0; }; - let new_id = add_routing_context(&mut rc, routing_context); - new_id + + add_routing_context(&mut rc, routing_context) } #[no_mangle] @@ -397,8 +397,8 @@ pub extern "C" fn routing_context_with_custom_privacy(id: u32, safety_selection: let Ok(routing_context) = routing_context.clone().with_custom_privacy(safety_selection) else { return 0; }; - let new_id = add_routing_context(&mut rc, routing_context); - new_id + + add_routing_context(&mut rc, routing_context) } #[no_mangle] @@ -411,8 +411,8 @@ pub extern "C" fn routing_context_with_sequencing(id: u32, sequencing: FfiStr) - return 0; }; let routing_context = routing_context.clone().with_sequencing(sequencing); - let new_id = add_routing_context(&mut rc, routing_context); - new_id + + add_routing_context(&mut rc, routing_context) } @@ -734,7 +734,7 @@ pub extern "C" fn release_table_db(id: u32) -> i32 { if rc.remove(&id).is_none() { return 0; } - return 1; + 1 } #[no_mangle] @@ -757,7 +757,7 @@ pub extern "C" fn table_db_get_column_count(id: u32) -> u32 { let Ok(cc) = table_db.clone().get_column_count() else { return 0; }; - return cc; + cc } #[no_mangle] @@ -794,8 +794,8 @@ pub extern "C" fn table_db_transact(id: u32) -> u32 { return 0; }; let tdbt = table_db.clone().transact(); - let tdbtid = add_table_db_transaction(tdbt); - return tdbtid; + + add_table_db_transaction(tdbt) } #[no_mangle] @@ -804,7 +804,7 @@ pub extern "C" fn release_table_db_transaction(id: u32) -> i32 { if tdbts.remove(&id).is_none() { return 0; } - return 1; + 1 } diff --git a/veilid-flutter/rust/src/lib.rs b/veilid-flutter/rust/src/lib.rs index 2121c5ce..b844dd3a 100644 --- a/veilid-flutter/rust/src/lib.rs +++ b/veilid-flutter/rust/src/lib.rs @@ -1,3 +1,6 @@ +#![deny(clippy::all)] +#![allow(clippy::comparison_chain, clippy::upper_case_acronyms)] +#![deny(unused_must_use)] #![recursion_limit = "256"] mod dart_ffi; diff --git a/veilid-server/src/client_api.rs b/veilid-server/src/client_api.rs index c12ad8c7..68ab0cb7 100644 --- a/veilid-server/src/client_api.rs +++ b/veilid-server/src/client_api.rs @@ -150,7 +150,7 @@ impl ClientApi { // Process control messages for the server async fn process_control(self, args: Vec) -> VeilidAPIResult { - if args.len() == 0 { + if args.is_empty() { apibail_generic!("no control request specified"); } if args[0] == "Shutdown" { @@ -207,7 +207,7 @@ impl ClientApi { // Avoid logging failed deserialization of large adversarial payloads from // http://127.0.0.1:5959 by using an initial colon to force a parse error. - let sanitized_line = if line.len() > MAX_NON_JSON_LOGGING && !line.starts_with("{") { + let sanitized_line = if line.len() > MAX_NON_JSON_LOGGING && !line.starts_with('{') { ":skipped long input that's not a JSON object".to_string() } else { line.to_string() @@ -265,7 +265,7 @@ impl ClientApi { linebuf.clear(); // Ignore newlines - if line.len() == 0 { + if line.is_empty() { continue; } @@ -289,7 +289,7 @@ impl ClientApi { mut writer: W, ) -> VeilidAPIResult> { while let Ok(resp) = responses_rx.recv_async().await { - if let Err(_) = writer.write_all(resp.as_bytes()).await { + if (writer.write_all(resp.as_bytes()).await).is_err() { break; } } @@ -420,7 +420,7 @@ impl ClientApi { // Pass other updates to clients let inner = self.inner.lock(); for ch in inner.update_channels.values() { - if let Err(_) = ch.send(veilid_update.clone()) { + if ch.send(veilid_update.clone()).is_err() { // eprintln!("failed to send update: {}", e); } } diff --git a/veilid-server/src/main.rs b/veilid-server/src/main.rs index 2c789bbc..252ba2d4 100644 --- a/veilid-server/src/main.rs +++ b/veilid-server/src/main.rs @@ -1,5 +1,6 @@ #![forbid(unsafe_code)] #![deny(clippy::all)] +#![allow(clippy::comparison_chain, clippy::upper_case_acronyms)] #![deny(unused_must_use)] #![recursion_limit = "256"] @@ -19,7 +20,7 @@ use clap::{Args, Parser}; use server::*; use settings::LogLevel; use std::collections::HashMap; -use std::ffi::{OsStr, OsString}; +use std::ffi::OsString; use std::path::Path; use std::str::FromStr; use tools::*; @@ -161,17 +162,11 @@ fn main() -> EyreResult<()> { } // Attempt to load configuration - let settings_path: Option<&OsStr> = if let Some(config_file) = &args.config_file { - if Path::new(&config_file).exists() { - Some(config_file) - } else { - None - } - } else { - None - }; + let settings_path: Option = args + .config_file + .filter(|config_file| Path::new(&config_file).exists()); - let settings = Settings::new(settings_path).wrap_err("configuration is invalid")?; + let settings = Settings::new(settings_path.as_deref()).wrap_err("configuration is invalid")?; // write lock the settings let mut settingsrw = settings.write(); @@ -302,7 +297,7 @@ fn main() -> EyreResult<()> { // --- Generate DHT Key --- if let Some(ckstr) = args.generate_key_pair { - if ckstr == "" { + if ckstr.is_empty() { let mut tks = veilid_core::TypedKeyGroup::new(); let mut tss = veilid_core::TypedSecretGroup::new(); for ck in veilid_core::VALID_CRYPTO_KINDS { @@ -311,16 +306,12 @@ fn main() -> EyreResult<()> { tks.add(veilid_core::TypedKey::new(tkp.kind, tkp.value.key)); tss.add(veilid_core::TypedSecret::new(tkp.kind, tkp.value.secret)); } - println!( - "Public Keys:\n{}\nSecret Keys:\n{}\n", - tks.to_string(), - tss.to_string() - ); + println!("Public Keys:\n{}\nSecret Keys:\n{}\n", tks, tss); } else { let ck: veilid_core::CryptoKind = veilid_core::FourCC::from_str(&ckstr).wrap_err("couldn't parse crypto kind")?; let tkp = veilid_core::Crypto::generate_keypair(ck).wrap_err("invalid crypto kind")?; - println!("{}", tkp.to_string()); + println!("{}", tkp); } return Ok(()); } diff --git a/veilid-server/src/server.rs b/veilid-server/src/server.rs index e918fc2f..8cfbe032 100644 --- a/veilid-server/src/server.rs +++ b/veilid-server/src/server.rs @@ -48,7 +48,19 @@ pub async fn run_veilid_server_internal( ) -> EyreResult<()> { trace!(?settings, ?server_mode); - let settingsr = settings.read(); + let ( + settings_auto_attach, + settings_client_api_enabled, + settings_client_api_listen_address_addrs, + ) = { + let settingsr = settings.read(); + + ( + settingsr.auto_attach, + settingsr.client_api.enabled, + settingsr.client_api.listen_address.addrs.clone(), + ) + }; // Create client api state change pipe let (sender, receiver): ( @@ -72,20 +84,19 @@ pub async fn run_veilid_server_internal( .wrap_err("VeilidCore startup failed")?; // Start client api if one is requested - let mut capi = if settingsr.client_api.enabled && matches!(server_mode, ServerMode::Normal) { + let mut capi = if settings_client_api_enabled && matches!(server_mode, ServerMode::Normal) { let some_capi = client_api::ClientApi::new(veilid_api.clone(), veilid_logs.clone(), settings.clone()); some_capi .clone() - .run(settingsr.client_api.listen_address.addrs.clone()); + .run(settings_client_api_listen_address_addrs); Some(some_capi) } else { None }; // Drop rwlock on settings - let auto_attach = settingsr.auto_attach || !matches!(server_mode, ServerMode::Normal); - drop(settingsr); + let auto_attach = settings_auto_attach || !matches!(server_mode, ServerMode::Normal); // Process all updates let capi2 = capi.clone(); diff --git a/veilid-server/src/settings.rs b/veilid-server/src/settings.rs index 498882fd..abc73335 100644 --- a/veilid-server/src/settings.rs +++ b/veilid-server/src/settings.rs @@ -1,5 +1,3 @@ -#![allow(clippy::bool_assert_comparison)] - use clap::ValueEnum; use directories::*; @@ -714,62 +712,62 @@ impl Settings { } // bump client api port - (*settingsrw).client_api.listen_address.offset_port(idx)?; + settingsrw.client_api.listen_address.offset_port(idx)?; // bump protocol ports - (*settingsrw) + settingsrw .core .network .protocol .udp .listen_address .offset_port(idx)?; - (*settingsrw) + settingsrw .core .network .protocol .tcp .listen_address .offset_port(idx)?; - (*settingsrw) + settingsrw .core .network .protocol .ws .listen_address .offset_port(idx)?; - if let Some(url) = &mut (*settingsrw).core.network.protocol.ws.url { + if let Some(url) = &mut settingsrw.core.network.protocol.ws.url { url.offset_port(idx)?; } - (*settingsrw) + settingsrw .core .network .protocol .wss .listen_address .offset_port(idx)?; - if let Some(url) = &mut (*settingsrw).core.network.protocol.wss.url { + if let Some(url) = &mut settingsrw.core.network.protocol.wss.url { url.offset_port(idx)?; } // bump application ports - (*settingsrw) + settingsrw .core .network .application .http .listen_address .offset_port(idx)?; - if let Some(url) = &mut (*settingsrw).core.network.application.http.url { + if let Some(url) = &mut settingsrw.core.network.application.http.url { url.offset_port(idx)?; } - (*settingsrw) + settingsrw .core .network .application .https .listen_address .offset_port(idx)?; - if let Some(url) = &mut (*settingsrw).core.network.application.https.url { + if let Some(url) = &mut settingsrw.core.network.application.https.url { url.offset_port(idx)?; } Ok(()) @@ -1531,7 +1529,7 @@ mod tests { let settings = Settings::new(None).unwrap(); let s = settings.read(); - assert_eq!(s.daemon.enabled, false); + assert!(!s.daemon.enabled); assert_eq!(s.daemon.pid_file, None); assert_eq!(s.daemon.chroot, None); assert_eq!(s.daemon.working_directory, None); @@ -1539,51 +1537,51 @@ mod tests { assert_eq!(s.daemon.group, None); assert_eq!(s.daemon.stdout_file, None); assert_eq!(s.daemon.stderr_file, None); - assert_eq!(s.client_api.enabled, true); + assert!(s.client_api.enabled); assert_eq!(s.client_api.listen_address.name, "localhost:5959"); assert_eq!( s.client_api.listen_address.addrs, listen_address_to_socket_addrs("localhost:5959").unwrap() ); - assert_eq!(s.auto_attach, true); - assert_eq!(s.logging.system.enabled, false); + assert!(s.auto_attach); + assert!(!s.logging.system.enabled); assert_eq!(s.logging.system.level, LogLevel::Info); - assert_eq!(s.logging.terminal.enabled, true); + assert!(s.logging.terminal.enabled); assert_eq!(s.logging.terminal.level, LogLevel::Info); - assert_eq!(s.logging.file.enabled, false); + assert!(!s.logging.file.enabled); assert_eq!(s.logging.file.path, ""); - assert_eq!(s.logging.file.append, true); + assert!(s.logging.file.append); assert_eq!(s.logging.file.level, LogLevel::Info); - assert_eq!(s.logging.api.enabled, true); + assert!(s.logging.api.enabled); assert_eq!(s.logging.api.level, LogLevel::Info); - assert_eq!(s.logging.otlp.enabled, false); + assert!(!s.logging.otlp.enabled); assert_eq!(s.logging.otlp.level, LogLevel::Trace); assert_eq!( s.logging.otlp.grpc_endpoint, NamedSocketAddrs::from_str("localhost:4317").unwrap() ); - assert_eq!(s.logging.console.enabled, false); + assert!(!s.logging.console.enabled); assert_eq!(s.testing.subnode_index, 0); assert_eq!( s.core.table_store.directory, Settings::get_default_table_store_path() ); - assert_eq!(s.core.table_store.delete, false); + assert!(!s.core.table_store.delete); assert_eq!( s.core.block_store.directory, Settings::get_default_block_store_path() ); - assert_eq!(s.core.block_store.delete, false); + assert!(!s.core.block_store.delete); - assert_eq!(s.core.protected_store.allow_insecure_fallback, true); - assert_eq!(s.core.protected_store.always_use_insecure_storage, true); + assert!(s.core.protected_store.allow_insecure_fallback); + assert!(s.core.protected_store.always_use_insecure_storage); assert_eq!( s.core.protected_store.directory, Settings::get_default_protected_store_directory() ); - assert_eq!(s.core.protected_store.delete, false); + assert!(!s.core.protected_store.delete); assert_eq!(s.core.protected_store.device_encryption_key_password, ""); assert_eq!( s.core.protected_store.new_device_encryption_key_password, @@ -1633,8 +1631,8 @@ mod tests { 2_000u32 ); // - assert_eq!(s.core.network.upnp, true); - assert_eq!(s.core.network.detect_address_changes, true); + assert!(s.core.network.upnp); + assert!(s.core.network.detect_address_changes); assert_eq!(s.core.network.restricted_nat_retries, 0u32); // assert_eq!( @@ -1647,7 +1645,7 @@ mod tests { ); assert_eq!(s.core.network.tls.connection_initial_timeout_ms, 2_000u32); // - assert_eq!(s.core.network.application.https.enabled, false); + assert!(!s.core.network.application.https.enabled); assert_eq!(s.core.network.application.https.listen_address.name, ":443"); assert_eq!( s.core.network.application.https.listen_address.addrs, @@ -1658,7 +1656,7 @@ mod tests { std::path::PathBuf::from("app") ); assert_eq!(s.core.network.application.https.url, None); - assert_eq!(s.core.network.application.http.enabled, false); + assert!(!s.core.network.application.http.enabled); assert_eq!(s.core.network.application.http.listen_address.name, ":80"); assert_eq!( s.core.network.application.http.listen_address.addrs, @@ -1670,23 +1668,23 @@ mod tests { ); assert_eq!(s.core.network.application.http.url, None); // - assert_eq!(s.core.network.protocol.udp.enabled, true); + assert!(s.core.network.protocol.udp.enabled); assert_eq!(s.core.network.protocol.udp.socket_pool_size, 0); assert_eq!(s.core.network.protocol.udp.listen_address.name, ""); assert_eq!(s.core.network.protocol.udp.listen_address.addrs, vec![]); assert_eq!(s.core.network.protocol.udp.public_address, None); // - assert_eq!(s.core.network.protocol.tcp.connect, true); - assert_eq!(s.core.network.protocol.tcp.listen, true); + assert!(s.core.network.protocol.tcp.connect); + assert!(s.core.network.protocol.tcp.listen); assert_eq!(s.core.network.protocol.tcp.max_connections, 32); assert_eq!(s.core.network.protocol.tcp.listen_address.name, ""); assert_eq!(s.core.network.protocol.tcp.listen_address.addrs, vec![]); assert_eq!(s.core.network.protocol.tcp.public_address, None); // - assert_eq!(s.core.network.protocol.ws.connect, true); - assert_eq!(s.core.network.protocol.ws.listen, true); + assert!(s.core.network.protocol.ws.connect); + assert!(s.core.network.protocol.ws.listen); assert_eq!(s.core.network.protocol.ws.max_connections, 32); assert_eq!(s.core.network.protocol.ws.listen_address.name, ""); assert_eq!(s.core.network.protocol.ws.listen_address.addrs, vec![]); @@ -1696,8 +1694,8 @@ mod tests { ); assert_eq!(s.core.network.protocol.ws.url, None); // - assert_eq!(s.core.network.protocol.wss.connect, true); - assert_eq!(s.core.network.protocol.wss.listen, false); + assert!(s.core.network.protocol.wss.connect); + assert!(!s.core.network.protocol.wss.listen); assert_eq!(s.core.network.protocol.wss.max_connections, 32); assert_eq!(s.core.network.protocol.wss.listen_address.name, ""); assert_eq!(s.core.network.protocol.wss.listen_address.addrs, vec![]); diff --git a/veilid-server/src/unix.rs b/veilid-server/src/unix.rs index 0738c7a6..b9ab9dba 100644 --- a/veilid-server/src/unix.rs +++ b/veilid-server/src/unix.rs @@ -1,8 +1,8 @@ -use crate::*; use crate::server::*; use crate::settings::Settings; use crate::tools::*; use crate::veilid_logs::*; +use crate::*; use futures_util::StreamExt; use signal_hook::consts::signal::*; use signal_hook_async_std::Signals; @@ -84,7 +84,7 @@ pub fn run_daemon(settings: Settings, _args: CmdlineArgs) -> EyreResult<()> { // Catch signals let signals = - Signals::new(&[SIGHUP, SIGTERM, SIGINT, SIGQUIT]).wrap_err("failed to init signals")?; + Signals::new([SIGHUP, SIGTERM, SIGINT, SIGQUIT]).wrap_err("failed to init signals")?; let handle = signals.handle(); let signals_task = spawn(handle_signals(signals)); diff --git a/veilid-tools/src/assembly_buffer.rs b/veilid-tools/src/assembly_buffer.rs index 8154edc8..316f86b8 100644 --- a/veilid-tools/src/assembly_buffer.rs +++ b/veilid-tools/src/assembly_buffer.rs @@ -262,7 +262,7 @@ impl AssemblyBuffer { remote_addr: SocketAddr, ) -> NetworkResult>> { // If we receive a zero length frame, send it - if frame.len() == 0 { + if frame.is_empty() { return NetworkResult::value(Some(frame.to_vec())); } @@ -338,10 +338,8 @@ impl AssemblyBuffer { // If we are returning a message, see if there are any more assemblies for this peer // If not, remove the peer - if out.is_some() { - if peer_messages.assemblies.len() == 0 { - e.remove(); - } + if out.is_some() && peer_messages.assemblies.is_empty() { + e.remove(); } NetworkResult::value(out) } @@ -361,7 +359,7 @@ impl AssemblyBuffer { /// Add framing to chunk to send to the wire fn frame_chunk(chunk: &[u8], offset: usize, message_len: usize, seq: SequenceType) -> Vec { - assert!(chunk.len() > 0); + assert!(!chunk.is_empty()); assert!(message_len <= MAX_LEN); assert!(offset + chunk.len() <= message_len); @@ -403,7 +401,7 @@ impl AssemblyBuffer { } // Do not frame or split anything zero bytes long, just send it - if data.len() == 0 { + if data.is_empty() { return sender(data, remote_addr).await; } @@ -432,3 +430,9 @@ impl AssemblyBuffer { Ok(NetworkResult::value(())) } } + +impl Default for AssemblyBuffer { + fn default() -> Self { + Self::new() + } +} diff --git a/veilid-tools/src/async_tag_lock.rs b/veilid-tools/src/async_tag_lock.rs index ec9e8ec3..4375b7ee 100644 --- a/veilid-tools/src/async_tag_lock.rs +++ b/veilid-tools/src/async_tag_lock.rs @@ -91,6 +91,11 @@ where } } + pub fn is_empty(&self) -> bool { + let inner = self.inner.lock(); + inner.table.is_empty() + } + pub fn len(&self) -> usize { let inner = self.inner.lock(); inner.table.len() @@ -154,3 +159,12 @@ where Some(AsyncTagLockGuard::new(self.clone(), tag, guard)) } } + +impl Default for AsyncTagLockTable +where + T: Hash + Eq + Clone + Debug, +{ + fn default() -> Self { + Self::new() + } +} diff --git a/veilid-tools/src/lib.rs b/veilid-tools/src/lib.rs index c824f696..e9cb44b1 100644 --- a/veilid-tools/src/lib.rs +++ b/veilid-tools/src/lib.rs @@ -20,6 +20,9 @@ //! * `rt-async-std` - Uses `async-std` as the async runtime //! * `rt-wasm-bindgen` - When building for the `wasm32` architecture, use this to enable `wasm-bindgen-futures` as the async runtime //! +#![deny(clippy::all)] +#![allow(clippy::comparison_chain, clippy::upper_case_acronyms)] +#![deny(unused_must_use)] // pub mod bump_port; pub mod assembly_buffer; diff --git a/veilid-tools/src/split_url.rs b/veilid-tools/src/split_url.rs index 033ffc96..a9e24bb4 100644 --- a/veilid-tools/src/split_url.rs +++ b/veilid-tools/src/split_url.rs @@ -41,7 +41,7 @@ fn must_encode_path(c: u8) -> bool { fn is_valid_scheme>(host: H) -> bool { let mut chars = host.as_ref().chars(); if let Some(ch) = chars.next() { - if !matches!(ch, 'A'..='Z' | 'a'..='z') { + if !ch.is_ascii_alphabetic() { return false; } } else { diff --git a/veilid-tools/src/timestamp.rs b/veilid-tools/src/timestamp.rs index 94f9871e..2d995180 100644 --- a/veilid-tools/src/timestamp.rs +++ b/veilid-tools/src/timestamp.rs @@ -6,7 +6,7 @@ cfg_if! { pub fn get_timestamp() -> u64 { if is_browser() { - return (Date::now() * 1000.0f64) as u64; + (Date::now() * 1000.0f64) as u64 } else { panic!("WASM requires browser environment"); } @@ -23,28 +23,34 @@ cfg_if! { let show_month = show_year || now.get_utc_month() != date.get_utc_month(); let show_date = show_month || now.get_utc_date() != date.get_utc_date(); + let s_year = if show_year { + format!("{:04}/",date.get_utc_full_year()) + } else { + "".to_owned() + }; + let s_month = if show_month { + format!("{:02}/",date.get_utc_month()) + } else { + "".to_owned() + }; + let s_date = if show_date { + format!("{:02}-",date.get_utc_date()) + } else { + "".to_owned() + }; + let s_time = format!("{:02}:{:02}:{:02}.{:04}", + date.get_utc_hours(), + date.get_utc_minutes(), + date.get_utc_seconds(), + date.get_utc_milliseconds() + ); + format!("{}{}{}{}", - if show_year { - format!("{:04}/",date.get_utc_full_year()) - } else { - "".to_owned() - }, - if show_month { - format!("{:02}/",date.get_utc_month()) - } else { - "".to_owned() - }, - if show_date { - format!("{:02}-",date.get_utc_date()) - } else { - "".to_owned() - }, - format!("{:02}:{:02}:{:02}.{:04}", - date.get_utc_hours(), - date.get_utc_minutes(), - date.get_utc_seconds(), - date.get_utc_milliseconds() - )) + s_year, + s_month, + s_date, + s_time + ) } else { panic!("WASM requires browser environment"); } @@ -68,29 +74,32 @@ cfg_if! { let show_month = show_year || now.month() != date.month(); let show_date = show_month || now.day() != date.day(); + let s_year = if show_year { + format!("{:04}/",date.year()) + } else { + "".to_owned() + }; + let s_month = if show_month { + format!("{:02}/",date.month()) + } else { + "".to_owned() + }; + let s_date = if show_date { + format!("{:02}-",date.day()) + } else { + "".to_owned() + }; + let s_time = format!("{:02}:{:02}:{:02}.{:04}", + date.hour(), + date.minute(), + date.second(), + date.nanosecond()/1_000_000 + ); format!("{}{}{}{}", - if show_year { - format!("{:04}/",date.year()) - } else { - "".to_owned() - }, - if show_month { - format!("{:02}/",date.month()) - } else { - "".to_owned() - }, - if show_date { - format!("{:02}-",date.day()) - } else { - "".to_owned() - }, - format!("{:02}:{:02}:{:02}.{:04}", - date.hour(), - date.minute(), - date.second(), - date.nanosecond()/1_000_000 - )) - + s_year, + s_month, + s_date, + s_time) } } } diff --git a/veilid-tools/src/tools.rs b/veilid-tools/src/tools.rs index 648da9b2..9543f9aa 100644 --- a/veilid-tools/src/tools.rs +++ b/veilid-tools/src/tools.rs @@ -242,7 +242,7 @@ pub fn compatible_unspecified_socket_addr(socket_addr: &SocketAddr) -> SocketAdd pub fn listen_address_to_socket_addrs(listen_address: &str) -> Result, String> { // If no address is specified, but the port is, use ipv4 and ipv6 unspecified // If the address is specified, only use the specified port and fail otherwise - let ip_addrs = vec![ + let ip_addrs = [ IpAddr::V4(Ipv4Addr::UNSPECIFIED), IpAddr::V6(Ipv6Addr::UNSPECIFIED), ]; @@ -335,6 +335,8 @@ cfg_if::cfg_if! { #[repr(C, align(8))] struct AlignToEight([u8; 8]); +/// # Safety +/// Ensure you immediately initialize this vector as it could contain sensitive data pub unsafe fn aligned_8_u8_vec_uninit(n_bytes: usize) -> Vec { let n_units = (n_bytes + mem::size_of::() - 1) / mem::size_of::(); let mut aligned: Vec = Vec::with_capacity(n_units); @@ -349,12 +351,14 @@ pub unsafe fn aligned_8_u8_vec_uninit(n_bytes: usize) -> Vec { ) } +/// # Safety +/// Ensure you immediately initialize this vector as it could contain sensitive data pub unsafe fn unaligned_u8_vec_uninit(n_bytes: usize) -> Vec { let mut unaligned: Vec = Vec::with_capacity(n_bytes); let ptr = unaligned.as_mut_ptr(); mem::forget(unaligned); - Vec::from_raw_parts(ptr as *mut u8, n_bytes, n_bytes) + Vec::from_raw_parts(ptr, n_bytes, n_bytes) } pub fn debug_backtrace() -> String { diff --git a/veilid-tools/src/wasm.rs b/veilid-tools/src/wasm.rs index 1a54aee5..187112ca 100644 --- a/veilid-tools/src/wasm.rs +++ b/veilid-tools/src/wasm.rs @@ -21,7 +21,7 @@ pub fn is_browser() -> bool { return cache != 0; } - let res = Reflect::has(&global().as_ref(), &"navigator".into()).unwrap_or_default(); + let res = Reflect::has(global().as_ref(), &"navigator".into()).unwrap_or_default(); CACHE.store(res as i8, Ordering::Relaxed); @@ -45,7 +45,7 @@ pub fn is_browser_https() -> bool { } pub fn get_wasm_global_string_value>(key: K) -> Option { - let Ok(v) = Reflect::get(&global().as_ref(), &JsValue::from_str(key.as_ref())) else { + let Ok(v) = Reflect::get(global().as_ref(), &JsValue::from_str(key.as_ref())) else { return None; }; v.as_string() diff --git a/veilid-wasm/src/lib.rs b/veilid-wasm/src/lib.rs index fa87391c..1521640c 100644 --- a/veilid-wasm/src/lib.rs +++ b/veilid-wasm/src/lib.rs @@ -1,5 +1,7 @@ // wasm-bindgen and clippy don't play well together yet -#![allow(clippy::all)] +#![deny(clippy::all)] +#![allow(clippy::comparison_chain, clippy::upper_case_acronyms)] +#![deny(unused_must_use)] #![cfg(target_arch = "wasm32")] #![no_std] @@ -77,7 +79,7 @@ pub fn unmarshall(b64: String) -> APIResult> { }) } -pub fn marshall(data: &Vec) -> String { +pub fn marshall(data: &[u8]) -> String { data_encoding::BASE64URL_NOPAD.encode(data) } @@ -114,7 +116,7 @@ where F: Future> + 'static, T: Serialize + Debug + 'static, { - future_to_promise(future.map(|res| res.map(|v| to_json(v)).map_err(|e| to_json(e)))) + future_to_promise(future.map(|res| res.map(|v| to_json(v)).map_err(to_json))) } pub fn wrap_api_future_plain(future: F) -> Promise @@ -123,14 +125,14 @@ where JsValue: From, T: 'static, { - future_to_promise(future.map(|res| res.map(|v| to_jsvalue(v)).map_err(|e| to_json(e)))) + future_to_promise(future.map(|res| res.map(|v| to_jsvalue(v)).map_err(to_json))) } pub fn wrap_api_future_void(future: F) -> Promise where F: Future> + 'static, { - future_to_promise(future.map(|res| res.map(|_| JsValue::UNDEFINED).map_err(|e| to_json(e)))) + future_to_promise(future.map(|res| res.map(|_| JsValue::UNDEFINED).map_err(to_json))) } ///////////////////////////////////////// @@ -338,7 +340,7 @@ pub fn release_routing_context(id: u32) -> i32 { if rc.remove(&id).is_none() { return 0; } - return 1; + 1 } #[wasm_bindgen()] @@ -350,8 +352,7 @@ pub fn routing_context_with_privacy(id: u32) -> u32 { let Ok(routing_context) = routing_context.clone().with_privacy() else { return 0; }; - let new_id = add_routing_context(routing_context); - new_id + add_routing_context(routing_context) } #[wasm_bindgen()] @@ -363,11 +364,13 @@ pub fn routing_context_with_custom_privacy(id: u32, safety_selection: String) -> let Some(routing_context) = rc.get(&id) else { return 0; }; - let Ok(routing_context) = routing_context.clone().with_custom_privacy(safety_selection) else { + let Ok(routing_context) = routing_context + .clone() + .with_custom_privacy(safety_selection) + else { return 0; }; - let new_id = add_routing_context(routing_context); - new_id + add_routing_context(routing_context) } #[wasm_bindgen()] @@ -379,8 +382,7 @@ pub fn routing_context_with_sequencing(id: u32, sequencing: String) -> u32 { return 0; }; let routing_context = routing_context.clone().with_sequencing(sequencing); - let new_id = add_routing_context(routing_context); - new_id + add_routing_context(routing_context) } #[wasm_bindgen()] @@ -392,7 +394,11 @@ pub fn routing_context_app_call(id: u32, target_string: String, request: String) let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_app_call", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "routing_context_app_call", + "id", + id, + )); }; routing_context.clone() }; @@ -414,7 +420,11 @@ pub fn routing_context_app_message(id: u32, target_string: String, message: Stri let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_app_message", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "routing_context_app_message", + "id", + id, + )); }; routing_context.clone() }; @@ -439,7 +449,11 @@ pub fn routing_context_create_dht_record(id: u32, schema: String, kind: u32) -> let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_create_dht_record", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "routing_context_create_dht_record", + "id", + id, + )); }; routing_context.clone() }; @@ -460,7 +474,11 @@ pub fn routing_context_open_dht_record(id: u32, key: String, writer: Option Promise { let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_close_dht_record", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "routing_context_close_dht_record", + "id", + id, + )); }; routing_context.clone() }; @@ -492,7 +514,11 @@ pub fn routing_context_delete_dht_record(id: u32, key: String) -> Promise { let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_delete_dht_record", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "routing_context_delete_dht_record", + "id", + id, + )); }; routing_context.clone() }; @@ -513,7 +539,11 @@ pub fn routing_context_get_dht_value( let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_get_dht_value", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "routing_context_get_dht_value", + "id", + id, + )); }; routing_context.clone() }; @@ -528,14 +558,18 @@ pub fn routing_context_get_dht_value( pub fn routing_context_set_dht_value(id: u32, key: String, subkey: u32, data: String) -> Promise { let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap(); let data: Vec = data_encoding::BASE64URL_NOPAD - .decode(&data.as_bytes()) + .decode(data.as_bytes()) .unwrap(); wrap_api_future_json(async move { let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_set_dht_value", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "routing_context_set_dht_value", + "id", + id, + )); }; routing_context.clone() }; @@ -561,7 +595,11 @@ pub fn routing_context_watch_dht_values( let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_watch_dht_values", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "routing_context_watch_dht_values", + "id", + id, + )); }; routing_context.clone() }; @@ -582,7 +620,11 @@ pub fn routing_context_cancel_dht_watch(id: u32, key: String, subkeys: String) - let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); let Some(routing_context) = rc.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_cancel_dht_watch", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "routing_context_cancel_dht_watch", + "id", + id, + )); }; routing_context.clone() }; @@ -696,7 +738,7 @@ pub fn release_table_db(id: u32) -> i32 { if tdbs.remove(&id).is_none() { return 0; } - return 1; + 1 } #[wasm_bindgen()] @@ -721,7 +763,7 @@ pub fn table_db_get_column_count(id: u32) -> u32 { let Ok(cc) = table_db.clone().get_column_count() else { return 0; }; - return cc; + cc } #[wasm_bindgen()] @@ -730,7 +772,11 @@ pub fn table_db_get_keys(id: u32, col: u32) -> Promise { let table_db = { let table_dbs = (*TABLE_DBS).borrow(); let Some(table_db) = table_dbs.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_store", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "table_db_store", + "id", + id, + )); }; table_db.clone() }; @@ -761,8 +807,7 @@ pub fn table_db_transact(id: u32) -> u32 { return 0; }; let tdbt = table_db.clone().transact(); - let tdbtid = add_table_db_transaction(tdbt); - return tdbtid; + add_table_db_transaction(tdbt) } #[wasm_bindgen()] @@ -771,7 +816,7 @@ pub fn release_table_db_transaction(id: u32) -> i32 { if tdbts.remove(&id).is_none() { return 0; } - return 1; + 1 } #[wasm_bindgen()] @@ -780,7 +825,11 @@ pub fn table_db_transaction_commit(id: u32) -> Promise { let tdbt = { let tdbts = (*TABLE_DB_TRANSACTIONS).borrow(); let Some(tdbt) = tdbts.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_transaction_commit", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "table_db_transaction_commit", + "id", + id, + )); }; tdbt.clone() }; @@ -796,7 +845,11 @@ pub fn table_db_transaction_rollback(id: u32) -> Promise { let tdbt = { let tdbts = (*TABLE_DB_TRANSACTIONS).borrow(); let Some(tdbt) = tdbts.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_transaction_rollback", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "table_db_transaction_rollback", + "id", + id, + )); }; tdbt.clone() }; @@ -818,7 +871,11 @@ pub fn table_db_transaction_store(id: u32, col: u32, key: String, value: String) let tdbt = { let tdbts = (*TABLE_DB_TRANSACTIONS).borrow(); let Some(tdbt) = tdbts.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_transaction_store", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "table_db_transaction_store", + "id", + id, + )); }; tdbt.clone() }; @@ -837,7 +894,11 @@ pub fn table_db_transaction_delete(id: u32, col: u32, key: String) -> Promise { let tdbt = { let tdbts = (*TABLE_DB_TRANSACTIONS).borrow(); let Some(tdbt) = tdbts.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_transaction_delete", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "table_db_transaction_delete", + "id", + id, + )); }; tdbt.clone() }; @@ -859,7 +920,11 @@ pub fn table_db_store(id: u32, col: u32, key: String, value: String) -> Promise let table_db = { let table_dbs = (*TABLE_DBS).borrow(); let Some(table_db) = table_dbs.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_store", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "table_db_store", + "id", + id, + )); }; table_db.clone() }; @@ -878,7 +943,11 @@ pub fn table_db_load(id: u32, col: u32, key: String) -> Promise { let table_db = { let table_dbs = (*TABLE_DBS).borrow(); let Some(table_db) = table_dbs.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_load", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "table_db_load", + "id", + id, + )); }; table_db.clone() }; @@ -898,7 +967,11 @@ pub fn table_db_delete(id: u32, col: u32, key: String) -> Promise { let table_db = { let table_dbs = (*TABLE_DBS).borrow(); let Some(table_db) = table_dbs.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_delete", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "table_db_delete", + "id", + id, + )); }; table_db.clone() }; diff --git a/veilid-wasm/src/veilid_client_js.rs b/veilid-wasm/src/veilid_client_js.rs index e89103cb..2b2319b7 100644 --- a/veilid-wasm/src/veilid_client_js.rs +++ b/veilid-wasm/src/veilid_client_js.rs @@ -163,12 +163,11 @@ impl VeilidClient { /// Return the cargo package version of veilid-core, in object format. pub fn version() -> VeilidVersion { let (major, minor, patch) = veilid_core::veilid_version(); - let vv = super::VeilidVersion { + super::VeilidVersion { major, minor, patch, - }; - vv + } } /// Return the cargo package version of veilid-core, in string format. diff --git a/veilid-wasm/src/veilid_crypto_js.rs b/veilid-wasm/src/veilid_crypto_js.rs index e12a1978..55f4557d 100644 --- a/veilid-wasm/src/veilid_crypto_js.rs +++ b/veilid-wasm/src/veilid_crypto_js.rs @@ -411,7 +411,7 @@ impl VeilidCrypto { veilid_core::SharedSecret::from_str(&shared_secret)?; let associated_data = associated_data - .map(|ad| unmarshall(ad)) + .map(unmarshall) .map_or(APIResult::Ok(None), |r| r.map(Some))?; let veilid_api = get_veilid_api()?; @@ -453,7 +453,7 @@ impl VeilidCrypto { veilid_core::SharedSecret::from_str(&shared_secret)?; let associated_data: Option> = associated_data - .map(|ad| unmarshall(ad)) + .map(unmarshall) .map_or(APIResult::Ok(None), |r| r.map(Some))?; let veilid_api = get_veilid_api()?; diff --git a/veilid-wasm/src/veilid_table_db_js.rs b/veilid-wasm/src/veilid_table_db_js.rs index f1fb86c3..181a43ef 100644 --- a/veilid-wasm/src/veilid_table_db_js.rs +++ b/veilid-wasm/src/veilid_table_db_js.rs @@ -23,7 +23,9 @@ impl VeilidTableDB { fn getTableDB(&self) -> APIResult { let Some(table_db) = &self.inner_table_db else { - return APIResult::Err(veilid_core::VeilidAPIError::generic("Unable to getTableDB instance. Ensure you've called openTable().")); + return APIResult::Err(veilid_core::VeilidAPIError::generic( + "Unable to getTableDB instance. Ensure you've called openTable().", + )); }; APIResult::Ok(table_db.clone()) } @@ -140,7 +142,9 @@ impl VeilidTableDBTransaction { fn getTransaction(&self) -> APIResult { let Some(transaction) = &self.inner_transaction else { - return APIResult::Err(veilid_core::VeilidAPIError::generic("Unable to getTransaction instance. inner_transaction is None.")); + return APIResult::Err(veilid_core::VeilidAPIError::generic( + "Unable to getTransaction instance. inner_transaction is None.", + )); }; APIResult::Ok(transaction.clone()) } @@ -174,3 +178,9 @@ impl VeilidTableDBTransaction { transaction.delete(col, &key) } } + +impl Default for VeilidTableDBTransaction { + fn default() -> Self { + Self::new() + } +}