From 37614e90924ebe0d38f26c3f7bfcd0b2d762e03c Mon Sep 17 00:00:00 2001 From: f321x Date: Tue, 19 Aug 2025 14:58:23 +0200 Subject: [PATCH] swaps: handle timeouts in send_direct_message Adds logic to retry sending a direct message in `NostrTransport.send_direct_message()` on `TimeoutError`. Handles `TimeoutError` exception more gracefully by catching it and returning `None`. --- electrum/submarine_swaps.py | 50 ++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/electrum/submarine_swaps.py b/electrum/submarine_swaps.py index 9a968daf8..adbc148ff 100644 --- a/electrum/submarine_swaps.py +++ b/electrum/submarine_swaps.py @@ -1567,25 +1567,37 @@ class NostrTransport(SwapServerTransport): tags = [['d', f'electrum-swapserver-{self.NOSTR_EVENT_VERSION}'], ['r', 'net:' + constants.net.NET_NAME], ['expiration', str(int(time.time() + self.OFFER_UPDATE_INTERVAL_SEC + 10))]] - event_id = await aionostr._add_event( - self.relay_manager, - kind=self.USER_STATUS_NIP38, - tags=tags, - content=json.dumps(offer), - private_key=self.nostr_private_key) - self.logger.info(f"published offer {event_id}") + try: + event_id = await aionostr._add_event( + self.relay_manager, + kind=self.USER_STATUS_NIP38, + tags=tags, + content=json.dumps(offer), + private_key=self.nostr_private_key) + self.logger.info(f"published offer {event_id}") + except asyncio.TimeoutError as e: + self.logger.warning(f"failed to publish swap offer: {str(e)}") - async def send_direct_message(self, pubkey: str, content: str) -> str: + @ignore_exceptions + @log_exceptions + async def send_direct_message(self, pubkey: str, content: str, retries: int = 0) -> Optional[str]: + assert retries < 25, "Use a sane retry amount" our_private_key = aionostr.key.PrivateKey(self.private_key) recv_pubkey_hex = aionostr.util.from_nip19(pubkey)['object'].hex() if pubkey.startswith('npub') else pubkey encrypted_msg = our_private_key.encrypt_message(content, recv_pubkey_hex) - event_id = await aionostr._add_event( - self.relay_manager, - kind=self.EPHEMERAL_REQUEST, - content=encrypted_msg, - private_key=self.nostr_private_key, - tags=[['p', recv_pubkey_hex]], - ) + try: + event_id = await aionostr._add_event( + self.relay_manager, + kind=self.EPHEMERAL_REQUEST, + content=encrypted_msg, + private_key=self.nostr_private_key, + tags=[['p', recv_pubkey_hex]], + ) + except asyncio.TimeoutError: + self.logger.warning(f"sending message to {pubkey} failed: timeout. {retries=}") + if retries > 0: + return await self.send_direct_message(pubkey, content, retries - 1) + return None return event_id @log_exceptions @@ -1593,7 +1605,9 @@ class NostrTransport(SwapServerTransport): self.logger.debug(f"swapserver req: method: {method} relays: {self.relays}") request_data['method'] = method server_npub = self.config.SWAPSERVER_NPUB - event_id = await self.send_direct_message(server_npub, json.dumps(request_data)) + event_id = await self.send_direct_message(server_npub, json.dumps(request_data), retries=1) + if not event_id: + raise SwapServerError() response = await self.dm_replies[event_id] if 'error' in response: self.logger.warning(f"error from swap server [DO NOT TRUST THIS MESSAGE]: {response['error']}") @@ -1725,7 +1739,7 @@ class NostrTransport(SwapServerTransport): "error": str(e)[:100], "reply_to": event.id, }) - await self.send_direct_message(event.pubkey, error_response) + await self.taskgroup.spawn(self.send_direct_message(event.pubkey, error_response)) else: self.logger.info(f'unknown message {content}') @@ -1747,7 +1761,7 @@ class NostrTransport(SwapServerTransport): raise Exception(method) r['reply_to'] = event_id self.logger.debug(f'sending response id={event_id}') - await self.send_direct_message(event_pubkey, json.dumps(r)) + await self.taskgroup.spawn(self.send_direct_message(event_pubkey, json.dumps(r), retries=2)) def _store_last_swapserver_relays(self, relays: Sequence[str]): self._last_swapserver_relays = relays