diff --git a/Cargo.lock b/Cargo.lock index 922ba9ec..da286bf4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1241,6 +1241,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "ctor" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e366bff8cd32dd8754b0991fb66b279dc48f598c3a18914852a6673deef583" +dependencies = [ + "quote", + "syn 2.0.38", +] + [[package]] name = "ctrlc" version = "3.4.1" @@ -2684,6 +2694,15 @@ version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +[[package]] +name = "libc-print" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17f111e2175c779daaf5e89fe3a3b0776b0adec218bc1159c56e4d3f58032f5" +dependencies = [ + "libc", +] + [[package]] name = "libloading" version = "0.7.4" @@ -5518,18 +5537,22 @@ name = "veilid-flutter" version = "0.2.4" dependencies = [ "allo-isolate", + "android_log-sys 0.3.1", "async-std", "backtrace", "cfg-if 1.0.0", + "ctor", "data-encoding", "ffi-support", "futures-util", "hostname", "jni", "lazy_static", + "libc-print", "opentelemetry", "opentelemetry-otlp", "opentelemetry-semantic-conventions", + "oslog", "paranoid-android", "parking_lot 0.12.1", "serde", diff --git a/veilid-core/src/network_manager/native/network_class_discovery.rs b/veilid-core/src/network_manager/native/network_class_discovery.rs index e0bdf9ec..2c6c59bd 100644 --- a/veilid-core/src/network_manager/native/network_class_discovery.rs +++ b/veilid-core/src/network_manager/native/network_class_discovery.rs @@ -50,7 +50,11 @@ impl Network { let mut add = false; if let Some(edi) = existing_dial_info.get(&(pt, at)) { - if did.class <= edi.class { + // Is the dial info class better than our existing dial info? + // Or is the new dial info the same class, but different? Only change if things are different. + if did.class < edi.class + || (did.class == edi.class && did.dial_info != edi.dial_info) + { // Better or same dial info class was found, clear existing dialinfo for this pt/at pair // Only keep one dial info per protocol/address type combination clear = true; diff --git a/veilid-core/src/routing_table/bucket_entry.rs b/veilid-core/src/routing_table/bucket_entry.rs index 2630b034..5f5d40cb 100644 --- a/veilid-core/src/routing_table/bucket_entry.rs +++ b/veilid-core/src/routing_table/bucket_entry.rs @@ -924,7 +924,7 @@ impl BucketEntry { impl Drop for BucketEntry { fn drop(&mut self) { - if self.ref_count.load(Ordering::Relaxed) != 0 { + if self.ref_count.load(Ordering::Acquire) != 0 { #[cfg(feature = "tracking")] { println!("NodeRef Tracking"); diff --git a/veilid-core/src/routing_table/node_ref.rs b/veilid-core/src/routing_table/node_ref.rs index 939d54a7..798f2b5a 100644 --- a/veilid-core/src/routing_table/node_ref.rs +++ b/veilid-core/src/routing_table/node_ref.rs @@ -379,7 +379,7 @@ impl NodeRef { entry: Arc, filter: Option, ) -> Self { - entry.ref_count.fetch_add(1u32, Ordering::Relaxed); + entry.ref_count.fetch_add(1u32, Ordering::AcqRel); Self { common: NodeRefBaseCommon { @@ -438,7 +438,7 @@ impl Clone for NodeRef { self.common .entry .ref_count - .fetch_add(1u32, Ordering::Relaxed); + .fetch_add(1u32, Ordering::AcqRel); Self { common: NodeRefBaseCommon { @@ -479,7 +479,7 @@ impl Drop for NodeRef { .common .entry .ref_count - .fetch_sub(1u32, Ordering::Relaxed) + .fetch_sub(1u32, Ordering::AcqRel) - 1; if new_ref_count == 0 { // get node ids with inner unlocked because nothing could be referencing this entry now 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 93ecca12..35f825e2 100644 --- a/veilid-core/src/routing_table/route_spec_store/mod.rs +++ b/veilid-core/src/routing_table/route_spec_store/mod.rs @@ -151,20 +151,21 @@ impl RouteSpecStore { } /// Purge the route spec store - pub async fn purge(&self) -> EyreResult<()> { + pub async fn purge(&self) -> VeilidAPIResult<()> { { let inner = &mut *self.inner.lock(); inner.content = Default::default(); inner.cache = Default::default(); } - self.save().await + self.save().await.map_err(VeilidAPIError::internal) } /// 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 + /// Returns Err(VeilidAPIError::TryAgain) if no route could be allocated at this time + /// Returns other errors on failure + /// Returns Ok(route id string) on success #[instrument(level = "trace", skip(self), ret, err)] pub fn allocate_route( &self, @@ -174,7 +175,7 @@ impl RouteSpecStore { hop_count: usize, directions: DirectionSet, avoid_nodes: &[TypedKey], - ) -> EyreResult> { + ) -> VeilidAPIResult { let inner = &mut *self.inner.lock(); let routing_table = self.unlocked_inner.routing_table.clone(); let rti = &mut *routing_table.inner.write(); @@ -203,21 +204,30 @@ impl RouteSpecStore { hop_count: usize, directions: DirectionSet, avoid_nodes: &[TypedKey], - ) -> EyreResult> { + ) -> VeilidAPIResult { use core::cmp::Ordering; if hop_count < 1 { - bail!("Not allocating route less than one hop in length"); + apibail_invalid_argument!( + "Not allocating route less than one hop in length", + "hop_count", + hop_count + ); } if hop_count > self.unlocked_inner.max_route_hop_count { - bail!("Not allocating route longer than max route hop count"); + apibail_invalid_argument!( + "Not allocating route longer than max route hop count", + "hop_count", + 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); + apibail_try_again!( + "unable to allocate route until we have a valid PublicInternet network class" + ); }; // Get our peer info @@ -369,8 +379,7 @@ impl RouteSpecStore { // 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); + apibail_try_again!("not enough nodes to construct route at this time"); } // Get peer info for everything @@ -522,8 +531,7 @@ impl RouteSpecStore { } } if route_nodes.is_empty() { - log_rtab!(debug "unable to find unique route at this time"); - return Ok(None); + apibail_try_again!("unable to find unique route at this time"); } drop(perm_func); @@ -579,7 +587,7 @@ impl RouteSpecStore { // Keep route in spec store inner.content.add_detail(id, rssd); - Ok(Some(id)) + Ok(id) } /// validate data using a private route's key and signature chain @@ -651,17 +659,21 @@ impl RouteSpecStore { feature = "verbose-tracing", instrument(level = "trace", skip(self), ret, err) )] - async fn test_allocated_route(&self, private_route_id: RouteId) -> EyreResult { + async fn test_allocated_route(&self, private_route_id: RouteId) -> VeilidAPIResult { // 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"); + apibail_invalid_argument!( + "route id not allocated", + "private_route_id", + private_route_id + ); }; let Some(key) = rssd.get_best_route_set_key() else { - bail!("no best key to test allocated route"); + apibail_internal!("no best key to test allocated route"); }; // Match the private route's hop length for safety route length let hop_count = rssd.hop_count(); @@ -703,12 +715,12 @@ impl RouteSpecStore { } #[instrument(level = "trace", skip(self), ret, err)] - async fn test_remote_route(&self, private_route_id: RouteId) -> EyreResult { + async fn test_remote_route(&self, private_route_id: RouteId) -> VeilidAPIResult { // 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"); + apibail_internal!("no best key to test remote route"); }; // Always test routes with safety routes that are more likely to succeed @@ -777,7 +789,7 @@ impl RouteSpecStore { feature = "verbose-tracing", instrument(level = "trace", skip(self), ret, err) )] - pub async fn test_route(&self, id: RouteId) -> EyreResult { + pub async fn test_route(&self, id: RouteId) -> VeilidAPIResult { let is_remote = self.is_route_id_remote(&id); if is_remote { self.test_remote_route(id).await @@ -904,13 +916,14 @@ impl RouteSpecStore { } /// 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) + /// Returns Err(VeilidAPIError::TryAgain) if no allocation could happen at this time (not an error) + /// Returns other Err() if the parameters are wrong + /// Returns Ok(compiled route) on success pub fn compile_safety_route( &self, safety_selection: SafetySelection, mut private_route: PrivateRoute, - ) -> EyreResult> { + ) -> VeilidAPIResult { // let profile_start_ts = get_timestamp(); let inner = &mut *self.inner.lock(); let routing_table = self.unlocked_inner.routing_table.clone(); @@ -920,7 +933,7 @@ impl RouteSpecStore { 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"); + apibail_generic!("crypto not supported for route"); }; let pr_pubkey = private_route.public_key.value; let pr_hopcount = private_route.hop_count as usize; @@ -928,7 +941,11 @@ impl RouteSpecStore { // 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"); + apibail_invalid_argument!( + "private route hop count too long", + "private_route.hop_count", + pr_hopcount + ); } // See if we are using a safety route, if not, short circuit this operation let safety_spec = match safety_selection { @@ -937,24 +954,26 @@ impl RouteSpecStore { // 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"); + apibail_generic!("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, - )?), + RouteNode::NodeId(id) => rti + .lookup_node_ref(routing_table.clone(), TypedKey::new(crypto_kind, id)) + .map_err(VeilidAPIError::internal)?, + RouteNode::PeerInfo(pi) => Some( + rti.register_node_with_peer_info( + routing_table.clone(), + RoutingDomain::PublicInternet, + *pi, + false, + ) + .map_err(VeilidAPIError::internal)?, + ), }; 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); + apibail_generic!("can't reach private route any more"); } let mut first_hop = opt_first_hop.unwrap(); @@ -963,14 +982,14 @@ impl RouteSpecStore { // Return the compiled safety route //println!("compile_safety_route profile (stub): {} us", (get_timestamp() - profile_start_ts)); - return Ok(Some(CompiledRoute { + return Ok(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, - })); + }); } }; @@ -983,9 +1002,9 @@ impl RouteSpecStore { pr_pubkey } else { let Some(avoid_node_id) = private_route.first_hop_node_id() else { - bail!("compiled private route should have first hop"); + apibail_generic!("compiled private route should have first hop"); }; - let Some(sr_pubkey) = self.get_route_for_safety_spec_inner( + self.get_route_for_safety_spec_inner( inner, rti, crypto_kind, @@ -993,22 +1012,17 @@ impl RouteSpecStore { 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"); + apibail_generic!("safety route id missing"); }; let Some(safety_rssd) = inner.content.get_detail(&safety_route_id) else { - bail!("route set detail missing"); + apibail_internal!("safety route set detail missing"); }; let Some(safety_rsd) = safety_rssd.get_route_by_key(&sr_pubkey) else { - bail!("route detail missing"); + apibail_internal!("safety route detail missing"); }; // We can optimize the peer info in this safety route if it has been successfully @@ -1040,7 +1054,7 @@ impl RouteSpecStore { }; // Return compiled route //println!("compile_safety_route profile (cached): {} us", (get_timestamp() - profile_start_ts)); - return Ok(Some(compiled_route)); + return Ok(compiled_route); } } @@ -1071,10 +1085,10 @@ impl RouteSpecStore { // 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")?; + .map_err(VeilidAPIError::internal)?; let enc_msg_data = vcrypto .encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None) - .wrap_err("encryption failed")?; + .map_err(VeilidAPIError::internal)?; // Make route hop data let route_hop_data = RouteHopData { @@ -1098,7 +1112,7 @@ impl RouteSpecStore { }) .flatten(); if pi.is_none() { - bail!("peer info should exist for route but doesn't"); + apibail_internal!("peer info should exist for route but doesn't"); } RouteNode::PeerInfo(Box::new(pi.unwrap())) }, @@ -1123,10 +1137,10 @@ impl RouteSpecStore { // Encode first RouteHopData let dh_secret = vcrypto .cached_dh(&safety_rsd.hops[0], &safety_rsd.secret_key) - .map_err(RPCError::map_internal("dh failed"))?; + .map_err(VeilidAPIError::internal)?; let enc_msg_data = vcrypto .encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None) - .map_err(RPCError::map_internal("encryption failed"))?; + .map_err(VeilidAPIError::internal)?; let route_hop_data = RouteHopData { nonce, @@ -1159,7 +1173,7 @@ impl RouteSpecStore { // Return compiled route //println!("compile_safety_route profile (uncached): {} us", (get_timestamp() - profile_start_ts)); - Ok(Some(compiled_route)) + Ok(compiled_route) } /// Get an allocated route that matches a particular safety spec @@ -1175,14 +1189,22 @@ impl RouteSpecStore { safety_spec: &SafetySpec, direction: DirectionSet, avoid_nodes: &[TypedKey], - ) -> EyreResult> { + ) -> VeilidAPIResult { // 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"); + apibail_invalid_argument!( + "safety route hop count is zero", + "safety_spec.hop_count", + safety_spec.hop_count + ); } if safety_spec.hop_count > max_route_hop_count { - bail!("safety route hop count too long"); + apibail_invalid_argument!( + "safety route hop count too long", + "safety_spec.hop_count", + safety_spec.hop_count + ); } // See if the preferred route is here @@ -1192,7 +1214,7 @@ impl RouteSpecStore { 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)); + return Ok(preferred_key.value); } } } @@ -1213,22 +1235,16 @@ impl RouteSpecStore { 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 + self.allocate_route_inner( + inner, + rti, + &[crypto_kind], + safety_spec.stability, + safety_spec.sequencing, + safety_spec.hop_count, + direction, + avoid_nodes, + )? }; let sr_pubkey = inner @@ -1240,7 +1256,7 @@ impl RouteSpecStore { .unwrap() .value; - Ok(Some(sr_pubkey)) + Ok(sr_pubkey) } /// Get a private route to use for the answer to question @@ -1253,7 +1269,7 @@ impl RouteSpecStore { crypto_kind: CryptoKind, safety_spec: &SafetySpec, avoid_nodes: &[TypedKey], - ) -> EyreResult> { + ) -> VeilidAPIResult { let inner = &mut *self.inner.lock(); let routing_table = self.unlocked_inner.routing_table.clone(); let rti = &mut *routing_table.inner.write(); @@ -1273,22 +1289,24 @@ impl RouteSpecStore { key: &PublicKey, rsd: &RouteSpecDetail, optimized: bool, - ) -> EyreResult { + ) -> VeilidAPIResult { 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"); + apibail_invalid_argument!( + "crypto not supported for route", + "rsd.crypto_kind", + rsd.crypto_kind + ); }; // Ensure our network class is valid before attempting to assemble any routes if !rti.has_valid_network_class(RoutingDomain::PublicInternet) { - let peer_info = rti.get_own_peer_info(RoutingDomain::PublicInternet); - bail!( - "can't make private routes until our node info is valid: {:?}", - peer_info + apibail_try_again!( + "unable to assemble route until we have a valid PublicInternet network class" ); } @@ -1296,7 +1314,11 @@ impl RouteSpecStore { 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"); + apibail_invalid_argument!( + "missing node id for crypto kind", + "rsd.crypto_kind", + rsd.crypto_kind + ); }; RouteNode::NodeId(node_id.value) } else { @@ -1320,12 +1342,9 @@ impl RouteSpecStore { }; // 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 dh_secret = vcrypto.cached_dh(&rsd.hops[h], &rsd.secret_key)?; + let enc_msg_data = + vcrypto.encrypt_aead(blob_data.as_slice(), &nonce, &dh_secret, None)?; let route_hop_data = RouteHopData { nonce, blob: enc_msg_data, @@ -1346,7 +1365,7 @@ impl RouteSpecStore { }) .flatten(); if pi.is_none() { - bail!("peer info should exist for route but doesn't",); + apibail_internal!("peer info should exist for route but doesn't"); } RouteNode::PeerInfo(Box::new(pi.unwrap())) }, @@ -1373,13 +1392,13 @@ impl RouteSpecStore { &self, key: &PublicKey, optimized: Option, - ) -> EyreResult { + ) -> VeilidAPIResult { let inner = &*self.inner.lock(); let Some(rsid) = inner.content.get_id_by_key(key) else { - bail!("route key does not exist"); + apibail_invalid_argument!("route key does not exist", "key", key); }; let Some(rssd) = inner.content.get_detail(&rsid) else { - bail!("route id does not exist"); + apibail_internal!("route id does not exist"); }; // See if we can optimize this compilation yet @@ -1406,10 +1425,10 @@ impl RouteSpecStore { &self, id: &RouteId, optimized: Option, - ) -> EyreResult> { + ) -> VeilidAPIResult> { let inner = &*self.inner.lock(); let Some(rssd) = inner.content.get_detail(id) else { - bail!("route id does not exist"); + apibail_invalid_argument!("route id does not exist", "id", id); }; // See if we can optimize this compilation yet @@ -1433,7 +1452,7 @@ impl RouteSpecStore { feature = "verbose-tracing", instrument(level = "trace", skip(self, blob), ret, err) )] - pub fn import_remote_private_route(&self, blob: Vec) -> EyreResult { + pub fn import_remote_private_route(&self, blob: Vec) -> VeilidAPIResult { let cur_ts = get_aligned_timestamp(); // decode the pr blob @@ -1450,7 +1469,7 @@ impl RouteSpecStore { 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"); + apibail_generic!("private route must have first hop"); } // ensure this isn't also an allocated route @@ -1528,7 +1547,7 @@ impl RouteSpecStore { &self, key: &PublicKey, cur_ts: Timestamp, - ) -> EyreResult<()> { + ) -> VeilidAPIResult<()> { let our_node_info_ts = self .unlocked_inner .routing_table @@ -1550,7 +1569,7 @@ impl RouteSpecStore { } } - bail!("private route is missing from store: {}", key); + apibail_invalid_argument!("private route is missing from store", "key", key); } /// Get the route statistics for any route we know about, local or remote @@ -1589,6 +1608,8 @@ impl RouteSpecStore { /// Clear caches when local our local node info changes #[instrument(level = "trace", skip(self))] pub fn reset(&self) { + log_rtab!(debug "flushing route spec store"); + let inner = &mut *self.inner.lock(); // Clean up local allocated routes @@ -1601,10 +1622,10 @@ impl RouteSpecStore { /// 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<()> { + pub fn mark_route_published(&self, id: &RouteId, published: bool) -> VeilidAPIResult<()> { let inner = &mut *self.inner.lock(); let Some(rssd) = inner.content.get_detail_mut(id) else { - bail!("route does not exist"); + apibail_invalid_argument!("route does not exist", "id", id); }; rssd.set_published(published); Ok(()) @@ -1622,13 +1643,13 @@ impl RouteSpecStore { } /// Convert private route list to binary blob - pub fn private_routes_to_blob(private_routes: &[PrivateRoute]) -> EyreResult> { + pub fn private_routes_to_blob(private_routes: &[PrivateRoute]) -> VeilidAPIResult> { 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"); + apibail_internal!("too many crypto kinds to encode blob"); } let pr_count = pr_count as u8; buffer.push(pr_count); @@ -1639,25 +1660,31 @@ impl RouteSpecStore { let mut pr_builder = pr_message.init_root::(); encode_private_route(private_route, &mut pr_builder) - .wrap_err("failed to encode private route")?; + .map_err(VeilidAPIError::internal)?; capnp::serialize_packed::write_message(&mut buffer, &pr_message) - .map_err(RPCError::internal) - .wrap_err("failed to convert builder to vec")?; + .map_err(RPCError::internal)?; } Ok(buffer) } - /// Convert binary blob to private route - pub fn blob_to_private_routes(crypto: Crypto, blob: Vec) -> EyreResult> { + /// Convert binary blob to private route vector + pub fn blob_to_private_routes( + crypto: Crypto, + blob: Vec, + ) -> VeilidAPIResult> { // Deserialize count if blob.is_empty() { - bail!("not deserializing empty private route blob"); + apibail_invalid_argument!( + "not deserializing empty private route blob", + "blob.is_empty", + true + ); } let pr_count = blob[0] as usize; if pr_count > MAX_CRYPTO_KINDS { - bail!("too many crypto kinds to decode blob"); + apibail_invalid_argument!("too many crypto kinds to decode blob", "blob[0]", pr_count); } // Deserialize stream of private routes @@ -1668,18 +1695,17 @@ impl RouteSpecStore { &mut pr_slice, capnp::message::ReaderOptions::new(), ) - .map_err(RPCError::internal) - .wrap_err("failed to make message reader")?; + .map_err(|e| VeilidAPIError::invalid_argument("failed to read blob", "e", e))?; 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")?; + .map_err(VeilidAPIError::internal)?; + let private_route = decode_private_route(&pr_reader).map_err(|e| { + VeilidAPIError::invalid_argument("failed to decode private route", "e", e) + })?; + private_route.validate(crypto.clone()).map_err(|e| { + VeilidAPIError::invalid_argument("failed to validate private route", "e", e) + })?; out.push(private_route); } @@ -1691,7 +1717,7 @@ impl RouteSpecStore { } /// Generate RouteId from typed key set of route public keys - fn generate_allocated_route_id(&self, rssd: &RouteSetSpecDetail) -> EyreResult { + fn generate_allocated_route_id(&self, rssd: &RouteSetSpecDetail) -> VeilidAPIResult { let route_set_keys = rssd.get_route_set_keys(); let crypto = self.unlocked_inner.routing_table.crypto(); @@ -1706,7 +1732,7 @@ impl RouteSpecStore { idbytes.extend_from_slice(&tk.value.bytes); } let Some(best_kind) = best_kind else { - bail!("no compatible crypto kinds in route"); + apibail_internal!("no compatible crypto kinds in route"); }; let vcrypto = crypto.get(best_kind).unwrap(); @@ -1714,7 +1740,10 @@ impl RouteSpecStore { } /// Generate RouteId from set of private routes - fn generate_remote_route_id(&self, private_routes: &[PrivateRoute]) -> EyreResult { + fn generate_remote_route_id( + &self, + private_routes: &[PrivateRoute], + ) -> VeilidAPIResult { let crypto = self.unlocked_inner.routing_table.crypto(); let mut idbytes = Vec::with_capacity(PUBLIC_KEY_LENGTH * private_routes.len()); @@ -1729,7 +1758,7 @@ impl RouteSpecStore { idbytes.extend_from_slice(&private_route.public_key.value.bytes); } let Some(best_kind) = best_kind else { - bail!("no compatible crypto kinds in route"); + apibail_internal!("no compatible crypto kinds in route"); }; let vcrypto = crypto.get(best_kind).unwrap(); diff --git a/veilid-core/src/routing_table/routing_domain_editor.rs b/veilid-core/src/routing_table/routing_domain_editor.rs index 4a7cbb7e..63eed645 100644 --- a/veilid-core/src/routing_table/routing_domain_editor.rs +++ b/veilid-core/src/routing_table/routing_domain_editor.rs @@ -140,7 +140,7 @@ impl RoutingDomainEditor { log_rtab!(debug "[{:?}] COMMIT: {:?}", self.routing_domain, self.changes); // Apply changes - let mut changed = false; + let mut peer_info_changed = false; { let mut inner = self.routing_table.inner.write(); inner.with_routing_domain_mut(self.routing_domain, |detail| { @@ -167,22 +167,21 @@ impl RoutingDomainEditor { detail .common_mut() .clear_dial_info_details(address_type, protocol_type); - changed = true; + peer_info_changed = true; } RoutingDomainChange::ClearRelayNode => { info!("[{:?}] cleared relay node", self.routing_domain); detail.common_mut().set_relay_node(None); - changed = true; + peer_info_changed = true; } RoutingDomainChange::SetRelayNode { relay_node } => { info!("[{:?}] set relay node: {}", self.routing_domain, relay_node); detail.common_mut().set_relay_node(Some(relay_node.clone())); - changed = true; + peer_info_changed = true; } RoutingDomainChange::SetRelayNodeKeepalive { ts } => { debug!("[{:?}] relay node keepalive: {:?}", self.routing_domain, ts); detail.common_mut().set_relay_node_last_keepalive(ts); - changed = true; } RoutingDomainChange::AddDialInfoDetail { dial_info_detail } => { info!( @@ -195,7 +194,7 @@ impl RoutingDomainEditor { .common_mut() .add_dial_info_detail(dial_info_detail.clone()); - changed = true; + peer_info_changed = true; } RoutingDomainChange::SetupNetwork { outbound_protocols, @@ -229,7 +228,7 @@ impl RoutingDomainEditor { address_types, capabilities.clone(), ); - changed = true; + peer_info_changed = true; } } RoutingDomainChange::SetNetworkClass { network_class } => { @@ -246,19 +245,19 @@ impl RoutingDomainEditor { info!("[{:?}] cleared network class", self.routing_domain,); } detail.common_mut().set_network_class(network_class); - changed = true; + peer_info_changed = true; } } } } }); - if changed { + if peer_info_changed { // Allow signed node info updates at same timestamp for otherwise dead nodes if our network has changed inner.reset_all_updated_since_last_network_change(); } } // Clear the routespecstore cache if our PublicInternet dial info has changed - if changed && self.routing_domain == RoutingDomain::PublicInternet { + if peer_info_changed && self.routing_domain == RoutingDomain::PublicInternet { let rss = self.routing_table.route_spec_store(); rss.reset(); } 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 2bab77de..8fbb36d2 100644 --- a/veilid-core/src/routing_table/tasks/private_route_management.rs +++ b/veilid-core/src/routing_table/tasks/private_route_management.rs @@ -132,6 +132,13 @@ impl RoutingTable { async move { let success = match rss.test_route(r).await { Ok(v) => v, + // Route was already removed + Err(VeilidAPIError::InvalidArgument { + context: _, + argument: _, + value: _, + }) => false, + // Other failures Err(e) => { log_rtab!(error "Test route failed: {}", e); ctx.lock().failed = true; @@ -205,15 +212,21 @@ impl RoutingTable { for _n in 0..routes_to_allocate { // Parameters here must be the most inclusive safety route spec // These will be used by test_remote_route as well - if let Some(k) = rss.allocate_route( + match rss.allocate_route( &VALID_CRYPTO_KINDS, Stability::default(), Sequencing::EnsureOrdered, default_route_hop_count, DirectionSet::all(), &[], - )? { - newly_allocated_routes.push(k); + ) { + Err(VeilidAPIError::TryAgain { message }) => { + log_rtab!(debug "Route allocation unavailable: {}", message); + } + Err(e) => return Err(e.into()), + Ok(v) => { + newly_allocated_routes.push(v); + } } } diff --git a/veilid-core/src/rpc_processor/destination.rs b/veilid-core/src/rpc_processor/destination.rs index 732334d5..cdee2666 100644 --- a/veilid-core/src/rpc_processor/destination.rs +++ b/veilid-core/src/rpc_processor/destination.rs @@ -164,7 +164,7 @@ impl RPCProcessor { pub(super) fn get_destination_respond_to( &self, dest: &Destination, - ) -> Result, RPCError> { + ) -> RPCNetworkResult { let routing_table = self.routing_table(); let rss = routing_table.route_spec_store(); @@ -180,23 +180,18 @@ impl RPCProcessor { SafetySelection::Safe(safety_spec) => { // Sent directly but with a safety route, respond to private route let crypto_kind = target.best_node_id().kind; - let Some(pr_key) = rss + let pr_key = network_result_try!(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", - )); - }; + .to_rpc_network_result()?); // Get the assembled route for response - let private_route = rss + let private_route = network_result_try!(rss .assemble_private_route(&pr_key, None) - .map_err(RPCError::internal)?; + .to_rpc_network_result()?); Ok(NetworkResult::Value(RespondTo::PrivateRoute(private_route))) } @@ -216,19 +211,14 @@ impl RPCProcessor { let mut avoid_nodes = relay.node_ids(); 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", - )); - }; + let pr_key = network_result_try!(rss + .get_private_route_for_safety_spec(crypto_kind, safety_spec, &avoid_nodes,) + .to_rpc_network_result()?); // Get the assembled route for response - let private_route = rss + let private_route = network_result_try!(rss .assemble_private_route(&pr_key, None) - .map_err(RPCError::internal)?; + .to_rpc_network_result()?); Ok(NetworkResult::Value(RespondTo::PrivateRoute(private_route))) } @@ -249,7 +239,7 @@ impl RPCProcessor { SafetySelection::Unsafe(_) => { // Sent to a private route with no safety route, use a stub safety route for the response if !routing_table.has_valid_network_class(RoutingDomain::PublicInternet) { - return Ok(NetworkResult::no_connection_other( + return Ok(NetworkResult::service_unavailable( "Own node info must be valid to use private route", )); } @@ -282,25 +272,19 @@ impl RPCProcessor { private_route.public_key.value } else { // Get the private route to respond to that matches the safety route spec we sent the request with - let Some(pr_key) = rss + network_result_try!(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", - )); - }; - pr_key + .to_rpc_network_result()?) }; // Get the assembled route for response - let private_route = rss + let private_route = network_result_try!(rss .assemble_private_route(&pr_key, None) - .map_err(RPCError::internal)?; + .to_rpc_network_result()?); Ok(NetworkResult::Value(RespondTo::PrivateRoute(private_route))) } diff --git a/veilid-core/src/rpc_processor/fanout_call.rs b/veilid-core/src/rpc_processor/fanout_call.rs index 3ae25137..1ff4e8d0 100644 --- a/veilid-core/src/rpc_processor/fanout_call.rs +++ b/veilid-core/src/rpc_processor/fanout_call.rs @@ -8,7 +8,7 @@ where result: Option>, } -pub type FanoutCallReturnType = Result>, RPCError>; +pub type FanoutCallReturnType = RPCNetworkResult>; pub type FanoutNodeInfoFilter = Arc bool + Send + Sync>; pub fn empty_fanout_node_info_filter() -> FanoutNodeInfoFilter { @@ -132,7 +132,7 @@ where // Do the call for this node match (self.call_routine)(next_node.clone()).await { - Ok(Some(v)) => { + Ok(NetworkResult::Value(v)) => { // Filter returned nodes let filtered_v: Vec = v .into_iter() @@ -155,8 +155,11 @@ where .register_find_node_answer(self.crypto_kind, filtered_v); self.clone().add_to_fanout_queue(&new_nodes); } - Ok(None) => { + #[allow(unused_variables)] + Ok(x) => { // Call failed, node will not be considered again + #[cfg(feature = "network-result-extra")] + log_rpc!(debug "Fanout result {}: {:?}", &next_node, x); } Err(e) => { // Error happened, abort everything and return the error diff --git a/veilid-core/src/rpc_processor/mod.rs b/veilid-core/src/rpc_processor/mod.rs index c9f1cf5a..19f87b67 100644 --- a/veilid-core/src/rpc_processor/mod.rs +++ b/veilid-core/src/rpc_processor/mod.rs @@ -469,24 +469,15 @@ impl RPCProcessor { let call_routine = |next_node: NodeRef| { let this = self.clone(); async move { - match this + let v = network_result_try!(this .clone() .rpc_call_find_node( Destination::direct(next_node).with_safety(safety_selection), node_id, vec![], ) - .await - { - Ok(v) => { - let v = network_result_value_or_log!(v => [ format!(": node_id={} count={} fanout={} fanout={} safety_selection={:?}", node_id, count, fanout, timeout_us, safety_selection) ] { - // Any other failures, just try the next node - return Ok(None); - }); - Ok(Some(v.answer)) - } - Err(e) => Err(e), - } + .await?); + Ok(NetworkResult::value(v.answer)) } }; @@ -636,7 +627,7 @@ impl RPCProcessor { remote_private_route: PrivateRoute, reply_private_route: Option, message_data: Vec, - ) -> Result, RPCError> { + ) -> RPCNetworkResult { let routing_table = self.routing_table(); let rss = routing_table.route_spec_store(); @@ -650,17 +641,8 @@ impl RPCProcessor { }; // Compile the safety route with the private route - let compiled_route: CompiledRoute = match rss - .compile_safety_route(safety_selection, remote_private_route) - .map_err(RPCError::internal)? - { - Some(cr) => cr, - None => { - return Ok(NetworkResult::no_connection_other( - "private route could not be compiled at this time", - )) - } - }; + let compiled_route: CompiledRoute = network_result_try!(rss + .compile_safety_route(safety_selection, remote_private_route).to_rpc_network_result()?); let sr_is_stub = compiled_route.safety_route.is_stub(); let sr_pubkey = compiled_route.safety_route.public_key.value; @@ -721,7 +703,7 @@ impl RPCProcessor { &self, dest: Destination, operation: &RPCOperation, - ) -> Result, RPCError> { + ) ->RPCNetworkResult { let out: NetworkResult; // Encode message to a builder and make a message reader for it @@ -1160,7 +1142,7 @@ impl RPCProcessor { dest: Destination, question: RPCQuestion, context: Option, - ) -> Result, RPCError> { + ) ->RPCNetworkResult { // Get sender peer info if we should send that let spi = self.get_sender_peer_info(&dest); @@ -1256,7 +1238,7 @@ impl RPCProcessor { &self, dest: Destination, statement: RPCStatement, - ) -> Result, RPCError> { + ) ->RPCNetworkResult<()> { // Get sender peer info if we should send that let spi = self.get_sender_peer_info(&dest); @@ -1331,7 +1313,7 @@ impl RPCProcessor { &self, request: RPCMessage, answer: RPCAnswer, - ) -> Result, RPCError> { + ) ->RPCNetworkResult<()> { // Extract destination from respond_to let dest = network_result_try!(self.get_respond_to_destination(&request)); @@ -1457,7 +1439,7 @@ impl RPCProcessor { async fn process_rpc_message( &self, encoded_msg: RPCMessageEncoded, - ) -> Result, RPCError> { + ) ->RPCNetworkResult<()> { let address_filter = self.network_manager.address_filter(); // Decode operation appropriately based on header detail diff --git a/veilid-core/src/rpc_processor/rpc_app_call.rs b/veilid-core/src/rpc_processor/rpc_app_call.rs index 37c21deb..239768ca 100644 --- a/veilid-core/src/rpc_processor/rpc_app_call.rs +++ b/veilid-core/src/rpc_processor/rpc_app_call.rs @@ -11,7 +11,7 @@ impl RPCProcessor { self, dest: Destination, message: Vec, - ) -> Result>>, RPCError> { + ) -> RPCNetworkResult>> { let debug_string = format!("AppCall(message(len)={}) => {}", message.len(), dest); let app_call_q = RPCOperationAppCallQ::new(message)?; @@ -49,10 +49,7 @@ impl RPCProcessor { } #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))] - pub(crate) async fn process_app_call_q( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_app_call_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Ignore if disabled let routing_table = self.routing_table(); diff --git a/veilid-core/src/rpc_processor/rpc_app_message.rs b/veilid-core/src/rpc_processor/rpc_app_message.rs index 9f6a3d89..d0aac195 100644 --- a/veilid-core/src/rpc_processor/rpc_app_message.rs +++ b/veilid-core/src/rpc_processor/rpc_app_message.rs @@ -11,7 +11,7 @@ impl RPCProcessor { self, dest: Destination, message: Vec, - ) -> Result, RPCError> { + ) -> RPCNetworkResult<()> { let app_message = RPCOperationAppMessage::new(message)?; let statement = RPCStatement::new(RPCStatementDetail::AppMessage(Box::new(app_message))); @@ -20,10 +20,7 @@ impl RPCProcessor { } #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))] - pub(crate) async fn process_app_message( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_app_message(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Ignore if disabled let routing_table = self.routing_table(); let opi = routing_table.get_own_peer_info(msg.header.routing_domain()); diff --git a/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs b/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs index 2e761488..133ded54 100644 --- a/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_cancel_tunnel.rs @@ -2,10 +2,7 @@ use super::*; impl RPCProcessor { #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))] - pub(crate) async fn process_cancel_tunnel_q( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_cancel_tunnel_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Ignore if disabled #[cfg(feature = "unstable-tunnels")] { diff --git a/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs b/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs index c13088c1..4b97985f 100644 --- a/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_complete_tunnel.rs @@ -2,10 +2,7 @@ use super::*; impl RPCProcessor { #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))] - pub(crate) async fn process_complete_tunnel_q( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_complete_tunnel_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Ignore if disabled #[cfg(feature = "unstable-tunnels")] { diff --git a/veilid-core/src/rpc_processor/rpc_error.rs b/veilid-core/src/rpc_processor/rpc_error.rs index f20f7102..dc339fb7 100644 --- a/veilid-core/src/rpc_processor/rpc_error.rs +++ b/veilid-core/src/rpc_processor/rpc_error.rs @@ -13,6 +13,8 @@ pub enum RPCError { Internal(String), #[error("[RPCError: Network({0})]")] Network(String), + #[error("[RPCError: TryAgain({0})]")] + TryAgain(String), } impl RPCError { @@ -56,6 +58,25 @@ impl From for VeilidAPIError { RPCError::Protocol(message) => VeilidAPIError::Generic { message }, RPCError::Internal(message) => VeilidAPIError::Internal { message }, RPCError::Network(message) => VeilidAPIError::Generic { message }, + RPCError::TryAgain(message) => VeilidAPIError::TryAgain { message }, + } + } +} + +pub(crate) type RPCNetworkResult = Result, RPCError>; + +pub(crate) trait ToRPCNetworkResult { + fn to_rpc_network_result(self) -> RPCNetworkResult; +} + +impl ToRPCNetworkResult for VeilidAPIResult { + fn to_rpc_network_result(self) -> RPCNetworkResult { + match self { + Err(VeilidAPIError::TryAgain { message }) => Err(RPCError::TryAgain(message)), + Err(VeilidAPIError::Timeout) => Ok(NetworkResult::timeout()), + Err(VeilidAPIError::Unimplemented { message }) => Err(RPCError::Unimplemented(message)), + Err(e) => Err(RPCError::internal(e)), + Ok(v) => Ok(NetworkResult::value(v)), } } } diff --git a/veilid-core/src/rpc_processor/rpc_find_block.rs b/veilid-core/src/rpc_processor/rpc_find_block.rs index 600f7d83..786cef37 100644 --- a/veilid-core/src/rpc_processor/rpc_find_block.rs +++ b/veilid-core/src/rpc_processor/rpc_find_block.rs @@ -2,10 +2,7 @@ use super::*; impl RPCProcessor { #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))] - pub(crate) async fn process_find_block_q( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_find_block_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Ignore if disabled #[cfg(feature = "unstable-blockstore")] { diff --git a/veilid-core/src/rpc_processor/rpc_find_node.rs b/veilid-core/src/rpc_processor/rpc_find_node.rs index 088b38b9..ded13285 100644 --- a/veilid-core/src/rpc_processor/rpc_find_node.rs +++ b/veilid-core/src/rpc_processor/rpc_find_node.rs @@ -16,7 +16,7 @@ impl RPCProcessor { dest: Destination, node_id: TypedKey, capabilities: Vec, - ) -> Result>>, RPCError> { + ) -> RPCNetworkResult>> { // Ensure destination never has a private route if matches!( dest, @@ -78,10 +78,7 @@ impl RPCProcessor { } #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))] - pub(crate) async fn process_find_node_q( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_find_node_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Ensure this never came over a private route, safety route is okay though match &msg.header.detail { RPCMessageHeaderDetail::Direct(_) | RPCMessageHeaderDetail::SafetyRouted(_) => {} diff --git a/veilid-core/src/rpc_processor/rpc_get_value.rs b/veilid-core/src/rpc_processor/rpc_get_value.rs index 89c1aeff..055aaa45 100644 --- a/veilid-core/src/rpc_processor/rpc_get_value.rs +++ b/veilid-core/src/rpc_processor/rpc_get_value.rs @@ -32,7 +32,7 @@ impl RPCProcessor { key: TypedKey, subkey: ValueSubkey, last_descriptor: Option, - ) -> Result>, RPCError> { + ) ->RPCNetworkResult> { // Ensure destination never has a private route // and get the target noderef so we can validate the response let Some(target) = dest.target() else { @@ -168,7 +168,7 @@ impl RPCProcessor { pub(crate) async fn process_get_value_q( &self, msg: RPCMessage, - ) -> Result, RPCError> { + ) ->RPCNetworkResult<()> { // Ensure this never came over a private route, safety route is okay though match &msg.header.detail { diff --git a/veilid-core/src/rpc_processor/rpc_return_receipt.rs b/veilid-core/src/rpc_processor/rpc_return_receipt.rs index 60c13cb2..8d3caaa1 100644 --- a/veilid-core/src/rpc_processor/rpc_return_receipt.rs +++ b/veilid-core/src/rpc_processor/rpc_return_receipt.rs @@ -11,7 +11,7 @@ impl RPCProcessor { self, dest: Destination, receipt: D, - ) -> Result, RPCError> { + ) -> RPCNetworkResult<()> { let receipt = receipt.as_ref().to_vec(); let return_receipt = RPCOperationReturnReceipt::new(receipt)?; @@ -25,10 +25,7 @@ impl RPCProcessor { } #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))] - pub(crate) async fn process_return_receipt( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_return_receipt(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Get the statement let (_, _, _, kind) = msg.operation.destructure(); let receipt = match kind { diff --git a/veilid-core/src/rpc_processor/rpc_route.rs b/veilid-core/src/rpc_processor/rpc_route.rs index 117a26a3..d33b9055 100644 --- a/veilid-core/src/rpc_processor/rpc_route.rs +++ b/veilid-core/src/rpc_processor/rpc_route.rs @@ -10,7 +10,7 @@ impl RPCProcessor { routed_operation: RoutedOperation, route_hop: RouteHop, safety_route: SafetyRoute, - ) -> Result, RPCError> { + ) -> RPCNetworkResult<()> { // Make sure hop count makes sense if safety_route.hop_count as usize > self.unlocked_inner.max_route_hop_count { return Ok(NetworkResult::invalid_message( @@ -69,7 +69,7 @@ impl RPCProcessor { next_route_node: RouteNode, safety_route_public_key: TypedKey, next_private_route: PrivateRoute, - ) -> Result, RPCError> { + ) -> RPCNetworkResult<()> { // Make sure hop count makes sense if next_private_route.hop_count as usize > self.unlocked_inner.max_route_hop_count { return Ok(NetworkResult::invalid_message( @@ -122,7 +122,7 @@ impl RPCProcessor { vcrypto: CryptoSystemVersion, routed_operation: RoutedOperation, remote_sr_pubkey: TypedKey, - ) -> Result, RPCError> { + ) -> RPCNetworkResult<()> { // 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 safety routes? let node_id_secret = self.routing_table.node_id_secret_key(remote_sr_pubkey.kind); @@ -170,7 +170,7 @@ impl RPCProcessor { routed_operation: RoutedOperation, remote_sr_pubkey: TypedKey, pr_pubkey: TypedKey, - ) -> Result, RPCError> { + ) -> RPCNetworkResult<()> { // 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( @@ -246,7 +246,7 @@ impl RPCProcessor { routed_operation: RoutedOperation, remote_sr_pubkey: TypedKey, pr_pubkey: TypedKey, - ) -> Result, RPCError> { + ) -> RPCNetworkResult<()> { // If the private route public key is our node id, then this was sent via safety route to our node directly // so there will be no signatures to validate if self.routing_table.node_ids().contains(&pr_pubkey) { @@ -277,7 +277,7 @@ impl RPCProcessor { mut routed_operation: RoutedOperation, sr_pubkey: TypedKey, mut private_route: PrivateRoute, - ) -> Result, RPCError> { + ) -> RPCNetworkResult<()> { 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", @@ -341,7 +341,7 @@ impl RPCProcessor { route_hop_data: &RouteHopData, pr_pubkey: &TypedKey, route_operation: &mut RoutedOperation, - ) -> Result, RPCError> { + ) -> RPCNetworkResult { // Get crypto kind let crypto_kind = pr_pubkey.kind; let Some(vcrypto) = self.crypto.get(crypto_kind) else { @@ -402,10 +402,7 @@ impl RPCProcessor { feature = "verbose-tracing", instrument(level = "trace", skip(self), ret, err) )] - pub(crate) async fn process_route( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_route(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Ignore if disabled let routing_table = self.routing_table(); if !routing_table.has_valid_network_class(msg.header.routing_domain()) { diff --git a/veilid-core/src/rpc_processor/rpc_set_value.rs b/veilid-core/src/rpc_processor/rpc_set_value.rs index b69a8ff5..b507b73e 100644 --- a/veilid-core/src/rpc_processor/rpc_set_value.rs +++ b/veilid-core/src/rpc_processor/rpc_set_value.rs @@ -36,7 +36,7 @@ impl RPCProcessor { value: SignedValueData, descriptor: SignedValueDescriptor, send_descriptor: bool, - ) -> Result>, RPCError> { + ) ->RPCNetworkResult> { // Ensure destination never has a private route // and get the target noderef so we can validate the response let Some(target) = dest.target() else { @@ -182,7 +182,7 @@ impl RPCProcessor { pub(crate) async fn process_set_value_q( &self, msg: RPCMessage, - ) -> Result, RPCError> { + ) ->RPCNetworkResult<()> { // Ignore if disabled let routing_table = self.routing_table(); let opi = routing_table.get_own_peer_info(msg.header.routing_domain()); diff --git a/veilid-core/src/rpc_processor/rpc_signal.rs b/veilid-core/src/rpc_processor/rpc_signal.rs index df041188..056770ee 100644 --- a/veilid-core/src/rpc_processor/rpc_signal.rs +++ b/veilid-core/src/rpc_processor/rpc_signal.rs @@ -11,7 +11,7 @@ impl RPCProcessor { self, dest: Destination, signal_info: SignalInfo, - ) -> Result, RPCError> { + ) -> RPCNetworkResult<()> { // Ensure destination never has a private route if matches!( dest, @@ -33,10 +33,7 @@ impl RPCProcessor { } #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))] - pub(crate) async fn process_signal( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_signal(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Ignore if disabled let routing_table = self.routing_table(); let opi = routing_table.get_own_peer_info(msg.header.routing_domain()); diff --git a/veilid-core/src/rpc_processor/rpc_start_tunnel.rs b/veilid-core/src/rpc_processor/rpc_start_tunnel.rs index 972ba67b..f6d8b3c7 100644 --- a/veilid-core/src/rpc_processor/rpc_start_tunnel.rs +++ b/veilid-core/src/rpc_processor/rpc_start_tunnel.rs @@ -2,10 +2,7 @@ use super::*; impl RPCProcessor { #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))] - pub(crate) async fn process_start_tunnel_q( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_start_tunnel_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Ignore if disabled #[cfg(feature = "unstable-tunnels")] { diff --git a/veilid-core/src/rpc_processor/rpc_status.rs b/veilid-core/src/rpc_processor/rpc_status.rs index 0160cced..7c894544 100644 --- a/veilid-core/src/rpc_processor/rpc_status.rs +++ b/veilid-core/src/rpc_processor/rpc_status.rs @@ -22,7 +22,7 @@ impl RPCProcessor { pub async fn rpc_call_status( self, dest: Destination, - ) -> Result>>, RPCError> { + ) -> RPCNetworkResult>> { let (opt_target_nr, routing_domain, node_status) = match dest.get_safety_selection() { SafetySelection::Unsafe(_) => { let (opt_target_nr, routing_domain) = match &dest { @@ -197,10 +197,7 @@ impl RPCProcessor { } #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))] - pub(crate) async fn process_status_q( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_status_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Get the question let kind = msg.operation.kind().clone(); let status_q = match kind { diff --git a/veilid-core/src/rpc_processor/rpc_supply_block.rs b/veilid-core/src/rpc_processor/rpc_supply_block.rs index 630635eb..368bd44b 100644 --- a/veilid-core/src/rpc_processor/rpc_supply_block.rs +++ b/veilid-core/src/rpc_processor/rpc_supply_block.rs @@ -2,10 +2,7 @@ use super::*; impl RPCProcessor { #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))] - pub(crate) async fn process_supply_block_q( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_supply_block_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Ignore if disabled #[cfg(feature = "unstable-blockstore")] { 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 2a97d88a..db5e8ef2 100644 --- a/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs +++ b/veilid-core/src/rpc_processor/rpc_validate_dial_info.rs @@ -56,10 +56,7 @@ impl RPCProcessor { } #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))] - pub(crate) async fn process_validate_dial_info( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_validate_dial_info(&self, msg: RPCMessage) -> RPCNetworkResult<()> { let routing_table = self.routing_table(); if !routing_table.has_valid_network_class(msg.header.routing_domain()) { return Ok(NetworkResult::service_unavailable( diff --git a/veilid-core/src/rpc_processor/rpc_value_changed.rs b/veilid-core/src/rpc_processor/rpc_value_changed.rs index 0776ce31..96c1b1fc 100644 --- a/veilid-core/src/rpc_processor/rpc_value_changed.rs +++ b/veilid-core/src/rpc_processor/rpc_value_changed.rs @@ -2,10 +2,7 @@ use super::*; impl RPCProcessor { #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), err))] - pub(crate) async fn process_value_changed( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_value_changed(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Ignore if disabled let routing_table = self.routing_table(); let opi = routing_table.get_own_peer_info(msg.header.routing_domain()); diff --git a/veilid-core/src/rpc_processor/rpc_watch_value.rs b/veilid-core/src/rpc_processor/rpc_watch_value.rs index 42095087..7ec9d69a 100644 --- a/veilid-core/src/rpc_processor/rpc_watch_value.rs +++ b/veilid-core/src/rpc_processor/rpc_watch_value.rs @@ -2,10 +2,7 @@ use super::*; impl RPCProcessor { #[cfg_attr(feature="verbose-tracing", instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), ret, err))] - pub(crate) async fn process_watch_value_q( - &self, - msg: RPCMessage, - ) -> Result, RPCError> { + pub(crate) async fn process_watch_value_q(&self, msg: RPCMessage) -> RPCNetworkResult<()> { // Ignore if disabled let routing_table = self.routing_table(); let opi = routing_table.get_own_peer_info(msg.header.routing_domain()); diff --git a/veilid-core/src/storage_manager/get_value.rs b/veilid-core/src/storage_manager/get_value.rs index 85a28a10..3fcb4b0c 100644 --- a/veilid-core/src/storage_manager/get_value.rs +++ b/veilid-core/src/storage_manager/get_value.rs @@ -54,19 +54,17 @@ impl StorageManager { let context = context.clone(); let last_descriptor = last_subkey_result.descriptor.clone(); async move { - let vres = rpc_processor - .clone() - .rpc_call_get_value( - Destination::direct(next_node.clone()).with_safety(safety_selection), - key, - subkey, - last_descriptor, - ) - .await?; - let gva = network_result_value_or_log!(vres => [ format!(": next_node={} safety_selection={:?} key={} subkey={}", next_node, safety_selection, key, subkey) ] { - // Any other failures, just try the next node - return Ok(None); - }); + let gva = network_result_try!( + rpc_processor + .clone() + .rpc_call_get_value( + Destination::direct(next_node.clone()).with_safety(safety_selection), + key, + subkey, + last_descriptor, + ) + .await? + ); // Keep the descriptor if we got one. If we had a last_descriptor it will // already be validated by rpc_call_get_value @@ -87,8 +85,9 @@ impl StorageManager { let (Some(descriptor), Some(schema)) = (&ctx.descriptor, &ctx.schema) else { // Got a value but no descriptor for it // Move to the next node - log_stor!(debug "Got value with no descriptor"); - return Ok(None); + return Ok(NetworkResult::invalid_message( + "Got value with no descriptor", + )); }; // Validate with schema @@ -99,8 +98,10 @@ impl StorageManager { ) { // Validation failed, ignore this value // Move to the next node - log_stor!(debug "Schema validation failed on subkey {}", subkey); - return Ok(None); + return Ok(NetworkResult::invalid_message(format!( + "Schema validation failed on subkey {}", + subkey + ))); } // If we have a prior value, see if this is a newer sequence number @@ -112,7 +113,7 @@ impl StorageManager { // If sequence number is the same, the data should be the same if prior_value.value_data() != value.value_data() { // Move to the next node - return Ok(None); + return Ok(NetworkResult::invalid_message("value data mismatch")); } // Increase the consensus count for the existing value ctx.value_count += 1; @@ -136,7 +137,7 @@ impl StorageManager { #[cfg(feature = "network-result-extra")] log_stor!(debug "GetValue fanout call returned peers {}", gva.answer.peers.len()); - Ok(Some(gva.answer.peers)) + Ok(NetworkResult::value(gva.answer.peers)) } }; diff --git a/veilid-core/src/storage_manager/mod.rs b/veilid-core/src/storage_manager/mod.rs index 0b28b71b..a04ec1ba 100644 --- a/veilid-core/src/storage_manager/mod.rs +++ b/veilid-core/src/storage_manager/mod.rs @@ -209,8 +209,7 @@ impl StorageManager { // Get rpc processor and drop mutex so we don't block while getting the value from the network let Some(rpc_processor) = inner.rpc_processor.clone() else { - // Offline, try again later - apibail_try_again!(); + apibail_try_again!("offline, try again later"); }; // Drop the mutex so we dont block during network access @@ -310,8 +309,7 @@ impl StorageManager { // Get rpc processor and drop mutex so we don't block while getting the value from the network let Some(rpc_processor) = inner.rpc_processor.clone() else { - // Offline, try again later - apibail_try_again!(); + apibail_try_again!("offline, try again later"); }; // Drop the lock for network access diff --git a/veilid-core/src/storage_manager/record_store.rs b/veilid-core/src/storage_manager/record_store.rs index 401d855d..c7a9da80 100644 --- a/veilid-core/src/storage_manager/record_store.rs +++ b/veilid-core/src/storage_manager/record_store.rs @@ -337,7 +337,7 @@ where .unwrap(); if !self.total_storage_space.check_limit() { self.total_storage_space.rollback(); - apibail_try_again!(); + apibail_try_again!("out of storage space"); } // Save to record table @@ -650,7 +650,7 @@ where .add(new_subkey_size as u64) .unwrap(); if !self.total_storage_space.check_limit() { - apibail_try_again!(); + apibail_try_again!("out of storage space"); } // Write subkey diff --git a/veilid-core/src/storage_manager/set_value.rs b/veilid-core/src/storage_manager/set_value.rs index 5c812f24..e7742fac 100644 --- a/veilid-core/src/storage_manager/set_value.rs +++ b/veilid-core/src/storage_manager/set_value.rs @@ -60,21 +60,19 @@ impl StorageManager { }; // send across the wire - let vres = rpc_processor - .clone() - .rpc_call_set_value( - Destination::direct(next_node.clone()).with_safety(safety_selection), - key, - subkey, - value, - descriptor.clone(), - send_descriptor, - ) - .await?; - let sva = network_result_value_or_log!(vres => [ format!(": next_node={} safety_selection={:?} key={} subkey={} send_descriptor={}", next_node, safety_selection, key, subkey, send_descriptor) ] { - // Any other failures, just try the next node and pretend this one never happened - return Ok(None); - }); + let sva = network_result_try!( + rpc_processor + .clone() + .rpc_call_set_value( + Destination::direct(next_node.clone()).with_safety(safety_selection), + key, + subkey, + value, + descriptor.clone(), + send_descriptor, + ) + .await? + ); // If the node was close enough to possibly set the value if sva.answer.set { @@ -91,7 +89,7 @@ impl StorageManager { value.value_data(), ) { // Validation failed, ignore this value and pretend we never saw this node - return Ok(None); + return Ok(NetworkResult::invalid_message("Schema validation failed")); } // We have a prior value, ensure this is a newer sequence number @@ -107,7 +105,7 @@ impl StorageManager { // If the sequence number is older, or an equal sequence number, // node should have not returned a value here. // Skip this node and its closer list because it is misbehaving - return Ok(None); + return Ok(NetworkResult::invalid_message("Sequence number is older")); } } else { // It was set on this node and no newer value was found and returned, @@ -124,7 +122,7 @@ impl StorageManager { #[cfg(feature = "network-result-extra")] log_stor!(debug "SetValue fanout call returned peers {}", sva.answer.peers.len()); - Ok(Some(sva.answer.peers)) + Ok(NetworkResult::value(sva.answer.peers)) } }; diff --git a/veilid-core/src/veilid_api/api.rs b/veilid-core/src/veilid_api/api.rs index e2139e49..496dcf66 100644 --- a/veilid-core/src/veilid_api/api.rs +++ b/veilid-core/src/veilid_api/api.rs @@ -200,9 +200,11 @@ impl VeilidAPI { /// `VLD0:XmnGyJrjMJBRC5ayJZRPXWTBspdX36-pbLb98H3UMeE` but if the prefix is left off /// `XmnGyJrjMJBRC5ayJZRPXWTBspdX36-pbLb98H3UMeE` will be parsed with the 'best' cryptosystem /// available (at the time of this writing this is `VLD0`) - pub async fn parse_as_target>(&self, s: S) -> VeilidAPIResult { + pub async fn parse_as_target(&self, s: S) -> VeilidAPIResult { + let s = s.to_string(); + // Is this a route id? - if let Ok(rrid) = RouteId::from_str(s.as_ref()) { + if let Ok(rrid) = RouteId::from_str(&s) { let routing_table = self.routing_table()?; let rss = routing_table.route_spec_store(); @@ -213,11 +215,11 @@ impl VeilidAPI { } // Is this a node id? - if let Ok(nid) = TypedKey::from_str(s.as_ref()) { + if let Ok(nid) = TypedKey::from_str(&s) { return Ok(Target::NodeId(nid)); } - Err(VeilidAPIError::invalid_target()) + Err(VeilidAPIError::parse_error("Unable to parse as target", s)) } //////////////////////////////////////////////////////////////// @@ -261,40 +263,28 @@ impl VeilidAPI { }; let rss = self.routing_table()?.route_spec_store(); - let r = rss - .allocate_route( - crypto_kinds, - stability, - sequencing, - default_route_hop_count, - Direction::Inbound.into(), - &[], - ) - .map_err(VeilidAPIError::internal)?; - let Some(route_id) = r else { - apibail_generic!("unable to allocate route"); - }; - if !rss - .test_route(route_id) - .await - .map_err(VeilidAPIError::no_connection)? - { + let route_id = rss.allocate_route( + crypto_kinds, + stability, + sequencing, + default_route_hop_count, + Direction::Inbound.into(), + &[], + )?; + if !rss.test_route(route_id).await? { rss.release_route(route_id); apibail_generic!("allocated route failed to test"); } - let private_routes = rss - .assemble_private_routes(&route_id, Some(true)) - .map_err(VeilidAPIError::generic)?; + let private_routes = rss.assemble_private_routes(&route_id, Some(true))?; let blob = match RouteSpecStore::private_routes_to_blob(&private_routes) { Ok(v) => v, Err(e) => { rss.release_route(route_id); - apibail_internal!(e); + return Err(e); } }; - rss.mark_route_published(&route_id, true) - .map_err(VeilidAPIError::internal)?; + rss.mark_route_published(&route_id, true)?; Ok((route_id, blob)) } @@ -305,7 +295,6 @@ impl VeilidAPI { pub fn import_remote_private_route(&self, blob: Vec) -> VeilidAPIResult { let rss = self.routing_table()?.route_spec_store(); rss.import_remote_private_route(blob) - .map_err(|e| VeilidAPIError::invalid_argument(e, "blob", "private route blob")) } /// Release either a locally allocated or remotely imported private route diff --git a/veilid-core/src/veilid_api/debug.rs b/veilid-core/src/veilid_api/debug.rs index 720c6161..413d033a 100644 --- a/veilid-core/src/veilid_api/debug.rs +++ b/veilid-core/src/veilid_api/debug.rs @@ -1096,8 +1096,7 @@ impl VeilidAPI { directions, &[], ) { - Ok(Some(v)) => format!("{}", v), - Ok(None) => "".to_string(), + Ok(v) => v.to_string(), Err(e) => { format!("Route allocation failed: {}", e) } diff --git a/veilid-core/src/veilid_api/error.rs b/veilid-core/src/veilid_api/error.rs index 39261179..b7c801ea 100644 --- a/veilid-core/src/veilid_api/error.rs +++ b/veilid-core/src/veilid_api/error.rs @@ -19,8 +19,8 @@ macro_rules! apibail_timeout { #[allow(unused_macros)] #[macro_export] macro_rules! apibail_try_again { - () => { - return Err(VeilidAPIError::try_again()) + ($x:expr) => { + return Err(VeilidAPIError::try_again($x)) }; } @@ -83,8 +83,8 @@ macro_rules! apibail_key_not_found { #[allow(unused_macros)] #[macro_export] macro_rules! apibail_invalid_target { - () => { - return Err(VeilidAPIError::invalid_target()) + ($x:expr) => { + return Err(VeilidAPIError::invalid_target($x)) }; } @@ -116,12 +116,12 @@ pub enum VeilidAPIError { AlreadyInitialized, #[error("Timeout")] Timeout, - #[error("TryAgain")] - TryAgain, + #[error("TryAgain: {message}")] + TryAgain { message: String }, #[error("Shutdown")] Shutdown, - #[error("Invalid target")] - InvalidTarget, + #[error("Invalid target: {message}")] + InvalidTarget { message: String }, #[error("No connection: {message}")] NoConnection { message: String }, #[error("Key not found: {key}")] @@ -158,14 +158,18 @@ impl VeilidAPIError { pub fn timeout() -> Self { Self::Timeout } - pub fn try_again() -> Self { - Self::TryAgain + pub fn try_again(msg: T) -> Self { + Self::TryAgain { + message: msg.to_string(), + } } pub fn shutdown() -> Self { Self::Shutdown } - pub fn invalid_target() -> Self { - Self::InvalidTarget + pub fn invalid_target(msg: T) -> Self { + Self::InvalidTarget { + message: msg.to_string(), + } } pub fn no_connection(msg: T) -> Self { Self::NoConnection { @@ -213,6 +217,21 @@ impl VeilidAPIError { message: msg.to_string(), } } + + pub(crate) fn from_network_result(nr: NetworkResult) -> Result { + match nr { + NetworkResult::Timeout => Err(VeilidAPIError::timeout()), + NetworkResult::ServiceUnavailable(m) => Err(VeilidAPIError::invalid_target(m)), + NetworkResult::NoConnection(m) => Err(VeilidAPIError::no_connection(m)), + NetworkResult::AlreadyExists(m) => { + Err(VeilidAPIError::generic(format!("Already exists: {}", m))) + } + NetworkResult::InvalidMessage(m) => { + Err(VeilidAPIError::parse_error("Invalid message", m)) + } + NetworkResult::Value(v) => Ok(v), + } + } } pub type VeilidAPIResult = Result; diff --git a/veilid-core/src/veilid_api/json_api/process.rs b/veilid-core/src/veilid_api/json_api/process.rs index a84be568..e4665d9c 100644 --- a/veilid-core/src/veilid_api/json_api/process.rs +++ b/veilid-core/src/veilid_api/json_api/process.rs @@ -220,7 +220,7 @@ impl JsonRequestProcessor { return Ok(Target::NodeId(nid)); } - Err(VeilidAPIError::invalid_target()) + Err(VeilidAPIError::parse_error("Unable to parse as target", s)) } ////////////////////////////////////////////////////////////////////////////////////// diff --git a/veilid-core/src/veilid_api/routing_context.rs b/veilid-core/src/veilid_api/routing_context.rs index 6e7a9a49..3d7f16a1 100644 --- a/veilid-core/src/veilid_api/routing_context.rs +++ b/veilid-core/src/veilid_api/routing_context.rs @@ -126,7 +126,7 @@ impl RoutingContext { .await { Ok(Some(nr)) => nr, - Ok(None) => apibail_invalid_target!(), + Ok(None) => apibail_invalid_target!("could not resolve node id"), Err(e) => return Err(e.into()), }; // Apply sequencing to match safety selection @@ -142,7 +142,7 @@ impl RoutingContext { let rss = self.api.routing_table()?.route_spec_store(); let Some(private_route) = rss.best_remote_private_route(&rsid) else { - apibail_invalid_target!(); + apibail_invalid_target!("could not get remote private route"); }; Ok(rpc_processor::Destination::PrivateRoute { @@ -174,10 +174,7 @@ impl RoutingContext { let answer = match rpc_processor.rpc_call_app_call(dest, message).await { Ok(NetworkResult::Value(v)) => v, Ok(NetworkResult::Timeout) => apibail_timeout!(), - Ok(NetworkResult::ServiceUnavailable(e)) => { - log_network_result!(format!("app_call: ServiceUnavailable({})", e)); - apibail_try_again!() - } + Ok(NetworkResult::ServiceUnavailable(e)) => apibail_invalid_target!(e), Ok(NetworkResult::NoConnection(e)) | Ok(NetworkResult::AlreadyExists(e)) => { apibail_no_connection!(e); } @@ -207,10 +204,7 @@ impl RoutingContext { match rpc_processor.rpc_call_app_message(dest, message).await { Ok(NetworkResult::Value(())) => {} Ok(NetworkResult::Timeout) => apibail_timeout!(), - Ok(NetworkResult::ServiceUnavailable(e)) => { - log_network_result!(format!("app_message: ServiceUnavailable({})", e)); - apibail_try_again!() - } + Ok(NetworkResult::ServiceUnavailable(e)) => apibail_invalid_target!(e), Ok(NetworkResult::NoConnection(e)) | Ok(NetworkResult::AlreadyExists(e)) => { apibail_no_connection!(e); } diff --git a/veilid-flutter/example/pubspec.lock b/veilid-flutter/example/pubspec.lock index 8d0b8994..71b84459 100644 --- a/veilid-flutter/example/pubspec.lock +++ b/veilid-flutter/example/pubspec.lock @@ -403,7 +403,7 @@ packages: path: ".." relative: true source: path - version: "0.2.3" + version: "0.2.4" web: dependency: transitive description: diff --git a/veilid-flutter/lib/veilid_api_exception.dart b/veilid-flutter/lib/veilid_api_exception.dart index 433114e9..6eb30393 100644 --- a/veilid-flutter/lib/veilid_api_exception.dart +++ b/veilid-flutter/lib/veilid_api_exception.dart @@ -22,7 +22,7 @@ abstract class VeilidAPIException implements Exception { } case 'TryAgain': { - return VeilidAPIExceptionTryAgain(); + return VeilidAPIExceptionTryAgain(json['message']! as String); } case 'Shutdown': { @@ -30,7 +30,7 @@ abstract class VeilidAPIException implements Exception { } case 'InvalidTarget': { - return VeilidAPIExceptionInvalidTarget(); + return VeilidAPIExceptionInvalidTarget(json['message']! as String); } case 'NoConnection': { @@ -108,11 +108,14 @@ class VeilidAPIExceptionTimeout implements VeilidAPIException { @immutable class VeilidAPIExceptionTryAgain implements VeilidAPIException { + // + const VeilidAPIExceptionTryAgain(this.message); + final String message; @override - String toString() => 'VeilidAPIException: TryAgain'; + String toString() => 'VeilidAPIException: TryAgain (message: $message)'; @override - String toDisplayError() => 'Try again'; + String toDisplayError() => 'Try again: (message: $message)'; } @immutable @@ -126,11 +129,15 @@ class VeilidAPIExceptionShutdown implements VeilidAPIException { @immutable class VeilidAPIExceptionInvalidTarget implements VeilidAPIException { - @override - String toString() => 'VeilidAPIException: InvalidTarget'; + // + const VeilidAPIExceptionInvalidTarget(this.message); + final String message; @override - String toDisplayError() => 'Invalid target'; + String toString() => 'VeilidAPIException: InvalidTarget (message: $message)'; + + @override + String toDisplayError() => 'Invalid target: (message: $message)'; } @immutable diff --git a/veilid-flutter/rust/Cargo.toml b/veilid-flutter/rust/Cargo.toml index ac0e27c5..18935cab 100644 --- a/veilid-flutter/rust/Cargo.toml +++ b/veilid-flutter/rust/Cargo.toml @@ -26,20 +26,21 @@ rt-tokio = [ "tokio-util", "opentelemetry/rt-tokio", ] +debug-load = ["dep:ctor", "dep:libc-print", "dep:android_log-sys", "dep:oslog"] [dependencies] veilid-core = { path = "../../veilid-core", default-features = false } -tracing = { version = "^0", features = ["log", "attributes"] } -tracing-subscriber = "^0" -parking_lot = "^0" -backtrace = "^0" -serde_json = "^1" -serde = "^1" -futures-util = { version = "^0", default-features = false, features = [ +tracing = { version = "0.1.37", features = ["log", "attributes"] } +tracing-subscriber = "0.3.17" +parking_lot = "0.12.1" +backtrace = "0.3.69" +serde_json = "1.0.107" +serde = "1.0.188" +futures-util = { version = "0.3.28", default-features = false, features = [ "alloc", ] } -cfg-if = "^1" -data-encoding = { version = "^2" } +cfg-if = "1.0.0" +data-encoding = { version = "2.4.0" } # Dependencies for native builds only # Linux, Windows, Mac, iOS, Android @@ -48,19 +49,27 @@ tracing-opentelemetry = "0.21" opentelemetry = { version = "0.20" } opentelemetry-otlp = { version = "0.13" } opentelemetry-semantic-conventions = "0.12" -async-std = { version = "^1", features = ["unstable"], optional = true } -tokio = { version = "^1", features = ["full"], optional = true } -tokio-stream = { version = "^0", features = ["net"], optional = true } -tokio-util = { version = "^0", features = ["compat"], optional = true } -allo-isolate = "^0" -ffi-support = "^0" -lazy_static = "^1" -hostname = "^0" +async-std = { version = "1.12.0", features = ["unstable"], optional = true } +tokio = { version = "1.32.0", features = ["full"], optional = true } +tokio-stream = { version = "0.1.14", features = ["net"], optional = true } +tokio-util = { version = "0.7.8", features = ["compat"], optional = true } +allo-isolate = "0.1.20" +ffi-support = "0.4.4" +lazy_static = "1.4.0" +hostname = "0.3.1" +ctor = { version = "0.2.5", optional = true } +libc-print = { version = "0.1.22", optional = true } + # Dependencies for WASM builds only [target.'cfg(target_arch = "wasm32")'.dependencies] # Dependencies for Android builds only [target.'cfg(target_os = "android")'.dependencies] -jni = "^0" +jni = "0.21.1" paranoid-android = "0.2.1" +android_log-sys = { version = "0.3.1", optional = true } + +# Dependencies for Android builds only +[target.'cfg(target_os = "ios")'.dependencies] +oslog = { version = "0.2.0", default-features = false, optional = true } diff --git a/veilid-flutter/rust/src/dart_ffi.rs b/veilid-flutter/rust/src/dart_ffi.rs index 00388f67..9b07bc4c 100644 --- a/veilid-flutter/rust/src/dart_ffi.rs +++ b/veilid-flutter/rust/src/dart_ffi.rs @@ -1,4 +1,3 @@ -use veilid_core::tools::*; use crate::dart_isolate_wrapper::*; use allo_isolate::*; use cfg_if::*; @@ -14,8 +13,47 @@ use std::os::raw::c_char; use std::sync::Arc; use tracing::*; use tracing_subscriber::prelude::*; +use veilid_core::tools::*; use veilid_core::Encodable as _; +// Detect flutter load/unload +cfg_if! { + if #[cfg(feature="debug-load")] { + #[ctor::ctor] + fn onload() { + cfg_if! { + if #[cfg(target_os="android")] { + use android_log_sys::*; + use std::ffi::{CString, c_int, c_char}; + unsafe { + let tag = CString::new("veilid").unwrap(); + let text = CString::new(">>> VEILID-FLUTTER LOADED <<<").unwrap(); + __android_log_write(LogPriority::INFO as c_int, tag.as_ptr() as *const c_char, text.as_ptr() as *const c_char); + } + } else { + libc_print::libc_println!(">>> VEILID-FLUTTER LOADED <<<"); + } + } + } + #[ctor::dtor] + fn onunload() { + cfg_if! { + if #[cfg(target_os="android")] { + use android_log_sys::*; + use std::ffi::{CString, c_int, c_char}; + unsafe { + let tag = CString::new("veilid").unwrap(); + let text = CString::new(">>> VEILID-FLUTTER UNLOADED <<<").unwrap(); + __android_log_write(LogPriority::INFO as c_int, tag.as_ptr() as *const c_char, text.as_ptr() as *const c_char); + } + } else { + libc_print::libc_println!(">>> VEILID-FLUTTER UNLOADED <<<"); + } + } + } + } +} + // Globals lazy_static! { static ref CORE_INITIALIZED: Mutex = Mutex::new(false); @@ -24,8 +62,7 @@ lazy_static! { Mutex::new(BTreeMap::new()); static ref ROUTING_CONTEXTS: Mutex> = Mutex::new(BTreeMap::new()); - static ref TABLE_DBS: Mutex> = - Mutex::new(BTreeMap::new()); + static ref TABLE_DBS: Mutex> = Mutex::new(BTreeMap::new()); static ref TABLE_DB_TRANSACTIONS: Mutex> = Mutex::new(BTreeMap::new()); } @@ -144,7 +181,6 @@ pub extern "C" fn initialize_veilid_flutter(dart_post_c_object_ptr: ffi::DartPos #[no_mangle] #[instrument] pub extern "C" fn initialize_veilid_core(platform_config: FfiStr) { - // Only do this once, ever // Until we have Dart native finalizers running on hot-restart, this will cause a crash if run more than once { @@ -249,7 +285,6 @@ pub extern "C" fn initialize_veilid_core(platform_config: FfiStr) { .try_init() .map_err(|e| format!("failed to initialize logging: {}", e)) .expect("failed to initalize ffi platform"); - } #[no_mangle] @@ -356,7 +391,10 @@ pub extern "C" fn shutdown_veilid_core(port: i64) { }); } -fn add_routing_context(rc: &mut BTreeMap, routing_context: veilid_core::RoutingContext) -> u32 { +fn add_routing_context( + rc: &mut BTreeMap, + routing_context: veilid_core::RoutingContext, +) -> u32 { let mut next_id: u32 = 1; while rc.contains_key(&next_id) { next_id += 1; @@ -394,7 +432,7 @@ pub extern "C" fn routing_context_with_privacy(id: u32) -> u32 { let Ok(routing_context) = routing_context.clone().with_privacy() else { return 0; }; - + add_routing_context(&mut rc, routing_context) } @@ -407,10 +445,13 @@ pub extern "C" fn routing_context_with_custom_privacy(id: u32, safety_selection: 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; }; - + add_routing_context(&mut rc, routing_context) } @@ -424,26 +465,25 @@ pub extern "C" fn routing_context_with_sequencing(id: u32, sequencing: FfiStr) - return 0; }; let routing_context = routing_context.clone().with_sequencing(sequencing); - + add_routing_context(&mut rc, routing_context) } - #[no_mangle] pub extern "C" fn routing_context_app_call(port: i64, id: u32, target: FfiStr, request: FfiStr) { let target_string: String = target.into_opt_string().unwrap(); let request: Vec = data_encoding::BASE64URL_NOPAD - .decode( - request.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(request.into_opt_string().unwrap().as_bytes()) .unwrap(); DartIsolateWrapper::new(port).spawn_result(async move { let routing_context = { let rc = ROUTING_CONTEXTS.lock(); 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() }; @@ -459,21 +499,21 @@ pub extern "C" fn routing_context_app_call(port: i64, id: u32, target: FfiStr, r pub extern "C" fn routing_context_app_message(port: i64, id: u32, target: FfiStr, message: FfiStr) { let target_string: String = target.into_opt_string().unwrap(); let message: Vec = data_encoding::BASE64URL_NOPAD - .decode( - message.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(message.into_opt_string().unwrap().as_bytes()) .unwrap(); - DartIsolateWrapper::new(port).spawn_result(async move { + DartIsolateWrapper::new(port).spawn_result(async move { let routing_context = { let rc = ROUTING_CONTEXTS.lock(); 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() }; - + let veilid_api = get_veilid_api().await?; let target = veilid_api.parse_as_target(target_string).await?; routing_context.app_message(target, message).await?; @@ -488,31 +528,45 @@ pub extern "C" fn routing_context_create_dht_record(port: i64, id: u32, schema: } else { Some(veilid_core::FourCC::from(kind)) }; - let schema: veilid_core::DHTSchema = veilid_core::deserialize_opt_json(schema.into_opt_string()).unwrap(); + let schema: veilid_core::DHTSchema = + veilid_core::deserialize_opt_json(schema.into_opt_string()).unwrap(); - DartIsolateWrapper::new(port).spawn_result_json(async move { + DartIsolateWrapper::new(port).spawn_result_json(async move { let routing_context = { let rc = ROUTING_CONTEXTS.lock(); 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() }; - - let dht_record_descriptor = routing_context.create_dht_record(schema, crypto_kind).await?; + + let dht_record_descriptor = routing_context + .create_dht_record(schema, crypto_kind) + .await?; APIResult::Ok(dht_record_descriptor) }); } #[no_mangle] pub extern "C" fn routing_context_open_dht_record(port: i64, id: u32, key: FfiStr, writer: FfiStr) { - let key: veilid_core::TypedKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); - let writer: Option = writer.into_opt_string().map(|s| veilid_core::deserialize_json(&s).unwrap()); - DartIsolateWrapper::new(port).spawn_result_json(async move { + let key: veilid_core::TypedKey = + veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); + let writer: Option = writer + .into_opt_string() + .map(|s| veilid_core::deserialize_json(&s).unwrap()); + DartIsolateWrapper::new(port).spawn_result_json(async move { let routing_context = { let rc = ROUTING_CONTEXTS.lock(); let Some(routing_context) = rc.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("routing_context_open_dht_record", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "routing_context_open_dht_record", + "id", + id, + )); }; routing_context.clone() }; @@ -521,15 +575,19 @@ pub extern "C" fn routing_context_open_dht_record(port: i64, id: u32, key: FfiSt }); } - #[no_mangle] pub extern "C" fn routing_context_close_dht_record(port: i64, id: u32, key: FfiStr) { - let key: veilid_core::TypedKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); - DartIsolateWrapper::new(port).spawn_result(async move { + let key: veilid_core::TypedKey = + veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); + DartIsolateWrapper::new(port).spawn_result(async move { let routing_context = { let rc = ROUTING_CONTEXTS.lock(); 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() }; @@ -538,15 +596,19 @@ pub extern "C" fn routing_context_close_dht_record(port: i64, id: u32, key: FfiS }); } - #[no_mangle] pub extern "C" fn routing_context_delete_dht_record(port: i64, id: u32, key: FfiStr) { - let key: veilid_core::TypedKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); - DartIsolateWrapper::new(port).spawn_result(async move { + let key: veilid_core::TypedKey = + veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); + DartIsolateWrapper::new(port).spawn_result(async move { let routing_context = { let rc = ROUTING_CONTEXTS.lock(); 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() }; @@ -555,40 +617,58 @@ pub extern "C" fn routing_context_delete_dht_record(port: i64, id: u32, key: Ffi }); } - #[no_mangle] -pub extern "C" fn routing_context_get_dht_value(port: i64, id: u32, key: FfiStr, subkey: u32, force_refresh: bool) { - let key: veilid_core::TypedKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); - DartIsolateWrapper::new(port).spawn_result_json(async move { +pub extern "C" fn routing_context_get_dht_value( + port: i64, + id: u32, + key: FfiStr, + subkey: u32, + force_refresh: bool, +) { + let key: veilid_core::TypedKey = + veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); + DartIsolateWrapper::new(port).spawn_result_json(async move { let routing_context = { let rc = ROUTING_CONTEXTS.lock(); 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() }; - let res = routing_context.get_dht_value(key, subkey, force_refresh).await?; + let res = routing_context + .get_dht_value(key, subkey, force_refresh) + .await?; APIResult::Ok(res) }); } - #[no_mangle] -pub extern "C" fn routing_context_set_dht_value(port: i64, id: u32, key: FfiStr, subkey: u32, data: FfiStr) { - let key: veilid_core::TypedKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); +pub extern "C" fn routing_context_set_dht_value( + port: i64, + id: u32, + key: FfiStr, + subkey: u32, + data: FfiStr, +) { + let key: veilid_core::TypedKey = + veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); let data: Vec = data_encoding::BASE64URL_NOPAD - .decode( - data.into_opt_string() - .unwrap() - .as_bytes(), - ) - .unwrap(); + .decode(data.into_opt_string().unwrap().as_bytes()) + .unwrap(); - DartIsolateWrapper::new(port).spawn_result_json(async move { + DartIsolateWrapper::new(port).spawn_result_json(async move { let routing_context = { let rc = ROUTING_CONTEXTS.lock(); 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() }; @@ -597,37 +677,61 @@ pub extern "C" fn routing_context_set_dht_value(port: i64, id: u32, key: FfiStr, }); } - #[no_mangle] -pub extern "C" fn routing_context_watch_dht_values(port: i64, id: u32, key: FfiStr, subkeys: FfiStr, expiration: u64, count: u32) { - let key: veilid_core::TypedKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); - let subkeys: veilid_core::ValueSubkeyRangeSet = veilid_core::deserialize_opt_json(subkeys.into_opt_string()).unwrap(); +pub extern "C" fn routing_context_watch_dht_values( + port: i64, + id: u32, + key: FfiStr, + subkeys: FfiStr, + expiration: u64, + count: u32, +) { + let key: veilid_core::TypedKey = + veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); + let subkeys: veilid_core::ValueSubkeyRangeSet = + veilid_core::deserialize_opt_json(subkeys.into_opt_string()).unwrap(); let expiration = veilid_core::Timestamp::from(expiration); - DartIsolateWrapper::new(port).spawn_result(async move { + DartIsolateWrapper::new(port).spawn_result(async move { let routing_context = { let rc = ROUTING_CONTEXTS.lock(); 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() }; - let res = routing_context.watch_dht_values(key, subkeys, expiration, count).await?; + let res = routing_context + .watch_dht_values(key, subkeys, expiration, count) + .await?; APIResult::Ok(res.as_u64()) }); } - #[no_mangle] -pub extern "C" fn routing_context_cancel_dht_watch(port: i64, id: u32, key: FfiStr, subkeys: FfiStr) { - let key: veilid_core::TypedKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); - let subkeys: veilid_core::ValueSubkeyRangeSet = veilid_core::deserialize_opt_json(subkeys.into_opt_string()).unwrap(); +pub extern "C" fn routing_context_cancel_dht_watch( + port: i64, + id: u32, + key: FfiStr, + subkeys: FfiStr, +) { + let key: veilid_core::TypedKey = + veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); + let subkeys: veilid_core::ValueSubkeyRangeSet = + veilid_core::deserialize_opt_json(subkeys.into_opt_string()).unwrap(); - DartIsolateWrapper::new(port).spawn_result(async move { + DartIsolateWrapper::new(port).spawn_result(async move { let routing_context = { let rc = ROUTING_CONTEXTS.lock(); 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() }; @@ -636,7 +740,6 @@ pub extern "C" fn routing_context_cancel_dht_watch(port: i64, id: u32, key: FfiS }); } - #[no_mangle] pub extern "C" fn new_private_route(port: i64) { DartIsolateWrapper::new(port).spawn_result_json(async move { @@ -707,7 +810,9 @@ pub extern "C" fn app_call_reply(port: i64, call_id: FfiStr, message: FfiStr) { let call_id = match call_id.parse() { Ok(v) => v, Err(e) => { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(e, "call_id", call_id)) + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + e, "call_id", call_id, + )) } }; let message = data_encoding::BASE64URL_NOPAD @@ -735,7 +840,10 @@ pub extern "C" fn open_table_db(port: i64, name: FfiStr, column_count: u32) { DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; let tstore = veilid_api.table_store()?; - let table_db = tstore.open(&name, column_count).await.map_err(veilid_core::VeilidAPIError::generic)?; + let table_db = tstore + .open(&name, column_count) + .await + .map_err(veilid_core::VeilidAPIError::generic)?; let new_id = add_table_db(table_db); APIResult::Ok(new_id) }); @@ -756,7 +864,10 @@ pub extern "C" fn delete_table_db(port: i64, name: FfiStr) { DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; let tstore = veilid_api.table_store()?; - let deleted = tstore.delete(&name).await.map_err(veilid_core::VeilidAPIError::generic)?; + let deleted = tstore + .delete(&name) + .await + .map_err(veilid_core::VeilidAPIError::generic)?; APIResult::Ok(deleted) }); } @@ -779,13 +890,20 @@ pub extern "C" fn table_db_get_keys(port: i64, id: u32, col: u32) { let table_db = { let table_dbs = TABLE_DBS.lock(); let Some(table_db) = table_dbs.get(&id) else { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument("table_db_get_keys", "id", id)); + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + "table_db_get_keys", + "id", + id, + )); }; table_db.clone() }; let keys = table_db.get_keys(col).await?; - let out: Vec = keys.into_iter().map(|k| BASE64URL_NOPAD.encode(&k)).collect(); + let out: Vec = keys + .into_iter() + .map(|k| BASE64URL_NOPAD.encode(&k)) + .collect(); APIResult::Ok(out) }); } @@ -807,7 +925,7 @@ pub extern "C" fn table_db_transact(id: u32) -> u32 { return 0; }; let tdbt = table_db.clone().transact(); - + add_table_db_transaction(tdbt) } @@ -820,18 +938,21 @@ pub extern "C" fn release_table_db_transaction(id: u32) -> i32 { 1 } - #[no_mangle] pub extern "C" fn table_db_transaction_commit(port: i64, id: u32) { DartIsolateWrapper::new(port).spawn_result(async move { let tdbt = { let tdbts = TABLE_DB_TRANSACTIONS.lock(); 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() }; - + tdbt.commit().await?; APIRESULT_VOID }); @@ -842,65 +963,70 @@ pub extern "C" fn table_db_transaction_rollback(port: i64, id: u32) { let tdbt = { let tdbts = TABLE_DB_TRANSACTIONS.lock(); 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() }; - + tdbt.rollback(); APIRESULT_VOID }); } #[no_mangle] -pub extern "C" fn table_db_transaction_store(port: i64, id: u32, col: u32, key: FfiStr, value: FfiStr) { +pub extern "C" fn table_db_transaction_store( + port: i64, + id: u32, + col: u32, + key: FfiStr, + value: FfiStr, +) { let key: Vec = data_encoding::BASE64URL_NOPAD - .decode( - key.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(key.into_opt_string().unwrap().as_bytes()) .unwrap(); let value: Vec = data_encoding::BASE64URL_NOPAD - .decode( - value.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(value.into_opt_string().unwrap().as_bytes()) .unwrap(); DartIsolateWrapper::new(port).spawn_result(async move { let tdbt = { let tdbts = TABLE_DB_TRANSACTIONS.lock(); 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() }; - + tdbt.store(col, &key, &value)?; APIRESULT_VOID }); } - #[no_mangle] pub extern "C" fn table_db_transaction_delete(port: i64, id: u32, col: u32, key: FfiStr) { let key: Vec = data_encoding::BASE64URL_NOPAD - .decode( - key.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(key.into_opt_string().unwrap().as_bytes()) .unwrap(); DartIsolateWrapper::new(port).spawn_result(async move { let tdbt = { let tdbts = TABLE_DB_TRANSACTIONS.lock(); 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() }; - + tdbt.delete(col, &key)?; APIRESULT_VOID }); @@ -909,28 +1035,24 @@ pub extern "C" fn table_db_transaction_delete(port: i64, id: u32, col: u32, key: #[no_mangle] pub extern "C" fn table_db_store(port: i64, id: u32, col: u32, key: FfiStr, value: FfiStr) { let key: Vec = data_encoding::BASE64URL_NOPAD - .decode( - key.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(key.into_opt_string().unwrap().as_bytes()) .unwrap(); let value: Vec = data_encoding::BASE64URL_NOPAD - .decode( - value.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(value.into_opt_string().unwrap().as_bytes()) .unwrap(); DartIsolateWrapper::new(port).spawn_result(async move { let table_db = { let table_dbs = TABLE_DBS.lock(); 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() }; - + table_db.store(col, &key, &value).await?; APIRESULT_VOID }); @@ -939,20 +1061,21 @@ pub extern "C" fn table_db_store(port: i64, id: u32, col: u32, key: FfiStr, valu #[no_mangle] pub extern "C" fn table_db_load(port: i64, id: u32, col: u32, key: FfiStr) { let key: Vec = data_encoding::BASE64URL_NOPAD - .decode(key.into_opt_string() - .unwrap() - .as_bytes() - ) + .decode(key.into_opt_string().unwrap().as_bytes()) .unwrap(); DartIsolateWrapper::new(port).spawn_result(async move { let table_db = { let table_dbs = TABLE_DBS.lock(); 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() }; - + let out = table_db.load(col, &key).await?; let out = out.map(|x| data_encoding::BASE64URL_NOPAD.encode(&x)); APIResult::Ok(out) @@ -962,31 +1085,36 @@ pub extern "C" fn table_db_load(port: i64, id: u32, col: u32, key: FfiStr) { #[no_mangle] pub extern "C" fn table_db_delete(port: i64, id: u32, col: u32, key: FfiStr) { let key: Vec = data_encoding::BASE64URL_NOPAD - .decode( - key.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(key.into_opt_string().unwrap().as_bytes()) .unwrap(); DartIsolateWrapper::new(port).spawn_result(async move { let table_db = { let table_dbs = TABLE_DBS.lock(); 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() }; - + let out = table_db.delete(col, &key).await?; let out = out.map(|x| data_encoding::BASE64URL_NOPAD.encode(&x)); APIResult::Ok(out) }); } - #[no_mangle] pub extern "C" fn valid_crypto_kinds() -> *mut c_char { - veilid_core::serialize_json(veilid_core::VALID_CRYPTO_KINDS.iter().map(|k| (*k).into()).collect::>()).into_ffi_value() + veilid_core::serialize_json( + veilid_core::VALID_CRYPTO_KINDS + .iter() + .map(|k| (*k).into()) + .collect::>(), + ) + .into_ffi_value() } #[no_mangle] @@ -1000,12 +1128,8 @@ pub extern "C" fn verify_signatures(port: i64, node_ids: FfiStr, data: FfiStr, s veilid_core::deserialize_opt_json(node_ids.into_opt_string()).unwrap(); let data: Vec = data_encoding::BASE64URL_NOPAD - .decode( - data.into_opt_string() - .unwrap() - .as_bytes(), - ) - .unwrap(); + .decode(data.into_opt_string().unwrap().as_bytes()) + .unwrap(); let typed_signatures: Vec = veilid_core::deserialize_opt_json(signatures.into_opt_string()).unwrap(); @@ -1020,14 +1144,9 @@ pub extern "C" fn verify_signatures(port: i64, node_ids: FfiStr, data: FfiStr, s #[no_mangle] pub extern "C" fn generate_signatures(port: i64, data: FfiStr, key_pairs: FfiStr) { - let data: Vec = data_encoding::BASE64URL_NOPAD - .decode( - data.into_opt_string() - .unwrap() - .as_bytes(), - ) - .unwrap(); + .decode(data.into_opt_string().unwrap().as_bytes()) + .unwrap(); let key_pairs: Vec = veilid_core::deserialize_opt_json(key_pairs.into_opt_string()).unwrap(); @@ -1060,11 +1179,17 @@ pub extern "C" fn crypto_cached_dh(port: i64, kind: u32, key: FfiStr, secret: Ff veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); let secret: veilid_core::SecretKey = veilid_core::deserialize_opt_json(secret.into_opt_string()).unwrap(); - + DartIsolateWrapper::new(port).spawn_result_json(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_cached_dh", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_cached_dh", + "kind", + kind.to_string(), + ) + })?; let out = csv.cached_dh(&key, &secret)?; APIResult::Ok(out) }); @@ -1073,7 +1198,7 @@ pub extern "C" fn crypto_cached_dh(port: i64, kind: u32, key: FfiStr, secret: Ff #[no_mangle] pub extern "C" fn crypto_compute_dh(port: i64, kind: u32, key: FfiStr, secret: FfiStr) { let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind); - + let key: veilid_core::PublicKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); let secret: veilid_core::SecretKey = @@ -1082,13 +1207,18 @@ pub extern "C" fn crypto_compute_dh(port: i64, kind: u32, key: FfiStr, secret: F DartIsolateWrapper::new(port).spawn_result_json(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_compute_dh", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_compute_dh", + "kind", + kind.to_string(), + ) + })?; let out = csv.compute_dh(&key, &secret)?; APIResult::Ok(out) }); } - #[no_mangle] pub extern "C" fn crypto_random_bytes(port: i64, kind: u32, len: u32) { let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind); @@ -1096,7 +1226,13 @@ pub extern "C" fn crypto_random_bytes(port: i64, kind: u32, len: u32) { DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_random_bytes", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_random_bytes", + "kind", + kind.to_string(), + ) + })?; let out = csv.random_bytes(len); let out = data_encoding::BASE64URL_NOPAD.encode(&out); APIResult::Ok(out) @@ -1110,82 +1246,96 @@ pub extern "C" fn crypto_default_salt_length(port: i64, kind: u32) { DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_default_salt_length", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_default_salt_length", + "kind", + kind.to_string(), + ) + })?; let out = csv.default_salt_length(); APIResult::Ok(out) }); } #[no_mangle] -pub extern "C" fn crypto_hash_password(port: i64, kind: u32, password: FfiStr, salt: FfiStr ) { +pub extern "C" fn crypto_hash_password(port: i64, kind: u32, password: FfiStr, salt: FfiStr) { let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind); let password: Vec = data_encoding::BASE64URL_NOPAD - .decode( - password.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(password.into_opt_string().unwrap().as_bytes()) .unwrap(); let salt: Vec = data_encoding::BASE64URL_NOPAD - .decode( - salt.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(salt.into_opt_string().unwrap().as_bytes()) .unwrap(); DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_hash_password", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_hash_password", + "kind", + kind.to_string(), + ) + })?; let out = csv.hash_password(&password, &salt)?; APIResult::Ok(out) }); } #[no_mangle] -pub extern "C" fn crypto_verify_password(port: i64, kind: u32, password: FfiStr, password_hash: FfiStr ) { +pub extern "C" fn crypto_verify_password( + port: i64, + kind: u32, + password: FfiStr, + password_hash: FfiStr, +) { let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind); let password: Vec = data_encoding::BASE64URL_NOPAD - .decode( - password.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(password.into_opt_string().unwrap().as_bytes()) .unwrap(); let password_hash = password_hash.into_opt_string().unwrap(); DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_verify_password", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_verify_password", + "kind", + kind.to_string(), + ) + })?; let out = csv.verify_password(&password, &password_hash)?; APIResult::Ok(out) }); } #[no_mangle] -pub extern "C" fn crypto_derive_shared_secret(port: i64, kind: u32, password: FfiStr, salt: FfiStr ) { +pub extern "C" fn crypto_derive_shared_secret( + port: i64, + kind: u32, + password: FfiStr, + salt: FfiStr, +) { let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind); let password: Vec = data_encoding::BASE64URL_NOPAD - .decode( - password.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(password.into_opt_string().unwrap().as_bytes()) .unwrap(); - let salt: Vec = data_encoding::BASE64URL_NOPAD - .decode( - salt.into_opt_string() - .unwrap() - .as_bytes(), - ) + let salt: Vec = data_encoding::BASE64URL_NOPAD + .decode(salt.into_opt_string().unwrap().as_bytes()) .unwrap(); DartIsolateWrapper::new(port).spawn_result_json(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_derive_shared_secret", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_derive_shared_secret", + "kind", + kind.to_string(), + ) + })?; let out = csv.derive_shared_secret(&password, &salt)?; APIResult::Ok(out) }); @@ -1198,7 +1348,13 @@ pub extern "C" fn crypto_random_nonce(port: i64, kind: u32) { DartIsolateWrapper::new(port).spawn_result_json(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_random_nonce", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_random_nonce", + "kind", + kind.to_string(), + ) + })?; let out = csv.random_nonce(); APIResult::Ok(out) }); @@ -1211,7 +1367,13 @@ pub extern "C" fn crypto_random_shared_secret(port: i64, kind: u32) { DartIsolateWrapper::new(port).spawn_result_json(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_random_shared_secret", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_random_shared_secret", + "kind", + kind.to_string(), + ) + })?; let out = csv.random_shared_secret(); APIResult::Ok(out) }); @@ -1224,7 +1386,13 @@ pub extern "C" fn crypto_generate_key_pair(port: i64, kind: u32) { DartIsolateWrapper::new(port).spawn_result_json(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_generate_key_pair", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_generate_key_pair", + "kind", + kind.to_string(), + ) + })?; let out = csv.generate_keypair(); APIResult::Ok(out) }); @@ -1235,17 +1403,19 @@ pub extern "C" fn crypto_generate_hash(port: i64, kind: u32, data: FfiStr) { let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind); let data: Vec = data_encoding::BASE64URL_NOPAD - .decode( - data.into_opt_string() - .unwrap() - .as_bytes(), - ) - .unwrap(); + .decode(data.into_opt_string().unwrap().as_bytes()) + .unwrap(); DartIsolateWrapper::new(port).spawn_result_json(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_generate_hash", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_generate_hash", + "kind", + kind.to_string(), + ) + })?; let out = csv.generate_hash(&data); APIResult::Ok(out) }); @@ -1263,7 +1433,13 @@ pub extern "C" fn crypto_validate_key_pair(port: i64, kind: u32, key: FfiStr, se DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_validate_key_pair", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_validate_key_pair", + "kind", + kind.to_string(), + ) + })?; let out = csv.validate_keypair(&key, &secret); APIResult::Ok(out) }); @@ -1274,12 +1450,8 @@ pub extern "C" fn crypto_validate_hash(port: i64, kind: u32, data: FfiStr, hash: let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind); let data: Vec = data_encoding::BASE64URL_NOPAD - .decode( - data.into_opt_string() - .unwrap() - .as_bytes(), - ) - .unwrap(); + .decode(data.into_opt_string().unwrap().as_bytes()) + .unwrap(); let hash: veilid_core::HashDigest = veilid_core::deserialize_opt_json(hash.into_opt_string()).unwrap(); @@ -1287,7 +1459,13 @@ pub extern "C" fn crypto_validate_hash(port: i64, kind: u32, data: FfiStr, hash: DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_validate_hash", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_validate_hash", + "kind", + kind.to_string(), + ) + })?; let out = csv.validate_hash(&data, &hash); APIResult::Ok(out) }); @@ -1305,7 +1483,13 @@ pub extern "C" fn crypto_distance(port: i64, kind: u32, key1: FfiStr, key2: FfiS DartIsolateWrapper::new(port).spawn_result_json(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_distance", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_distance", + "kind", + kind.to_string(), + ) + })?; let out = csv.distance(&key1, &key2); APIResult::Ok(out) }); @@ -1320,42 +1504,44 @@ pub extern "C" fn crypto_sign(port: i64, kind: u32, key: FfiStr, secret: FfiStr, let secret: veilid_core::CryptoKey = veilid_core::deserialize_opt_json(secret.into_opt_string()).unwrap(); let data: Vec = data_encoding::BASE64URL_NOPAD - .decode( - data.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(data.into_opt_string().unwrap().as_bytes()) .unwrap(); - + DartIsolateWrapper::new(port).spawn_result_json(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_sign", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument("crypto_sign", "kind", kind.to_string()) + })?; let out = csv.sign(&key, &secret, &data)?; APIResult::Ok(out) }); } #[no_mangle] -pub extern "C" fn crypto_verify(port: i64, kind: u32, key: FfiStr, data: FfiStr, signature: FfiStr) { +pub extern "C" fn crypto_verify( + port: i64, + kind: u32, + key: FfiStr, + data: FfiStr, + signature: FfiStr, +) { let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind); let key: veilid_core::CryptoKey = veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); let data: Vec = data_encoding::BASE64URL_NOPAD - .decode( - data.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(data.into_opt_string().unwrap().as_bytes()) .unwrap(); let signature: veilid_core::Signature = veilid_core::deserialize_opt_json(signature.into_opt_string()).unwrap(); - + DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_verify", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument("crypto_verify", "kind", kind.to_string()) + })?; csv.verify(&key, &data, &signature)?; APIRESULT_VOID }); @@ -1364,106 +1550,150 @@ pub extern "C" fn crypto_verify(port: i64, kind: u32, key: FfiStr, data: FfiStr, #[no_mangle] pub extern "C" fn crypto_aead_overhead(port: i64, kind: u32) { let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind); - + DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_aead_overhead", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_aead_overhead", + "kind", + kind.to_string(), + ) + })?; let out = csv.aead_overhead(); APIResult::Ok(out) }); } #[no_mangle] -pub extern "C" fn crypto_decrypt_aead(port: i64, kind: u32, body: FfiStr, nonce: FfiStr, shared_secret: FfiStr, associated_data: FfiStr) { +pub extern "C" fn crypto_decrypt_aead( + port: i64, + kind: u32, + body: FfiStr, + nonce: FfiStr, + shared_secret: FfiStr, + associated_data: FfiStr, +) { let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind); - + let body: Vec = data_encoding::BASE64URL_NOPAD - .decode( - body.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(body.into_opt_string().unwrap().as_bytes()) .unwrap(); - + let nonce: veilid_core::Nonce = veilid_core::deserialize_opt_json(nonce.into_opt_string()).unwrap(); - + let shared_secret: veilid_core::SharedSecret = veilid_core::deserialize_opt_json(shared_secret.into_opt_string()).unwrap(); - let associated_data: Option> = associated_data.into_opt_string().map(|s| data_encoding::BASE64URL_NOPAD.decode(s.as_bytes()).unwrap()); + let associated_data: Option> = associated_data + .into_opt_string() + .map(|s| data_encoding::BASE64URL_NOPAD.decode(s.as_bytes()).unwrap()); DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_decrypt_aead", "kind", kind.to_string()))?; - let out = csv.decrypt_aead(&body, &nonce, &shared_secret, match &associated_data { - Some(ad) => Some(ad.as_slice()), - None => None + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_decrypt_aead", + "kind", + kind.to_string(), + ) })?; + let out = csv.decrypt_aead( + &body, + &nonce, + &shared_secret, + match &associated_data { + Some(ad) => Some(ad.as_slice()), + None => None, + }, + )?; let out = data_encoding::BASE64URL_NOPAD.encode(&out); APIResult::Ok(out) }); } #[no_mangle] -pub extern "C" fn crypto_encrypt_aead(port: i64, kind: u32, body: FfiStr, nonce: FfiStr, shared_secret: FfiStr, associated_data: FfiStr) { +pub extern "C" fn crypto_encrypt_aead( + port: i64, + kind: u32, + body: FfiStr, + nonce: FfiStr, + shared_secret: FfiStr, + associated_data: FfiStr, +) { let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind); - + let body: Vec = data_encoding::BASE64URL_NOPAD - .decode( - body.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(body.into_opt_string().unwrap().as_bytes()) .unwrap(); - + let nonce: veilid_core::Nonce = veilid_core::deserialize_opt_json(nonce.into_opt_string()).unwrap(); - + let shared_secret: veilid_core::SharedSecret = veilid_core::deserialize_opt_json(shared_secret.into_opt_string()).unwrap(); - let associated_data: Option> = associated_data.into_opt_string().map(|s| data_encoding::BASE64URL_NOPAD.decode(s.as_bytes()).unwrap()); + let associated_data: Option> = associated_data + .into_opt_string() + .map(|s| data_encoding::BASE64URL_NOPAD.decode(s.as_bytes()).unwrap()); DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_encrypt_aead", "kind", kind.to_string()))?; - let out = csv.encrypt_aead(&body, &nonce, &shared_secret, match &associated_data { - Some(ad) => Some(ad.as_slice()), - None => None + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_encrypt_aead", + "kind", + kind.to_string(), + ) })?; + let out = csv.encrypt_aead( + &body, + &nonce, + &shared_secret, + match &associated_data { + Some(ad) => Some(ad.as_slice()), + None => None, + }, + )?; let out = data_encoding::BASE64URL_NOPAD.encode(&out); APIResult::Ok(out) }); } - - #[no_mangle] -pub extern "C" fn crypto_crypt_no_auth(port: i64, kind: u32, body: FfiStr, nonce: FfiStr, shared_secret: FfiStr) { +pub extern "C" fn crypto_crypt_no_auth( + port: i64, + kind: u32, + body: FfiStr, + nonce: FfiStr, + shared_secret: FfiStr, +) { let kind: veilid_core::CryptoKind = veilid_core::FourCC::from(kind); - + let mut body: Vec = data_encoding::BASE64URL_NOPAD - .decode( - body.into_opt_string() - .unwrap() - .as_bytes(), - ) + .decode(body.into_opt_string().unwrap().as_bytes()) .unwrap(); - + let nonce: veilid_core::Nonce = veilid_core::deserialize_opt_json(nonce.into_opt_string()).unwrap(); - + let shared_secret: veilid_core::SharedSecret = veilid_core::deserialize_opt_json(shared_secret.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; let crypto = veilid_api.crypto()?; - let csv = crypto.get(kind).ok_or_else(|| veilid_core::VeilidAPIError::invalid_argument("crypto_crypt_no_auth", "kind", kind.to_string()))?; + let csv = crypto.get(kind).ok_or_else(|| { + veilid_core::VeilidAPIError::invalid_argument( + "crypto_crypt_no_auth", + "kind", + kind.to_string(), + ) + })?; csv.crypt_in_place_no_auth(&mut body, &nonce, &shared_secret); let body = data_encoding::BASE64URL_NOPAD.encode(&body); APIResult::Ok(body) diff --git a/veilid-python/tests/test_routing_context.py b/veilid-python/tests/test_routing_context.py index f9536e8c..8b0de277 100644 --- a/veilid-python/tests/test_routing_context.py +++ b/veilid-python/tests/test_routing_context.py @@ -105,7 +105,9 @@ async def test_routing_context_app_call_loopback(): await api.debug("purge routes") # make a routing context that uses a safety route - rc = await (await api.new_routing_context()).with_privacy() + rc = await (await (await api.new_routing_context()).with_privacy()).with_sequencing( + veilid.Sequencing.ENSURE_ORDERED + ) async with rc: # make a new local private route prl, blob = await api.new_private_route() diff --git a/veilid-python/veilid/error.py b/veilid-python/veilid/error.py index 17459fcd..73745454 100644 --- a/veilid-python/veilid/error.py +++ b/veilid-python/veilid/error.py @@ -55,6 +55,7 @@ class VeilidAPIErrorTryAgain(VeilidAPIError): """Operation could not be performed at this time, retry again later""" label = "Try again" + message: str @dataclass @@ -69,6 +70,7 @@ class VeilidAPIErrorInvalidTarget(VeilidAPIError): """Target of operation is not valid""" label = "Invalid target" + message: str @dataclass diff --git a/veilid-python/veilid/schema/RecvMessage.json b/veilid-python/veilid/schema/RecvMessage.json index f4dcf69c..475ca99d 100644 --- a/veilid-python/veilid/schema/RecvMessage.json +++ b/veilid-python/veilid/schema/RecvMessage.json @@ -2974,7 +2974,8 @@ { "type": "object", "required": [ - "kind" + "kind", + "message" ], "properties": { "kind": { @@ -2982,6 +2983,9 @@ "enum": [ "TryAgain" ] + }, + "message": { + "type": "string" } } }, @@ -3002,7 +3006,8 @@ { "type": "object", "required": [ - "kind" + "kind", + "message" ], "properties": { "kind": { @@ -3010,6 +3015,9 @@ "enum": [ "InvalidTarget" ] + }, + "message": { + "type": "string" } } }, diff --git a/veilid-tools/src/assembly_buffer.rs b/veilid-tools/src/assembly_buffer.rs index abe95e5f..e31cbb1c 100644 --- a/veilid-tools/src/assembly_buffer.rs +++ b/veilid-tools/src/assembly_buffer.rs @@ -413,7 +413,7 @@ impl AssemblyBuffer { .await; // Get a message seq - let seq = self.unlocked_inner.next_seq.fetch_add(1, Ordering::Relaxed); + let seq = self.unlocked_inner.next_seq.fetch_add(1, Ordering::AcqRel); // Chunk it up let mut offset = 0usize; diff --git a/veilid-tools/src/network_result.rs b/veilid-tools/src/network_result.rs index 760cd460..21f6961a 100644 --- a/veilid-tools/src/network_result.rs +++ b/veilid-tools/src/network_result.rs @@ -218,7 +218,7 @@ impl NetworkResult { Self::Value(v) => NetworkResult::::Value(f(v)), } } - pub fn into_result(self) -> Result { + pub fn into_io_result(self) -> Result { match self { Self::Timeout => Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")), Self::ServiceUnavailable(s) => Err(io::Error::new( diff --git a/veilid-tools/src/tests/native/test_assembly_buffer.rs b/veilid-tools/src/tests/native/test_assembly_buffer.rs index 95620ad8..b717a5a8 100644 --- a/veilid-tools/src/tests/native/test_assembly_buffer.rs +++ b/veilid-tools/src/tests/native/test_assembly_buffer.rs @@ -52,7 +52,7 @@ pub async fn test_single_out_in() { // Send to input let r_message = assbuf_in .insert_frame(&frame, r_remote_addr) - .into_result() + .into_io_result() .expect("should get a value") .expect("should get something out"); @@ -114,7 +114,7 @@ pub async fn test_one_frag_out_in() { // Send to input let r_message = assbuf_in .insert_frame(&frame, r_remote_addr) - .into_result() + .into_io_result() .expect("should get a value"); // We should have gotten the same message @@ -179,7 +179,7 @@ pub async fn test_many_frags_out_in() { // Send to input let r_message = assbuf_in .insert_frame(&frame, r_remote_addr) - .into_result() + .into_io_result() .expect("should get a value"); // We should have gotten the same message @@ -244,7 +244,7 @@ pub async fn test_many_frags_out_in_single_host() { // Send to input let r_message = assbuf_in .insert_frame(&frame, r_remote_addr) - .into_result() + .into_io_result() .expect("should get a value"); // We should have gotten the same message @@ -271,7 +271,7 @@ pub async fn test_many_frags_with_drops() { let first = first.clone(); async move { // Send only first packet, drop rest - if first.swap(false, Ordering::Relaxed) { + if first.swap(false, Ordering::AcqRel) { net_tx .send_async((framed_chunk, remote_addr)) .await @@ -306,7 +306,7 @@ pub async fn test_many_frags_with_drops() { Ok(NetworkResult::Value(())) )); - first.store(true, Ordering::Relaxed); + first.store(true, Ordering::Release); } println!("all_sent len={}", all_sent.len()); @@ -322,7 +322,7 @@ pub async fn test_many_frags_with_drops() { // Send to input let r_message = assbuf_in .insert_frame(&frame, r_remote_addr) - .into_result() + .into_io_result() .expect("should get a value"); // We should have gotten the same message @@ -399,7 +399,7 @@ pub async fn test_many_frags_reordered() { // Send to input let r_message = assbuf_in .insert_frame(&frame, r_remote_addr) - .into_result() + .into_io_result() .expect("should get a value"); // We should have gotten the same message diff --git a/veilid-tools/src/tick_task.rs b/veilid-tools/src/tick_task.rs index 68ce4ddc..1d6bc5d7 100644 --- a/veilid-tools/src/tick_task.rs +++ b/veilid-tools/src/tick_task.rs @@ -58,7 +58,7 @@ impl TickTask { } pub fn is_running(&self) -> bool { - self.running.load(core::sync::atomic::Ordering::Relaxed) + self.running.load(core::sync::atomic::Ordering::Acquire) } pub async fn stop(&self) -> Result<(), E> { @@ -120,9 +120,9 @@ impl TickTask { let running = self.running.clone(); let routine = self.routine.get().unwrap()(stop_token, last_timestamp_us, now); let wrapped_routine = Box::pin(async move { - running.store(true, core::sync::atomic::Ordering::Relaxed); + running.store(true, core::sync::atomic::Ordering::Release); let out = routine.await; - running.store(false, core::sync::atomic::Ordering::Relaxed); + running.store(false, core::sync::atomic::Ordering::Release); out }); match self.single_future.single_spawn(wrapped_routine).await { diff --git a/veilid-tools/src/wasm.rs b/veilid-tools/src/wasm.rs index cfdcdd50..1d198fb3 100644 --- a/veilid-tools/src/wasm.rs +++ b/veilid-tools/src/wasm.rs @@ -18,21 +18,21 @@ extern "C" { pub fn is_browser() -> bool { static CACHE: AtomicI8 = AtomicI8::new(-1); - let cache = CACHE.load(Ordering::Relaxed); + let cache = CACHE.load(Ordering::Acquire); if cache != -1 { return cache != 0; } let res = Reflect::has(global().as_ref(), &"navigator".into()).unwrap_or_default(); - CACHE.store(res as i8, Ordering::Relaxed); + CACHE.store(res as i8, Ordering::Release); res } pub fn is_browser_https() -> bool { static CACHE: AtomicI8 = AtomicI8::new(-1); - let cache = CACHE.load(Ordering::Relaxed); + let cache = CACHE.load(Ordering::Acquire); if cache != -1 { return cache != 0; } @@ -41,7 +41,7 @@ pub fn is_browser_https() -> bool { .map(|res| res.is_truthy()) .unwrap_or_default(); - CACHE.store(res as i8, Ordering::Relaxed); + CACHE.store(res as i8, Ordering::Release); res } diff --git a/veilid-wasm/src/lib.rs b/veilid-wasm/src/lib.rs index 05d7ae25..db98d7ea 100644 --- a/veilid-wasm/src/lib.rs +++ b/veilid-wasm/src/lib.rs @@ -191,7 +191,7 @@ pub fn initialize_veilid_wasm() { static INITIALIZED: AtomicBool = AtomicBool::new(false); #[wasm_bindgen()] pub fn initialize_veilid_core(platform_config: String) { - if INITIALIZED.swap(true, Ordering::Relaxed) { + if INITIALIZED.swap(true, Ordering::AcqRel) { return; } let platform_config: VeilidWASMConfig = veilid_core::deserialize_json(&platform_config) diff --git a/veilid-wasm/src/veilid_client_js.rs b/veilid-wasm/src/veilid_client_js.rs index 2b2319b7..6c52372a 100644 --- a/veilid-wasm/src/veilid_client_js.rs +++ b/veilid-wasm/src/veilid_client_js.rs @@ -30,7 +30,7 @@ pub struct VeilidClient {} #[wasm_bindgen(js_class = veilidClient)] impl VeilidClient { pub async fn initializeCore(platformConfig: VeilidWASMConfig) { - if INITIALIZED.swap(true, Ordering::Relaxed) { + if INITIALIZED.swap(true, Ordering::AcqRel) { return; } console_error_panic_hook::set_once();