From 14ba85efdaea942d56f027e81d573daff0a783b1 Mon Sep 17 00:00:00 2001 From: John Smith Date: Fri, 16 Jun 2023 11:57:55 -0400 Subject: [PATCH] fixes --- veilid-cli/src/command_processor.rs | 2 +- .../route_spec_store/route_spec_store.rs | 6 +- veilid-core/src/rpc_processor/rpc_app_call.rs | 8 ++- veilid-core/src/veilid_api/api.rs | 8 ++- veilid-core/src/veilid_api/debug.rs | 66 ++++++++++++------- .../src/veilid_api/types/app_message_call.rs | 8 +-- veilid-flutter/lib/veilid_ffi.dart | 6 +- veilid-flutter/lib/veilid_js.dart | 4 +- veilid-flutter/lib/veilid_state.dart | 12 ++-- veilid-flutter/rust/src/dart_ffi.rs | 10 +-- veilid-python/tests/test_basic.py | 10 +++ veilid-python/tests/test_routing_context.py | 56 +++++++++++++--- veilid-python/veilid/api.py | 4 +- veilid-python/veilid/json_api.py | 8 +-- veilid-python/veilid/schema/RecvMessage.json | 4 +- veilid-python/veilid/state.py | 8 +-- veilid-python/veilid/types.py | 3 + veilid-wasm/src/lib.rs | 10 +-- 18 files changed, 158 insertions(+), 75 deletions(-) diff --git a/veilid-cli/src/command_processor.rs b/veilid-cli/src/command_processor.rs index 636561d1..dcf0a8cb 100644 --- a/veilid-cli/src/command_processor.rs +++ b/veilid-cli/src/command_processor.rs @@ -488,7 +488,7 @@ reply - reply to an AppCall not handled directly by the server format!("#{}", hex::encode(&message)) }; - let id = json_str_u64(&call["id"]); + let id = json_str_u64(&call["call_id"]); self.inner().ui_sender.add_node_event(format!( "AppCall ({:?}) id = {:016x} : {}", diff --git a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs index 043cd1e3..db490b08 100644 --- a/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs +++ b/veilid-core/src/routing_table/route_spec_store/route_spec_store.rs @@ -1331,9 +1331,9 @@ impl RouteSpecStore { } // ensure this isn't also an allocated route - if inner.content.get_id_by_key(&private_route.public_key.value).is_some() { - bail!("should not import allocated route"); - } + // if inner.content.get_id_by_key(&private_route.public_key.value).is_some() { + // bail!("should not import allocated route"); + // } } inner.cache.cache_remote_private_route(cur_ts, id, private_routes); diff --git a/veilid-core/src/rpc_processor/rpc_app_call.rs b/veilid-core/src/rpc_processor/rpc_app_call.rs index 80919d4c..55399a0a 100644 --- a/veilid-core/src/rpc_processor/rpc_app_call.rs +++ b/veilid-core/src/rpc_processor/rpc_app_call.rs @@ -99,10 +99,14 @@ impl RPCProcessor { } /// Exposed to API for apps to return app call answers - pub async fn app_call_reply(&self, id: OperationId, message: Vec) -> Result<(), RPCError> { + pub async fn app_call_reply( + &self, + call_id: OperationId, + message: Vec, + ) -> Result<(), RPCError> { self.unlocked_inner .waiting_app_call_table - .complete_op_waiter(id, message) + .complete_op_waiter(call_id, message) .await } } diff --git a/veilid-core/src/veilid_api/api.rs b/veilid-core/src/veilid_api/api.rs index 33d4ed91..e694f111 100644 --- a/veilid-core/src/veilid_api/api.rs +++ b/veilid-core/src/veilid_api/api.rs @@ -258,10 +258,14 @@ impl VeilidAPI { // App Calls #[instrument(level = "debug", skip(self))] - pub async fn app_call_reply(&self, id: OperationId, message: Vec) -> VeilidAPIResult<()> { + pub async fn app_call_reply( + &self, + call_id: OperationId, + message: Vec, + ) -> VeilidAPIResult<()> { let rpc_processor = self.rpc_processor()?; rpc_processor - .app_call_reply(id, message) + .app_call_reply(call_id, message) .await .map_err(|e| e.into()) } diff --git a/veilid-core/src/veilid_api/debug.rs b/veilid-core/src/veilid_api/debug.rs index 7c95e0c9..bdc269ee 100644 --- a/veilid-core/src/veilid_api/debug.rs +++ b/veilid-core/src/veilid_api/debug.rs @@ -31,16 +31,22 @@ fn get_string(text: &str) -> Option { Some(text.to_owned()) } -fn get_route_id(rss: RouteSpecStore, allow_remote: bool) -> impl Fn(&str) -> Option { +fn get_route_id( + rss: RouteSpecStore, + allow_allocated: bool, + allow_remote: bool, +) -> impl Fn(&str) -> Option { return move |text: &str| { if text.is_empty() { return None; } match RouteId::from_str(text).ok() { Some(key) => { - let routes = rss.list_allocated_routes(|k, _| Some(*k)); - if routes.contains(&key) { - return Some(key); + if allow_allocated { + let routes = rss.list_allocated_routes(|k, _| Some(*k)); + if routes.contains(&key) { + return Some(key); + } } if allow_remote { let rroutes = rss.list_remote_routes(|k, _| Some(*k)); @@ -50,11 +56,13 @@ fn get_route_id(rss: RouteSpecStore, allow_remote: bool) -> impl Fn(&str) -> Opt } } None => { - let routes = rss.list_allocated_routes(|k, _| Some(*k)); - for r in routes { - let rkey = r.encode(); - if rkey.starts_with(text) { - return Some(r); + if allow_allocated { + let routes = rss.list_allocated_routes(|k, _| Some(*k)); + for r in routes { + let rkey = r.encode(); + if rkey.starts_with(text) { + return Some(r); + } } } if allow_remote { @@ -90,7 +98,7 @@ fn get_safety_selection(text: &str, routing_table: RoutingTable) -> Option impl FnOnce(&str) -> Option 2 { @@ -747,7 +765,7 @@ impl VeilidAPI { 1, "debug_route", "route_id", - get_route_id(rss.clone(), false), + get_route_id(rss.clone(), true, false), )?; // Unpublish route @@ -769,7 +787,7 @@ impl VeilidAPI { 1, "debug_route", "route_id", - get_route_id(rss.clone(), true), + get_route_id(rss.clone(), true, true), )?; match rss.debug_route(&route_id) { @@ -831,7 +849,7 @@ impl VeilidAPI { 1, "debug_route", "route_id", - get_route_id(rss.clone(), true), + get_route_id(rss.clone(), true, true), )?; let success = rss diff --git a/veilid-core/src/veilid_api/types/app_message_call.rs b/veilid-core/src/veilid_api/types/app_message_call.rs index fb33ae50..41f3bdff 100644 --- a/veilid-core/src/veilid_api/types/app_message_call.rs +++ b/veilid-core/src/veilid_api/types/app_message_call.rs @@ -67,15 +67,15 @@ pub struct VeilidAppCall { /// The id to reply to #[serde(with = "json_as_string")] #[schemars(with = "String")] - id: OperationId, + call_id: OperationId, } impl VeilidAppCall { - pub fn new(sender: Option, message: Vec, id: OperationId) -> Self { + pub fn new(sender: Option, message: Vec, call_id: OperationId) -> Self { Self { sender, message, - id, + call_id, } } @@ -86,6 +86,6 @@ impl VeilidAppCall { &self.message } pub fn id(&self) -> OperationId { - self.id + self.call_id } } diff --git a/veilid-flutter/lib/veilid_ffi.dart b/veilid-flutter/lib/veilid_ffi.dart index a0a9bdcd..f39773e8 100644 --- a/veilid-flutter/lib/veilid_ffi.dart +++ b/veilid-flutter/lib/veilid_ffi.dart @@ -1560,12 +1560,12 @@ class VeilidFFI implements Veilid { } @override - Future appCallReply(String id, Uint8List message) { - final nativeId = id.toNativeUtf8(); + Future appCallReply(String call_id, Uint8List message) { + final nativeCallId = call_id.toNativeUtf8(); final nativeEncodedMessage = base64UrlNoPadEncode(message).toNativeUtf8(); final recvPort = ReceivePort("app_call_reply"); final sendPort = recvPort.sendPort; - _appCallReply(sendPort.nativePort, nativeId, nativeEncodedMessage); + _appCallReply(sendPort.nativePort, nativeCallId, nativeEncodedMessage); return processFutureVoid(recvPort.first); } diff --git a/veilid-flutter/lib/veilid_js.dart b/veilid-flutter/lib/veilid_js.dart index 139c10a5..f1c5f84b 100644 --- a/veilid-flutter/lib/veilid_js.dart +++ b/veilid-flutter/lib/veilid_js.dart @@ -580,10 +580,10 @@ class VeilidJS implements Veilid { } @override - Future appCallReply(String id, Uint8List message) { + Future appCallReply(String callId, Uint8List message) { var encodedMessage = base64UrlNoPadEncode(message); return _wrapApiPromise( - js_util.callMethod(wasm, "app_call_reply", [id, encodedMessage])); + js_util.callMethod(wasm, "app_call_reply", [callId, encodedMessage])); } @override diff --git a/veilid-flutter/lib/veilid_state.dart b/veilid-flutter/lib/veilid_state.dart index 40c263c5..8d90dbff 100644 --- a/veilid-flutter/lib/veilid_state.dart +++ b/veilid-flutter/lib/veilid_state.dart @@ -262,7 +262,9 @@ abstract class VeilidUpdate { case "AppCall": { return VeilidAppCall( - sender: json["sender"], message: json["message"], id: json["id"]); + sender: json["sender"], + message: json["message"], + callId: json["call_id"]); } case "Attachment": { @@ -348,22 +350,22 @@ class VeilidAppMessage implements VeilidUpdate { class VeilidAppCall implements VeilidUpdate { final String? sender; final Uint8List message; - final String id; + final String callId; // VeilidAppCall({ required this.sender, required this.message, - required this.id, + required this.callId, }); @override Map toJson() { return { - 'kind': "AppMessage", + 'kind': "AppCall", 'sender': sender, 'message': base64UrlNoPadEncode(message), - 'id': id, + 'call_id': callId, }; } } diff --git a/veilid-flutter/rust/src/dart_ffi.rs b/veilid-flutter/rust/src/dart_ffi.rs index 238f1805..2a9b65a7 100644 --- a/veilid-flutter/rust/src/dart_ffi.rs +++ b/veilid-flutter/rust/src/dart_ffi.rs @@ -707,21 +707,21 @@ pub extern "C" fn release_private_route(port: i64, route_id: FfiStr) { } #[no_mangle] -pub extern "C" fn app_call_reply(port: i64, id: FfiStr, message: FfiStr) { - let id = id.into_opt_string().unwrap_or_default(); +pub extern "C" fn app_call_reply(port: i64, call_id: FfiStr, message: FfiStr) { + let call_id = call_id.into_opt_string().unwrap_or_default(); let message = message.into_opt_string().unwrap_or_default(); DartIsolateWrapper::new(port).spawn_result(async move { - let id = match id.parse() { + let call_id = match call_id.parse() { Ok(v) => v, Err(e) => { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(e, "id", id)) + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(e, "call_id", call_id)) } }; let message = data_encoding::BASE64URL_NOPAD .decode(message.as_bytes()) .map_err(|e| veilid_core::VeilidAPIError::invalid_argument(e, "message", message))?; let veilid_api = get_veilid_api().await?; - veilid_api.app_call_reply(id, message).await?; + veilid_api.app_call_reply(call_id, message).await?; APIRESULT_VOID }); } diff --git a/veilid-python/tests/test_basic.py b/veilid-python/tests/test_basic.py index 6b083390..b2c44b23 100644 --- a/veilid-python/tests/test_basic.py +++ b/veilid-python/tests/test_basic.py @@ -12,6 +12,15 @@ async def test_connect(): pass await simple_connect_and_run(func) + +@pytest.mark.asyncio +async def test_get_node_id(): + async def func(api: veilid.VeilidAPI): + # get our own node id + state = await api.get_state() + node_id = state.config.config.network.routing_table.node_id.pop() + await simple_connect_and_run(func) + @pytest.mark.asyncio async def test_fail_connect(): with pytest.raises(Exception): @@ -27,3 +36,4 @@ async def test_version(): vstr = await api.veilid_version_string() print("veilid_version_string: {}".format(vstr)) await simple_connect_and_run(func) + diff --git a/veilid-python/tests/test_routing_context.py b/veilid-python/tests/test_routing_context.py index 4b782ecb..2ab8a579 100644 --- a/veilid-python/tests/test_routing_context.py +++ b/veilid-python/tests/test_routing_context.py @@ -32,16 +32,56 @@ async def test_routing_context_app_message_loopback(): # make a routing context that uses a safety route rc = await (await api.new_routing_context()).with_privacy() - # get our own node id - state = await api.get_state() - node_id = state.config.config.network.routing_table.node_id.pop() + # make a new local private route + prl, blob = await api.new_private_route() - # send an app message to our node id + # import it as a remote route as well so we can send to it + prr = await api.import_remote_private_route(blob) + + # send an app message to our own private route message = b"abcd1234" - await rc.app_message(node_id, message) + await rc.app_message(prr, message) # we should get the same message back - #update: veilid.VeilidUpdate = await asyncio.wait_for(app_message_queue.get(), timeout=10) - #appmsg: veilid.VeilidAppMessage = update.detail - #assert appmsg.message == message + update: veilid.VeilidUpdate = await asyncio.wait_for(app_message_queue.get(), timeout=10) + appmsg: veilid.VeilidAppMessage = update.detail + assert appmsg.message == message + +@pytest.mark.asyncio +async def test_routing_context_app_call_loopback(): + + app_call_queue = asyncio.Queue() + + async def app_call_queue_update_callback(update: veilid.VeilidUpdate): + if update.kind == veilid.VeilidUpdateKind.APP_CALL: + await app_call_queue.put(update) + + api = await veilid.json_api_connect(VEILID_SERVER, VEILID_SERVER_PORT, app_call_queue_update_callback) + async with api: + + # make a routing context that uses a safety route + rc = await (await api.new_routing_context()).with_privacy() + + # make a new local private route + prl, blob = await api.new_private_route() + + # import it as a remote route as well so we can send to it + prr = await api.import_remote_private_route(blob) + + # send an app message to our own private route + request = b"abcd1234" + app_call_task = asyncio.create_task(rc.app_call(prr, request), name = "app call task") + + # we should get the same request back + update: veilid.VeilidUpdate = await asyncio.wait_for(app_call_queue.get(), timeout=10) + appcall: veilid.VeilidAppCall = update.detail + assert appcall.message == request + + # now we reply to the request + reply = b"qwer5678" + await api.app_call_reply(appcall.call_id, reply) + + # now we should get the reply from the call + result = await app_call_task + assert result == reply diff --git a/veilid-python/veilid/api.py b/veilid-python/veilid/api.py index 07134d43..f201a324 100644 --- a/veilid-python/veilid/api.py +++ b/veilid-python/veilid/api.py @@ -159,10 +159,10 @@ class VeilidAPI(ABC): async def detach(self): pass @abstractmethod - async def new_private_route(self) -> NewPrivateRouteResult: + async def new_private_route(self) -> Tuple[RouteId, bytes]: pass @abstractmethod - async def new_custom_private_route(self, kinds: list[CryptoKind], stability: Stability, sequencing: Sequencing) -> NewPrivateRouteResult: + async def new_custom_private_route(self, kinds: list[CryptoKind], stability: Stability, sequencing: Sequencing) -> Tuple[RouteId, bytes]: pass @abstractmethod async def import_remote_private_route(self, blob: bytes) -> RouteId: diff --git a/veilid-python/veilid/json_api.py b/veilid-python/veilid/json_api.py index 5b0d0fc3..eb388fc3 100644 --- a/veilid-python/veilid/json_api.py +++ b/veilid-python/veilid/json_api.py @@ -223,15 +223,15 @@ class _JsonVeilidAPI(VeilidAPI): raise_api_result(await self.send_ndjson_request(Operation.ATTACH)) async def detach(self): raise_api_result(await self.send_ndjson_request(Operation.DETACH)) - async def new_private_route(self) -> NewPrivateRouteResult: - return NewPrivateRouteResult.from_json(raise_api_result(await self.send_ndjson_request(Operation.NEW_PRIVATE_ROUTE))) - async def new_custom_private_route(self, kinds: list[CryptoKind], stability: Stability, sequencing: Sequencing) -> NewPrivateRouteResult: + async def new_private_route(self) -> Tuple[RouteId, bytes]: + return NewPrivateRouteResult.from_json(raise_api_result(await self.send_ndjson_request(Operation.NEW_PRIVATE_ROUTE))).to_tuple() + async def new_custom_private_route(self, kinds: list[CryptoKind], stability: Stability, sequencing: Sequencing) -> Tuple[RouteId, bytes]: return NewPrivateRouteResult.from_json(raise_api_result( await self.send_ndjson_request(Operation.NEW_CUSTOM_PRIVATE_ROUTE, kinds = kinds, stability = stability, sequencing = sequencing) - )) + )).to_tuple() async def import_remote_private_route(self, blob: bytes) -> RouteId: return RouteId(raise_api_result( await self.send_ndjson_request(Operation.IMPORT_REMOTE_PRIVATE_ROUTE, diff --git a/veilid-python/veilid/schema/RecvMessage.json b/veilid-python/veilid/schema/RecvMessage.json index b84dc8e4..15b37df1 100644 --- a/veilid-python/veilid/schema/RecvMessage.json +++ b/veilid-python/veilid/schema/RecvMessage.json @@ -2347,12 +2347,12 @@ "description": "Direct question blob passed to hosting application for processing to send an eventual AppReply", "type": "object", "required": [ - "id", + "call_id", "kind", "message" ], "properties": { - "id": { + "call_id": { "description": "The id to reply to", "type": "string" }, diff --git a/veilid-python/veilid/state.py b/veilid-python/veilid/state.py index 13404754..acb5c37c 100644 --- a/veilid-python/veilid/state.py +++ b/veilid-python/veilid/state.py @@ -250,12 +250,12 @@ class VeilidAppMessage: class VeilidAppCall: sender: Optional[TypedKey] message: bytes - operation_id: str + call_id: str - def __init__(self, sender: Optional[TypedKey], message: bytes, operation_id: str): + def __init__(self, sender: Optional[TypedKey], message: bytes, call_id: str): self.sender = sender self.message = message - self.operation_id = operation_id + self.call_id = call_id @staticmethod def from_json(j: dict) -> Self: @@ -263,7 +263,7 @@ class VeilidAppCall: return VeilidAppCall( None if j['sender'] is None else TypedKey(j['sender']), urlsafe_b64decode_no_pad(j['message']), - j['operation_id']) + j['call_id']) class VeilidRouteChange: dead_routes: list[RouteId] diff --git a/veilid-python/veilid/types.py b/veilid-python/veilid/types.py index b3388531..684f3b17 100644 --- a/veilid-python/veilid/types.py +++ b/veilid-python/veilid/types.py @@ -204,6 +204,9 @@ class NewPrivateRouteResult: self.route_id = route_id self.blob = blob + def to_tuple(self) -> Tuple[RouteId, bytes]: + return (self.route_id, self.blob) + @staticmethod def from_json(j: dict) -> Self: return NewPrivateRouteResult( diff --git a/veilid-wasm/src/lib.rs b/veilid-wasm/src/lib.rs index a43f7ce5..72b2c102 100644 --- a/veilid-wasm/src/lib.rs +++ b/veilid-wasm/src/lib.rs @@ -641,19 +641,21 @@ pub fn release_private_route(route_id: String) -> Promise { } #[wasm_bindgen()] -pub fn app_call_reply(id: String, message: String) -> Promise { +pub fn app_call_reply(call_id: String, message: String) -> Promise { let message: Vec = data_encoding::BASE64URL_NOPAD .decode(message.as_bytes()) .unwrap(); wrap_api_future_void(async move { - let id = match id.parse() { + let call_id = match call_id.parse() { Ok(v) => v, Err(e) => { - return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument(e, "id", id)) + return APIResult::Err(veilid_core::VeilidAPIError::invalid_argument( + e, "call_id", call_id, + )) } }; let veilid_api = get_veilid_api()?; - veilid_api.app_call_reply(id, message).await?; + veilid_api.app_call_reply(call_id, message).await?; APIRESULT_UNDEFINED }) }