import asyncio from typing import List from loguru import logger from ..core.base import MintQuoteState from ..core.settings import settings from ..lightning.base import LightningBackend from .protocols import SupportsBackends, SupportsDb, SupportsEvents class LedgerTasks(SupportsDb, SupportsBackends, SupportsEvents): async def dispatch_listeners(self) -> List[asyncio.Task]: tasks = [] for method, unitbackends in self.backends.items(): for unit, backend in unitbackends.items(): logger.debug( f"Dispatching backend invoice listener for {method} {unit} {backend.__class__.__name__}" ) tasks.append(asyncio.create_task(self.invoice_listener(backend))) return tasks async def invoice_listener(self, backend: LightningBackend) -> None: if backend.supports_incoming_payment_stream: retry_delay = settings.mint_retry_exponential_backoff_base_delay max_retry_delay = settings.mint_retry_exponential_backoff_max_delay while True: try: # Reset retry delay on successful connection to backend stream retry_delay = settings.mint_retry_exponential_backoff_base_delay async for checking_id in backend.paid_invoices_stream(): await self.invoice_callback_dispatcher(checking_id) except Exception as e: logger.error(f"Error in invoice listener: {e}") logger.info(f"Restarting invoice listener in {retry_delay} seconds...") await asyncio.sleep(retry_delay) # Exponential backoff retry_delay = min(retry_delay * 2, max_retry_delay) async def invoice_callback_dispatcher(self, checking_id: str) -> None: logger.debug(f"Invoice callback dispatcher: {checking_id}") async with self.db.get_connection( lock_table="mint_quotes", lock_select_statement=f"checking_id='{checking_id}'", lock_timeout=5, ) as conn: quote = await self.crud.get_mint_quote( checking_id=checking_id, db=self.db, conn=conn ) if not quote: logger.error(f"Quote not found for {checking_id}") return logger.trace( f"Invoice callback dispatcher: quote {quote} trying to set as {MintQuoteState.paid}" ) # set the quote as paid if quote.unpaid: quote.state = MintQuoteState.paid await self.crud.update_mint_quote(quote=quote, db=self.db, conn=conn) logger.trace( f"Quote {quote.quote} with {MintQuoteState.unpaid} set as {quote.state.value}" ) await self.events.submit(quote)