diff --git a/lnbits/core/crud/wallets.py b/lnbits/core/crud/wallets.py index aae4d243b..a54e80bf6 100644 --- a/lnbits/core/crud/wallets.py +++ b/lnbits/core/crud/wallets.py @@ -1,16 +1,77 @@ from datetime import datetime, timezone +from random import SystemRandom from time import time from typing import Any from uuid import uuid4 +from sqlalchemy.exc import OperationalError + from lnbits.core.db import db from lnbits.core.models.wallets import BaseWallet, WalletsFilters, WalletType -from lnbits.db import Connection, Filters, Page +from lnbits.db import Connection, Database, Filters, Page from lnbits.settings import settings from lnbits.utils.cache import cache from ..models import Wallet +_RANDOM = SystemRandom() +_LEGACY_LNURLP_DB = Database("ext_lnurlp") + +_PARTICIPLES = [ + "asking", + "blazing", + "bouncing", + "charging", + "climbing", + "dancing", + "drifting", + "flying", + "glowing", + "hopping", + "jumping", + "laughing", + "leaping", + "racing", + "rising", + "running", + "shining", + "singing", + "skipping", + "sparking", + "spinning", + "sprinting", + "twirling", + "wandering", + "zipping", +] + +_NOUNS = [ + "anchor", + "beacon", + "bolt", + "comet", + "ember", + "falcon", + "flash", + "garden", + "harbor", + "lantern", + "market", + "meadow", + "meteor", + "orbit", + "phoenix", + "rabbit", + "rocket", + "signal", + "spark", + "summit", + "thunder", + "wallet", + "wave", + "zephyr", +] + async def create_wallet( *, @@ -32,8 +93,6 @@ async def create_wallet( currency=settings.lnbits_default_accounting_currency or "USD", ) if settings.lnbits_enable_wallet_lightning_addresses and wallet.is_lightning_wallet: - from ..services.lightning_address import generate_lightning_address_local_part - wallet.lightning_address = await generate_lightning_address_local_part(conn) await (conn or db).insert("wallets", wallet) @@ -243,6 +302,43 @@ async def wallet_lightning_address_exists( return bool(row) +def _generate_local_part() -> str: + participle = _RANDOM.choice(_PARTICIPLES) + noun = _RANDOM.choice(_NOUNS) + suffix = _RANDOM.randint(0, 999) + return f"{participle}.{noun}.{suffix:03d}" + + +async def legacy_lnurlp_address_exists(local_part: str) -> bool: + try: + row: Any = await _LEGACY_LNURLP_DB.fetchone( + """ + SELECT 1 FROM lnurlp.pay_links + WHERE username = :username + LIMIT 1 + """, + {"username": local_part}, + ) + return row is not None + except OperationalError: + return False + except Exception: + return False + + +async def generate_lightning_address_local_part( + conn: Connection | None = None, +) -> str: + for _ in range(100): + local_part = _generate_local_part() + if await wallet_lightning_address_exists(local_part, conn): + continue + if await legacy_lnurlp_address_exists(local_part): + continue + return local_part + raise ValueError("Could not generate a unique wallet lightning address.") + + async def wallet_lightning_address_exists_for_other_wallet( local_part: str, wallet_id: str, conn: Connection | None = None ) -> bool: diff --git a/lnbits/core/services/lightning_address.py b/lnbits/core/services/lightning_address.py index b8888f379..2c2f9faf0 100644 --- a/lnbits/core/services/lightning_address.py +++ b/lnbits/core/services/lightning_address.py @@ -1,7 +1,5 @@ import json import re -from random import SystemRandom -from typing import Any from fastapi import Query, Request from lnurl import ( @@ -14,118 +12,22 @@ from lnurl import ( MilliSatoshi, ) from pydantic import parse_obj_as -from sqlalchemy.exc import OperationalError from lnbits.core.crud.wallets import ( + generate_lightning_address_local_part, get_wallet_by_lightning_address, - wallet_lightning_address_exists, + legacy_lnurlp_address_exists, wallet_lightning_address_exists_for_other_wallet, ) from lnbits.core.db import db from lnbits.core.models.wallets import Wallet -from lnbits.db import Connection, Database +from lnbits.db import Connection from lnbits.exceptions import PaymentError from lnbits.settings import settings MAX_SENDABLE_MSAT = 2_100_000_000_000_000_000 COMMENT_ALLOWED = 799 LIGHTNING_ADDRESS_REGEX = re.compile(r"^[a-z0-9_.-]{1,210}$") -_RANDOM = SystemRandom() -_LEGACY_LNURLP_DB = Database("ext_lnurlp") - -_PARTICIPLES = [ - "asking", - "blazing", - "bouncing", - "charging", - "climbing", - "dancing", - "drifting", - "flying", - "glowing", - "hopping", - "jumping", - "laughing", - "leaping", - "racing", - "rising", - "running", - "shining", - "singing", - "skipping", - "sparking", - "spinning", - "sprinting", - "twirling", - "wandering", - "zipping", -] - -_NOUNS = [ - "anchor", - "beacon", - "bolt", - "comet", - "ember", - "falcon", - "flash", - "garden", - "harbor", - "lantern", - "market", - "meadow", - "meteor", - "orbit", - "phoenix", - "rabbit", - "rocket", - "signal", - "spark", - "summit", - "thunder", - "wallet", - "wave", - "zephyr", -] - - -def _generate_local_part() -> str: - participle = _RANDOM.choice(_PARTICIPLES) - noun = _RANDOM.choice(_NOUNS) - suffix = _RANDOM.randint(0, 999) - return f"{participle}.{noun}.{suffix:03d}" - - -async def legacy_lnurlp_address_exists(local_part: str) -> bool: - try: - row: Any = await _LEGACY_LNURLP_DB.fetchone( - """ - SELECT 1 FROM lnurlp.pay_links - WHERE username = :username - LIMIT 1 - """, - {"username": local_part}, - ) - return row is not None - except OperationalError: - return False - except Exception: - return False - - -async def generate_lightning_address_local_part( - conn: Connection | None = None, -) -> str: - for _ in range(100): - local_part = _generate_local_part() - if await wallet_lightning_address_exists(local_part, conn): - continue - if await legacy_lnurlp_address_exists(local_part): - continue - return local_part - raise ValueError("Could not generate a unique wallet lightning address.") - - def normalize_lightning_address_local_part(local_part: str) -> str: return local_part.strip().lower() diff --git a/lnbits/middleware.py b/lnbits/middleware.py index 4d534d7b8..be03bd7b0 100644 --- a/lnbits/middleware.py +++ b/lnbits/middleware.py @@ -123,8 +123,10 @@ class ExtensionsRedirectMiddleware: username = path_parts[2].lower() base_username = username.partition("+")[0] - from lnbits.core.crud.wallets import get_wallet_by_lightning_address - from lnbits.core.services.lightning_address import legacy_lnurlp_address_exists + from lnbits.core.crud.wallets import ( + get_wallet_by_lightning_address, + legacy_lnurlp_address_exists, + ) if await legacy_lnurlp_address_exists(username): return False