diff --git a/veilid-core/src/network_manager/connection_table.rs b/veilid-core/src/network_manager/connection_table.rs index 7fe2a18d..0a09eb21 100644 --- a/veilid-core/src/network_manager/connection_table.rs +++ b/veilid-core/src/network_manager/connection_table.rs @@ -83,6 +83,7 @@ impl ConnectionTable { unord.push(v); } } + inner.protocol_index_by_id.clear(); inner.id_by_descriptor.clear(); inner.ids_by_remote.clear(); unord diff --git a/veilid-core/src/routing_table/mod.rs b/veilid-core/src/routing_table/mod.rs index ab5d5645..60e7dfc4 100644 --- a/veilid-core/src/routing_table/mod.rs +++ b/veilid-core/src/routing_table/mod.rs @@ -542,7 +542,7 @@ impl RoutingTable { } /// Attempt to empty the routing table - /// should only be performed when there are no node_refs (detached) + /// May not empty buckets completely if there are existing node_refs pub fn purge_buckets(&self) { self.inner.write().purge_buckets(); } diff --git a/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs b/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs index 78488403..e7858816 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_set_spec_detail.rs @@ -1,7 +1,7 @@ use super::*; #[derive(Clone, Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(C), derive(CheckBytes))] +#[archive_attr(repr(C, align(8)), derive(CheckBytes))] pub struct RouteSpecDetail { /// Crypto kind pub crypto_kind: CryptoKind, @@ -13,7 +13,7 @@ pub struct RouteSpecDetail { } #[derive(Clone, Debug, RkyvArchive, RkyvSerialize, RkyvDeserialize)] -#[archive_attr(repr(C), derive(CheckBytes))] +#[archive_attr(repr(C, align(8)), derive(CheckBytes))] pub struct RouteSetSpecDetail { /// Route set per crypto kind route_set: BTreeMap, diff --git a/veilid-core/src/veilid_api/debug.rs b/veilid-core/src/veilid_api/debug.rs index 494ac1b8..db5faee7 100644 --- a/veilid-core/src/veilid_api/debug.rs +++ b/veilid-core/src/veilid_api/debug.rs @@ -471,13 +471,14 @@ impl VeilidAPI { // Purge connection table let connection_manager = self.network_manager()?.connection_manager(); connection_manager.shutdown().await; - connection_manager.startup().await; // Eliminate last_connections from routing table entries self.network_manager()? .routing_table() .purge_last_connections(); + connection_manager.startup().await; + Ok("Connections purged".to_owned()) } else if args[0] == "routes" { // Purge route spec store diff --git a/veilid-flutter/example/lib/app.dart b/veilid-flutter/example/lib/app.dart index 4cb9dfbc..00e6600a 100644 --- a/veilid-flutter/example/lib/app.dart +++ b/veilid-flutter/example/lib/app.dart @@ -118,15 +118,18 @@ class _MyAppState extends State with UiLoggy { }); await Veilid.instance.attach(); } else if (!startup && _startedUp) { - await Veilid.instance.shutdownVeilidCore(); - if (_updateProcessor != null) { - await _updateProcessor; + try { + await Veilid.instance.shutdownVeilidCore(); + if (_updateProcessor != null) { + await _updateProcessor; + } + } finally { + setState(() { + _updateProcessor = null; + _updateStream = null; + _startedUp = false; + }); } - setState(() { - _updateProcessor = null; - _updateStream = null; - _startedUp = false; - }); } } diff --git a/veilid-flutter/lib/default_config.dart b/veilid-flutter/lib/default_config.dart index d2844be6..90a5f245 100644 --- a/veilid-flutter/lib/default_config.dart +++ b/veilid-flutter/lib/default_config.dart @@ -46,12 +46,12 @@ Future getDefaultVeilidConfig(String programName) async { clientWhitelistTimeoutMs: 300000, reverseConnectionReceiptTimeMs: 5000, holePunchReceiptTimeMs: 5000, - nodeId: null, - nodeIdSecret: null, - bootstrap: kIsWeb - ? ["ws://bootstrap.dev.veilid.net:5150/ws"] - : ["bootstrap.dev.veilid.net"], routingTable: VeilidConfigRoutingTable( + nodeId: [], + nodeIdSecret: [], + bootstrap: kIsWeb + ? ["ws://bootstrap.dev.veilid.net:5150/ws"] + : ["bootstrap.dev.veilid.net"], limitOverAttached: 64, limitFullyAttached: 32, limitAttachedStrong: 16, diff --git a/veilid-flutter/lib/veilid.dart b/veilid-flutter/lib/veilid.dart index dac8d6d7..8d9a8c90 100644 --- a/veilid-flutter/lib/veilid.dart +++ b/veilid-flutter/lib/veilid.dart @@ -697,6 +697,9 @@ class VeilidConfigRPC { //////////// class VeilidConfigRoutingTable { + List nodeId; + List nodeIdSecret; + List bootstrap; int limitOverAttached; int limitFullyAttached; int limitAttachedStrong; @@ -704,6 +707,9 @@ class VeilidConfigRoutingTable { int limitAttachedWeak; VeilidConfigRoutingTable({ + required this.nodeId, + required this.nodeIdSecret, + required this.bootstrap, required this.limitOverAttached, required this.limitFullyAttached, required this.limitAttachedStrong, @@ -713,6 +719,9 @@ class VeilidConfigRoutingTable { Map get json { return { + 'node_id': nodeId.map((p) => p).toList(), + 'node_id_secret': nodeIdSecret.map((p) => p).toList(), + 'bootstrap': bootstrap.map((p) => p).toList(), 'limit_over_attached': limitOverAttached, 'limit_fully_attached': limitFullyAttached, 'limit_attached_strong': limitAttachedStrong, @@ -722,7 +731,10 @@ class VeilidConfigRoutingTable { } VeilidConfigRoutingTable.fromJson(dynamic json) - : limitOverAttached = json['limit_over_attached'], + : nodeId = List.from(json['node_id'].map((j) => j)), + nodeIdSecret = List.from(json['node_id_secret'].map((j) => j)), + bootstrap = List.from(json['bootstrap'].map((j) => j)), + limitOverAttached = json['limit_over_attached'], limitFullyAttached = json['limit_fully_attached'], limitAttachedStrong = json['limit_attached_strong'], limitAttachedGood = json['limit_attached_good'], @@ -741,9 +753,6 @@ class VeilidConfigNetwork { int clientWhitelistTimeoutMs; int reverseConnectionReceiptTimeMs; int holePunchReceiptTimeMs; - String? nodeId; - String? nodeIdSecret; - List bootstrap; VeilidConfigRoutingTable routingTable; VeilidConfigRPC rpc; VeilidConfigDHT dht; @@ -764,9 +773,6 @@ class VeilidConfigNetwork { required this.clientWhitelistTimeoutMs, required this.reverseConnectionReceiptTimeMs, required this.holePunchReceiptTimeMs, - required this.nodeId, - required this.nodeIdSecret, - required this.bootstrap, required this.routingTable, required this.rpc, required this.dht, @@ -789,9 +795,6 @@ class VeilidConfigNetwork { 'client_whitelist_timeout_ms': clientWhitelistTimeoutMs, 'reverse_connection_receipt_time_ms': reverseConnectionReceiptTimeMs, 'hole_punch_receipt_time_ms': holePunchReceiptTimeMs, - 'node_id': nodeId, - 'node_id_secret': nodeIdSecret, - 'bootstrap': bootstrap, 'routing_table': routingTable.json, 'rpc': rpc.json, 'dht': dht.json, @@ -817,9 +820,6 @@ class VeilidConfigNetwork { reverseConnectionReceiptTimeMs = json['reverse_connection_receipt_time_ms'], holePunchReceiptTimeMs = json['hole_punch_receipt_time_ms'], - nodeId = json['node_id'], - nodeIdSecret = json['node_id_secret'], - bootstrap = json['bootstrap'], routingTable = VeilidConfigRoutingTable.fromJson(json['routing_table']), rpc = VeilidConfigRPC.fromJson(json['rpc']), dht = VeilidConfigDHT.fromJson(json['dht']), @@ -1159,26 +1159,26 @@ class PeerStats { //////////// class PeerTableData { - String nodeId; + List nodeIds; PeerAddress peerAddress; PeerStats peerStats; PeerTableData({ - required this.nodeId, + required this.nodeIds, required this.peerAddress, required this.peerStats, }); Map get json { return { - 'node_id': nodeId, + 'node_ids': nodeIds.map((p) => p).toList(), 'peer_address': peerAddress.json, 'peer_stats': peerStats.json, }; } PeerTableData.fromJson(dynamic json) - : nodeId = json['node_id'], + : nodeIds = List.from(json['node_ids'].map((j) => j)), peerAddress = PeerAddress.fromJson(json['peer_address']), peerStats = PeerStats.fromJson(json['peer_stats']); } diff --git a/veilid-flutter/lib/veilid_ffi.dart b/veilid-flutter/lib/veilid_ffi.dart index f6bee244..dd0b13a4 100644 --- a/veilid-flutter/lib/veilid_ffi.dart +++ b/veilid-flutter/lib/veilid_ffi.dart @@ -861,8 +861,9 @@ class VeilidFFI implements Veilid { final sendPort = recvPort.sendPort; _newCustomPrivateRoute(sendPort.nativePort, stability.json.toNativeUtf8(), sequencing.json.toNativeUtf8()); - final keyblob = await processFutureJson(RouteBlob.fromJson, recvPort.first); - return keyblob; + final routeBlob = + await processFutureJson(RouteBlob.fromJson, recvPort.first); + return routeBlob; } @override diff --git a/veilid-flutter/rust/src/dart_ffi.rs b/veilid-flutter/rust/src/dart_ffi.rs index bbc4a766..4650844d 100644 --- a/veilid-flutter/rust/src/dart_ffi.rs +++ b/veilid-flutter/rust/src/dart_ffi.rs @@ -499,9 +499,9 @@ pub extern "C" fn new_private_route(port: i64) { let (route_id, blob) = veilid_api.new_private_route().await?; - let keyblob = VeilidFFIRouteBlob { route_id, blob }; + let route_blob = VeilidFFIRouteBlob { route_id, blob }; - APIResult::Ok(keyblob) + APIResult::Ok(route_blob) }); } @@ -519,9 +519,9 @@ pub extern "C" fn new_custom_private_route(port: i64, stability: FfiStr, sequenc .new_custom_private_route(&veilid_core::VALID_CRYPTO_KINDS, stability, sequencing) .await?; - let keyblob = VeilidFFIRouteBlob { route_id, blob }; + let route_blob = VeilidFFIRouteBlob { route_id, blob }; - APIResult::Ok(keyblob) + APIResult::Ok(route_blob) }); } @@ -544,9 +544,9 @@ pub extern "C" fn import_remote_private_route(port: i64, blob: FfiStr) { } #[no_mangle] -pub extern "C" fn release_private_route(port: i64, key: FfiStr) { +pub extern "C" fn release_private_route(port: i64, route_id: FfiStr) { let route_id: veilid_core::RouteId = - veilid_core::deserialize_opt_json(key.into_opt_string()).unwrap(); + veilid_core::deserialize_opt_json(route_id.into_opt_string()).unwrap(); DartIsolateWrapper::new(port).spawn_result(async move { let veilid_api = get_veilid_api().await?; veilid_api.release_private_route(route_id)?; diff --git a/veilid-wasm/src/lib.rs b/veilid-wasm/src/lib.rs index d7baf61f..26da17bf 100644 --- a/veilid-wasm/src/lib.rs +++ b/veilid-wasm/src/lib.rs @@ -79,6 +79,28 @@ pub fn from_json( deserialize_json(&s) } +// Parse target +fn parse_target(s: String) -> APIResult { + // Is this a route id? + if let Ok(rrid) = veilid_core::RouteId::from_str(&s) { + let veilid_api = get_veilid_api()?; + let routing_table = veilid_api.routing_table()?; + let rss = routing_table.route_spec_store(); + + // Is this a valid remote route id? (can't target allocated routes) + if rss.is_route_id_remote(&rrid) { + return Ok(veilid_core::Target::PrivateRoute(rrid)); + } + } + + // Is this a node id? + if let Ok(nid) = veilid_core::PublicKey::from_str(&s) { + return Ok(veilid_core::Target::NodeId(nid)); + } + + Err(veilid_core::VeilidAPIError::invalid_target()) +} + // Utility types for async API results type APIResult = Result; const APIRESULT_UNDEFINED: APIResult<()> = APIResult::Ok(()); @@ -136,8 +158,8 @@ pub struct VeilidWASMConfig { } #[derive(Debug, Deserialize, Serialize)] -pub struct VeilidKeyBlob { - pub key: veilid_core::TypedKey, +pub struct VeilidRouteBlob { + pub route_id: veilid_core::RouteId, #[serde(with = "veilid_core::json_as_base64")] pub blob: Vec, } @@ -355,7 +377,6 @@ pub fn routing_context_app_call(id: u32, target: String, request: String) -> Pro wrap_api_future_plain(async move { let veilid_api = get_veilid_api()?; let routing_table = veilid_api.routing_table()?; - let rss = routing_table.route_spec_store(); let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); @@ -365,15 +386,7 @@ pub fn routing_context_app_call(id: u32, target: String, request: String) -> Pro routing_context.clone() }; - let target: DHTKey = - DHTKey::try_decode(&target).map_err(|e| VeilidAPIError::parse_error(e, &target))?; - - let target = if rss.get_remote_private_route(&target).is_some() { - veilid_core::Target::PrivateRoute(target) - } else { - veilid_core::Target::NodeId(veilid_core::NodeId::new(target)) - }; - + let target = parse_target(target)?; let answer = routing_context.app_call(target, request).await?; let answer = data_encoding::BASE64URL_NOPAD.encode(&answer); APIResult::Ok(answer) @@ -388,7 +401,6 @@ pub fn routing_context_app_message(id: u32, target: String, message: String) -> wrap_api_future_void(async move { let veilid_api = get_veilid_api()?; let routing_table = veilid_api.routing_table()?; - let rss = routing_table.route_spec_store(); let routing_context = { let rc = (*ROUTING_CONTEXTS).borrow(); @@ -398,15 +410,7 @@ pub fn routing_context_app_message(id: u32, target: String, message: String) -> routing_context.clone() }; - let target: DHTKey = - DHTKey::try_decode(&target).map_err(|e| VeilidAPIError::parse_error(e, &target))?; - - let target = if rss.get_remote_private_route(&target).is_some() { - veilid_core::Target::PrivateRoute(target) - } else { - veilid_core::Target::NodeId(veilid_core::NodeId::new(target)) - }; - + let target = parse_target(target)?; routing_context.app_message(target, message).await?; APIRESULT_UNDEFINED }) @@ -417,11 +421,11 @@ pub fn new_private_route() -> Promise { wrap_api_future_json(async move { let veilid_api = get_veilid_api()?; - let (key, blob) = veilid_api.new_private_route().await?; + let (route_id, blob) = veilid_api.new_private_route().await?; - let keyblob = VeilidKeyBlob { key, blob }; + let route_blob = VeilidRouteBlob { route_id, blob }; - APIResult::Ok(keyblob) + APIResult::Ok(route_blob) }) } @@ -433,13 +437,13 @@ pub fn new_custom_private_route(stability: String, sequencing: String) -> Promis wrap_api_future_json(async move { let veilid_api = get_veilid_api()?; - let (key, blob) = veilid_api - .new_custom_private_route(stability, sequencing) + let (route_id, blob) = veilid_api + .new_custom_private_route(&veilid_core::VALID_CRYPTO_KINDS, stability, sequencing) .await?; - let keyblob = VeilidKeyBlob { key, blob }; + let route_blob = VeilidRouteBlob { route_id, blob }; - APIResult::Ok(keyblob) + APIResult::Ok(route_blob) }) } @@ -458,11 +462,11 @@ pub fn import_remote_private_route(blob: String) -> Promise { } #[wasm_bindgen()] -pub fn release_private_route(key: String) -> Promise { - let key: veilid_core::TypedKey = veilid_core::deserialize_json(&key).unwrap(); +pub fn release_private_route(route_id: String) -> Promise { + let route_id: veilid_core::RouteId = veilid_core::deserialize_json(&route_id).unwrap(); wrap_api_future_void(async move { let veilid_api = get_veilid_api()?; - veilid_api.release_private_route(&key)?; + veilid_api.release_private_route(route_id)?; APIRESULT_UNDEFINED }) }