mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-03-15 12:20:21 +01:00
chore: adhere to ruff's "N" rules (#2377)
* chore: adhere to ruff's "N" rules WARN: reinstall failing extensions! bunch of more consistent variable naming. inspired by this issue. https://github.com/lnbits/lnbits/issues/2308 * fixup! chore: adhere to ruff's "N" rules * rename to funding_source * skip jmeter --------- Co-authored-by: Pavol Rusnak <pavol@rusnak.io>
This commit is contained in:
parent
055426ab53
commit
6d5ad9e229
28 changed files with 191 additions and 173 deletions
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
|
@ -53,13 +53,13 @@ jobs:
|
|||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
jmeter:
|
||||
needs: [ lint ]
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.9"]
|
||||
poetry-version: ["1.5.1"]
|
||||
uses: ./.github/workflows/jmeter.yml
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
poetry-version: ${{ matrix.poetry-version }}
|
||||
# jmeter:
|
||||
# needs: [ lint ]
|
||||
# strategy:
|
||||
# matrix:
|
||||
# python-version: ["3.9"]
|
||||
# poetry-version: ["1.5.1"]
|
||||
# uses: ./.github/workflows/jmeter.yml
|
||||
# with:
|
||||
# python-version: ${{ matrix.python-version }}
|
||||
# poetry-version: ${{ matrix.poetry-version }}
|
||||
|
|
|
@ -25,7 +25,7 @@ from starlette.responses import JSONResponse
|
|||
|
||||
from lnbits.core.crud import get_dbversions, get_installed_extensions
|
||||
from lnbits.core.helpers import migrate_extension_database
|
||||
from lnbits.core.services import websocketUpdater
|
||||
from lnbits.core.services import websocket_updater
|
||||
from lnbits.core.tasks import ( # watchdog_task
|
||||
killswitch_task,
|
||||
wait_for_paid_invoices,
|
||||
|
@ -37,7 +37,7 @@ from lnbits.tasks import (
|
|||
register_invoice_listener,
|
||||
)
|
||||
from lnbits.utils.cache import cache
|
||||
from lnbits.wallets import get_wallet_class, set_wallet_class
|
||||
from lnbits.wallets import get_funding_source, set_funding_source
|
||||
|
||||
from .commands import migrate_databases
|
||||
from .core import init_core_routers
|
||||
|
@ -81,7 +81,7 @@ async def startup(app: FastAPI):
|
|||
|
||||
# initialize WALLET
|
||||
try:
|
||||
set_wallet_class()
|
||||
set_funding_source()
|
||||
except Exception as e:
|
||||
logger.error(f"Error initializing {settings.lnbits_backend_wallet_class}: {e}")
|
||||
set_void_wallet_class()
|
||||
|
@ -110,8 +110,8 @@ async def shutdown():
|
|||
|
||||
# wait a bit to allow them to finish, so that cleanup can run without problems
|
||||
await asyncio.sleep(0.1)
|
||||
WALLET = get_wallet_class()
|
||||
await WALLET.cleanup()
|
||||
funding_source = get_funding_source()
|
||||
await funding_source.cleanup()
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
|
@ -178,33 +178,35 @@ def create_app() -> FastAPI:
|
|||
|
||||
async def check_funding_source() -> None:
|
||||
|
||||
WALLET = get_wallet_class()
|
||||
funding_source = get_funding_source()
|
||||
|
||||
max_retries = settings.funding_source_max_retries
|
||||
retry_counter = 0
|
||||
|
||||
while True:
|
||||
try:
|
||||
logger.info(f"Connecting to backend {WALLET.__class__.__name__}...")
|
||||
error_message, balance = await WALLET.status()
|
||||
logger.info(f"Connecting to backend {funding_source.__class__.__name__}...")
|
||||
error_message, balance = await funding_source.status()
|
||||
if not error_message:
|
||||
retry_counter = 0
|
||||
logger.success(
|
||||
f"✔️ Backend {WALLET.__class__.__name__} connected "
|
||||
f"✔️ Backend {funding_source.__class__.__name__} connected "
|
||||
f"and with a balance of {balance} msat."
|
||||
)
|
||||
break
|
||||
logger.error(
|
||||
f"The backend for {WALLET.__class__.__name__} isn't "
|
||||
f"The backend for {funding_source.__class__.__name__} isn't "
|
||||
f"working properly: '{error_message}'",
|
||||
RuntimeWarning,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error connecting to {WALLET.__class__.__name__}: {e}")
|
||||
logger.error(
|
||||
f"Error connecting to {funding_source.__class__.__name__}: {e}"
|
||||
)
|
||||
|
||||
if retry_counter >= max_retries:
|
||||
set_void_wallet_class()
|
||||
WALLET = get_wallet_class()
|
||||
funding_source = get_funding_source()
|
||||
break
|
||||
|
||||
retry_counter += 1
|
||||
|
@ -221,7 +223,7 @@ def set_void_wallet_class():
|
|||
"Fallback to VoidWallet, because the backend for "
|
||||
f"{settings.lnbits_backend_wallet_class} isn't working properly"
|
||||
)
|
||||
set_wallet_class("VoidWallet")
|
||||
set_funding_source("VoidWallet")
|
||||
|
||||
|
||||
async def check_installed_extensions(app: FastAPI):
|
||||
|
@ -414,7 +416,7 @@ def initialize_server_logger():
|
|||
async def update_websocket_serverlog():
|
||||
while True:
|
||||
msg = await serverlog_queue.get()
|
||||
await websocketUpdater(super_user_hash, msg)
|
||||
await websocket_updater(super_user_hash, msg)
|
||||
|
||||
create_permanent_task(update_websocket_serverlog)
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ from lnbits.db import Connection, FilterModel, FromRowModel
|
|||
from lnbits.helpers import url_for
|
||||
from lnbits.lnurl import encode as lnurl_encode
|
||||
from lnbits.settings import settings
|
||||
from lnbits.wallets import get_wallet_class
|
||||
from lnbits.wallets import get_funding_source
|
||||
from lnbits.wallets.base import PaymentPendingStatus, PaymentStatus
|
||||
|
||||
|
||||
|
@ -265,11 +265,11 @@ class Payment(FromRowModel):
|
|||
f"pending payment {self.checking_id}"
|
||||
)
|
||||
|
||||
WALLET = get_wallet_class()
|
||||
funding_source = get_funding_source()
|
||||
if self.is_out:
|
||||
status = await WALLET.get_payment_status(self.checking_id)
|
||||
status = await funding_source.get_payment_status(self.checking_id)
|
||||
else:
|
||||
status = await WALLET.get_invoice_status(self.checking_id)
|
||||
status = await funding_source.get_invoice_status(self.checking_id)
|
||||
|
||||
logger.debug(f"Status: {status}")
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ from lnbits.settings import (
|
|||
settings,
|
||||
)
|
||||
from lnbits.utils.exchange_rates import fiat_amount_as_satoshis, satoshis_amount_as_fiat
|
||||
from lnbits.wallets import FAKE_WALLET, get_wallet_class, set_wallet_class
|
||||
from lnbits.wallets import fake_wallet, get_funding_source, set_funding_source
|
||||
from lnbits.wallets.base import (
|
||||
PaymentPendingStatus,
|
||||
PaymentResponse,
|
||||
|
@ -62,11 +62,11 @@ from .helpers import to_valid_user_id
|
|||
from .models import Payment, UserConfig, Wallet
|
||||
|
||||
|
||||
class PaymentFailure(Exception):
|
||||
class PaymentError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InvoiceFailure(Exception):
|
||||
class InvoiceError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
|
@ -123,16 +123,16 @@ async def create_invoice(
|
|||
conn: Optional[Connection] = None,
|
||||
) -> Tuple[str, str]:
|
||||
if not amount > 0:
|
||||
raise InvoiceFailure("Amountless invoices not supported.")
|
||||
raise InvoiceError("Amountless invoices not supported.")
|
||||
|
||||
user_wallet = await get_wallet(wallet_id, conn=conn)
|
||||
if not user_wallet:
|
||||
raise InvoiceFailure(f"Could not fetch wallet '{wallet_id}'.")
|
||||
raise InvoiceError(f"Could not fetch wallet '{wallet_id}'.")
|
||||
|
||||
invoice_memo = None if description_hash else memo
|
||||
|
||||
# use the fake wallet if the invoice is for internal use only
|
||||
wallet = FAKE_WALLET if internal else get_wallet_class()
|
||||
funding_source = fake_wallet if internal else get_funding_source()
|
||||
|
||||
amount_sat, extra = await calculate_fiat_amounts(
|
||||
amount, wallet_id, currency=currency, extra=extra, conn=conn
|
||||
|
@ -141,20 +141,22 @@ async def create_invoice(
|
|||
if settings.is_wallet_max_balance_exceeded(
|
||||
user_wallet.balance_msat / 1000 + amount_sat
|
||||
):
|
||||
raise InvoiceFailure(
|
||||
raise InvoiceError(
|
||||
f"Wallet balance cannot exceed "
|
||||
f"{settings.lnbits_wallet_limit_max_balance} sats."
|
||||
)
|
||||
|
||||
ok, checking_id, payment_request, error_message = await wallet.create_invoice(
|
||||
amount=amount_sat,
|
||||
memo=invoice_memo,
|
||||
description_hash=description_hash,
|
||||
unhashed_description=unhashed_description,
|
||||
expiry=expiry or settings.lightning_invoice_expiry,
|
||||
ok, checking_id, payment_request, error_message = (
|
||||
await funding_source.create_invoice(
|
||||
amount=amount_sat,
|
||||
memo=invoice_memo,
|
||||
description_hash=description_hash,
|
||||
unhashed_description=unhashed_description,
|
||||
expiry=expiry or settings.lightning_invoice_expiry,
|
||||
)
|
||||
)
|
||||
if not ok or not payment_request or not checking_id:
|
||||
raise InvoiceFailure(error_message or "unexpected backend error.")
|
||||
raise InvoiceError(error_message or "unexpected backend error.")
|
||||
|
||||
invoice = bolt11_decode(payment_request)
|
||||
|
||||
|
@ -197,12 +199,12 @@ async def pay_invoice(
|
|||
try:
|
||||
invoice = bolt11_decode(payment_request)
|
||||
except Exception:
|
||||
raise InvoiceFailure("Bolt11 decoding failed.")
|
||||
raise InvoiceError("Bolt11 decoding failed.")
|
||||
|
||||
if not invoice.amount_msat or not invoice.amount_msat > 0:
|
||||
raise InvoiceFailure("Amountless invoices not supported.")
|
||||
raise InvoiceError("Amountless invoices not supported.")
|
||||
if max_sat and invoice.amount_msat > max_sat * 1000:
|
||||
raise InvoiceFailure("Amount in invoice is too high.")
|
||||
raise InvoiceError("Amount in invoice is too high.")
|
||||
|
||||
await check_wallet_limits(wallet_id, conn, invoice.amount_msat)
|
||||
|
||||
|
@ -237,7 +239,7 @@ async def pay_invoice(
|
|||
# we check if an internal invoice exists that has already been paid
|
||||
# (not pending anymore)
|
||||
if not await check_internal_pending(invoice.payment_hash, conn=conn):
|
||||
raise PaymentFailure("Internal invoice already paid.")
|
||||
raise PaymentError("Internal invoice already paid.")
|
||||
|
||||
# check_internal() returns the checking_id of the invoice we're waiting for
|
||||
# (pending only)
|
||||
|
@ -256,7 +258,7 @@ async def pay_invoice(
|
|||
internal_invoice.amount != invoice.amount_msat
|
||||
or internal_invoice.bolt11 != payment_request.lower()
|
||||
):
|
||||
raise PaymentFailure("Invalid invoice.")
|
||||
raise PaymentError("Invalid invoice.")
|
||||
|
||||
logger.debug(f"creating temporary internal payment with id {internal_id}")
|
||||
# create a new payment from this wallet
|
||||
|
@ -284,7 +286,7 @@ async def pay_invoice(
|
|||
except Exception as e:
|
||||
logger.error(f"could not create temporary payment: {e}")
|
||||
# happens if the same wallet tries to pay an invoice twice
|
||||
raise PaymentFailure("Could not make payment.")
|
||||
raise PaymentError("Could not make payment.")
|
||||
|
||||
# do the balance check
|
||||
wallet = await get_wallet(wallet_id, conn=conn)
|
||||
|
@ -295,7 +297,7 @@ async def pay_invoice(
|
|||
not internal_checking_id
|
||||
and wallet.balance_msat > -fee_reserve_total_msat
|
||||
):
|
||||
raise PaymentFailure(
|
||||
raise PaymentError(
|
||||
f"You must reserve at least ({round(fee_reserve_total_msat/1000)}"
|
||||
" sat) to cover potential routing fees."
|
||||
)
|
||||
|
@ -323,8 +325,8 @@ async def pay_invoice(
|
|||
service_fee_msat = service_fee(invoice.amount_msat, internal=False)
|
||||
logger.debug(f"backend: sending payment {temp_id}")
|
||||
# actually pay the external invoice
|
||||
WALLET = get_wallet_class()
|
||||
payment: PaymentResponse = await WALLET.pay_invoice(
|
||||
funding_source = get_funding_source()
|
||||
payment: PaymentResponse = await funding_source.pay_invoice(
|
||||
payment_request, fee_reserve_msat
|
||||
)
|
||||
|
||||
|
@ -363,7 +365,7 @@ async def pay_invoice(
|
|||
async with db.connect() as conn:
|
||||
logger.debug(f"deleting temporary payment {temp_id}")
|
||||
await delete_wallet_payment(temp_id, wallet_id, conn=conn)
|
||||
raise PaymentFailure(
|
||||
raise PaymentError(
|
||||
f"Payment failed: {payment.error_message}"
|
||||
or "Payment failed, but backend didn't give us an error message."
|
||||
)
|
||||
|
@ -499,7 +501,6 @@ async def redeem_lnurl_withdraw(
|
|||
async def perform_lnurlauth(
|
||||
callback: str,
|
||||
wallet: WalletTypeInfo = Depends(require_admin_key),
|
||||
conn: Optional[Connection] = None,
|
||||
) -> Optional[LnurlErrorResponse]:
|
||||
cb = urlparse(callback)
|
||||
|
||||
|
@ -592,7 +593,7 @@ async def check_transaction_status(
|
|||
|
||||
|
||||
# WARN: this same value must be used for balance check and passed to
|
||||
# WALLET.pay_invoice(), it may cause a vulnerability if the values differ
|
||||
# funding_source.pay_invoice(), it may cause a vulnerability if the values differ
|
||||
def fee_reserve(amount_msat: int, internal: bool = False) -> int:
|
||||
if internal:
|
||||
return 0
|
||||
|
@ -621,7 +622,7 @@ def fee_reserve_total(amount_msat: int, internal: bool = False) -> int:
|
|||
|
||||
|
||||
async def send_payment_notification(wallet: Wallet, payment: Payment):
|
||||
await websocketUpdater(
|
||||
await websocket_updater(
|
||||
wallet.id,
|
||||
json.dumps(
|
||||
{
|
||||
|
@ -760,25 +761,25 @@ class WebsocketConnectionManager:
|
|||
await connection.send_text(message)
|
||||
|
||||
|
||||
websocketManager = WebsocketConnectionManager()
|
||||
websocket_manager = WebsocketConnectionManager()
|
||||
|
||||
|
||||
async def websocketUpdater(item_id, data):
|
||||
return await websocketManager.send_data(f"{data}", item_id)
|
||||
async def websocket_updater(item_id, data):
|
||||
return await websocket_manager.send_data(f"{data}", item_id)
|
||||
|
||||
|
||||
async def switch_to_voidwallet() -> None:
|
||||
WALLET = get_wallet_class()
|
||||
if WALLET.__class__.__name__ == "VoidWallet":
|
||||
funding_source = get_funding_source()
|
||||
if funding_source.__class__.__name__ == "VoidWallet":
|
||||
return
|
||||
set_wallet_class("VoidWallet")
|
||||
set_funding_source("VoidWallet")
|
||||
settings.lnbits_backend_wallet_class = "VoidWallet"
|
||||
|
||||
|
||||
async def get_balance_delta() -> Tuple[int, int, int]:
|
||||
WALLET = get_wallet_class()
|
||||
funding_source = get_funding_source()
|
||||
total_balance = await get_total_balance()
|
||||
error_message, node_balance = await WALLET.status()
|
||||
error_message, node_balance = await funding_source.status()
|
||||
if error_message:
|
||||
raise Exception(error_message)
|
||||
return node_balance - total_balance, node_balance, total_balance
|
||||
|
|
|
@ -16,7 +16,7 @@ from lnbits.core.services import (
|
|||
send_payment_notification,
|
||||
switch_to_voidwallet,
|
||||
)
|
||||
from lnbits.settings import get_wallet_class, settings
|
||||
from lnbits.settings import get_funding_source, settings
|
||||
from lnbits.tasks import send_push_notification
|
||||
|
||||
api_invoice_listeners: Dict[str, asyncio.Queue] = {}
|
||||
|
@ -28,8 +28,11 @@ async def killswitch_task():
|
|||
LNbits and will switch to VoidWallet if the killswitch is triggered.
|
||||
"""
|
||||
while True:
|
||||
WALLET = get_wallet_class()
|
||||
if settings.lnbits_killswitch and WALLET.__class__.__name__ != "VoidWallet":
|
||||
funding_source = get_funding_source()
|
||||
if (
|
||||
settings.lnbits_killswitch
|
||||
and funding_source.__class__.__name__ != "VoidWallet"
|
||||
):
|
||||
with httpx.Client() as client:
|
||||
try:
|
||||
r = client.get(settings.lnbits_status_manifest, timeout=4)
|
||||
|
@ -55,8 +58,11 @@ async def watchdog_task():
|
|||
and will switch to VoidWallet if the watchdog delta is reached.
|
||||
"""
|
||||
while True:
|
||||
WALLET = get_wallet_class()
|
||||
if settings.lnbits_watchdog and WALLET.__class__.__name__ != "VoidWallet":
|
||||
funding_source = get_funding_source()
|
||||
if (
|
||||
settings.lnbits_watchdog
|
||||
and funding_source.__class__.__name__ != "VoidWallet"
|
||||
):
|
||||
try:
|
||||
delta, *_ = await get_balance_delta()
|
||||
logger.debug(f"Running watchdog task. current delta: {delta}")
|
||||
|
|
|
@ -316,16 +316,16 @@ def _new_sso(provider: str) -> Optional[SSOBase]:
|
|||
logger.warning(f"{provider} auth allowed but not configured.")
|
||||
return None
|
||||
|
||||
SSOProviderClass = _find_auth_provider_class(provider)
|
||||
ssoProvider = SSOProviderClass(
|
||||
sso_provider_class = _find_auth_provider_class(provider)
|
||||
sso_provider = sso_provider_class(
|
||||
client_id, client_secret, None, allow_insecure_http=True
|
||||
)
|
||||
if (
|
||||
discovery_url
|
||||
and getattr(ssoProvider, "discovery_url", discovery_url) != discovery_url
|
||||
and getattr(sso_provider, "discovery_url", discovery_url) != discovery_url
|
||||
):
|
||||
ssoProvider.discovery_url = discovery_url
|
||||
return ssoProvider
|
||||
sso_provider.discovery_url = discovery_url
|
||||
return sso_provider
|
||||
except Exception as e:
|
||||
logger.warning(e)
|
||||
|
||||
|
@ -337,9 +337,9 @@ def _find_auth_provider_class(provider: str) -> Callable:
|
|||
for module in sso_modules:
|
||||
try:
|
||||
provider_module = importlib.import_module(f"{module}.{provider}")
|
||||
ProviderClass = getattr(provider_module, f"{provider.title()}SSO")
|
||||
if ProviderClass:
|
||||
return ProviderClass
|
||||
provider_class = getattr(provider_module, f"{provider.title()}SSO")
|
||||
if provider_class:
|
||||
return provider_class
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ from lnbits.core.models import User
|
|||
from lnbits.decorators import check_admin, check_user_exists
|
||||
from lnbits.helpers import template_renderer, url_for
|
||||
from lnbits.settings import settings
|
||||
from lnbits.wallets import get_wallet_class
|
||||
from lnbits.wallets import get_funding_source
|
||||
|
||||
from ...extension_manager import InstallableExtension, get_valid_extensions
|
||||
from ...utils.exchange_rates import allowed_currencies, currencies
|
||||
|
@ -452,8 +452,8 @@ async def node(request: Request, user: User = Depends(check_admin)):
|
|||
if not settings.lnbits_node_ui:
|
||||
raise HTTPException(status_code=HTTPStatus.SERVICE_UNAVAILABLE)
|
||||
|
||||
WALLET = get_wallet_class()
|
||||
_, balance = await WALLET.status()
|
||||
funding_source = get_funding_source()
|
||||
_, balance = await funding_source.status()
|
||||
|
||||
return template_renderer().TemplateResponse(
|
||||
request,
|
||||
|
@ -472,8 +472,8 @@ async def node_public(request: Request):
|
|||
if not settings.lnbits_public_node_ui:
|
||||
raise HTTPException(status_code=HTTPStatus.SERVICE_UNAVAILABLE)
|
||||
|
||||
WALLET = get_wallet_class()
|
||||
_, balance = await WALLET.status()
|
||||
funding_source = get_funding_source()
|
||||
_, balance = await funding_source.status()
|
||||
|
||||
return template_renderer().TemplateResponse(
|
||||
request,
|
||||
|
@ -490,8 +490,8 @@ async def index(request: Request, user: User = Depends(check_admin)):
|
|||
if not settings.lnbits_admin_ui:
|
||||
raise HTTPException(status_code=HTTPStatus.NOT_FOUND)
|
||||
|
||||
WALLET = get_wallet_class()
|
||||
_, balance = await WALLET.status()
|
||||
funding_source = get_funding_source()
|
||||
_, balance = await funding_source.status()
|
||||
|
||||
return template_renderer().TemplateResponse(
|
||||
request,
|
||||
|
|
|
@ -27,8 +27,8 @@ from ...utils.cache import cache
|
|||
|
||||
|
||||
def require_node():
|
||||
NODE = get_node_class()
|
||||
if not NODE:
|
||||
node_class = get_node_class()
|
||||
if not node_class:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_IMPLEMENTED,
|
||||
detail="Active backend does not implement Node API",
|
||||
|
@ -38,7 +38,7 @@ def require_node():
|
|||
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
|
||||
detail="Not enabled",
|
||||
)
|
||||
return NODE
|
||||
return node_class
|
||||
|
||||
|
||||
def check_public():
|
||||
|
|
|
@ -56,8 +56,8 @@ from ..crud import (
|
|||
update_pending_payments,
|
||||
)
|
||||
from ..services import (
|
||||
InvoiceFailure,
|
||||
PaymentFailure,
|
||||
InvoiceError,
|
||||
PaymentError,
|
||||
check_transaction_status,
|
||||
create_invoice,
|
||||
fee_reserve_total,
|
||||
|
@ -171,7 +171,7 @@ async def api_payments_create_invoice(data: CreateInvoice, wallet: Wallet):
|
|||
payment_db = await get_standalone_payment(payment_hash, conn=conn)
|
||||
assert payment_db is not None, "payment not found"
|
||||
checking_id = payment_db.checking_id
|
||||
except InvoiceFailure as e:
|
||||
except InvoiceError as e:
|
||||
raise HTTPException(status_code=520, detail=str(e))
|
||||
except Exception as exc:
|
||||
raise exc
|
||||
|
@ -230,7 +230,7 @@ async def api_payments_pay_invoice(
|
|||
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail=str(e))
|
||||
except PermissionError as e:
|
||||
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail=str(e))
|
||||
except PaymentFailure as e:
|
||||
except PaymentError as e:
|
||||
raise HTTPException(status_code=520, detail=str(e))
|
||||
except Exception as exc:
|
||||
raise exc
|
||||
|
@ -257,20 +257,20 @@ async def api_payments_pay_invoice(
|
|||
)
|
||||
async def api_payments_create(
|
||||
wallet: WalletTypeInfo = Depends(require_invoice_key),
|
||||
invoiceData: CreateInvoice = Body(...),
|
||||
invoice_data: CreateInvoice = Body(...),
|
||||
):
|
||||
if invoiceData.out is True and wallet.wallet_type == WalletType.admin:
|
||||
if not invoiceData.bolt11:
|
||||
if invoice_data.out is True and wallet.wallet_type == WalletType.admin:
|
||||
if not invoice_data.bolt11:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
detail="BOLT11 string is invalid or not given",
|
||||
)
|
||||
return await api_payments_pay_invoice(
|
||||
invoiceData.bolt11, wallet.wallet, invoiceData.extra
|
||||
invoice_data.bolt11, wallet.wallet, invoice_data.extra
|
||||
) # admin key
|
||||
elif not invoiceData.out:
|
||||
elif not invoice_data.out:
|
||||
# invoice key
|
||||
return await api_payments_create_invoice(invoiceData, wallet.wallet)
|
||||
return await api_payments_create_invoice(invoice_data, wallet.wallet)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.UNAUTHORIZED,
|
||||
|
@ -415,10 +415,10 @@ async def api_payments_sse(
|
|||
|
||||
# TODO: refactor this route into a public and admin one
|
||||
@payment_router.get("/{payment_hash}")
|
||||
async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)):
|
||||
async def api_payment(payment_hash, x_api_key: Optional[str] = Header(None)):
|
||||
# We use X_Api_Key here because we want this call to work with and without keys
|
||||
# If a valid key is given, we also return the field "details", otherwise not
|
||||
wallet = await get_wallet_for_key(X_Api_Key) if isinstance(X_Api_Key, str) else None
|
||||
wallet = await get_wallet_for_key(x_api_key) if isinstance(x_api_key, str) else None
|
||||
|
||||
payment = await get_standalone_payment(
|
||||
payment_hash, wallet_id=wallet.id if wallet else None
|
||||
|
|
|
@ -5,8 +5,8 @@ from fastapi import (
|
|||
)
|
||||
|
||||
from ..services import (
|
||||
websocketManager,
|
||||
websocketUpdater,
|
||||
websocket_manager,
|
||||
websocket_updater,
|
||||
)
|
||||
|
||||
websocket_router = APIRouter(prefix="/api/v1/ws", tags=["Websocket"])
|
||||
|
@ -14,18 +14,18 @@ websocket_router = APIRouter(prefix="/api/v1/ws", tags=["Websocket"])
|
|||
|
||||
@websocket_router.websocket("/{item_id}")
|
||||
async def websocket_connect(websocket: WebSocket, item_id: str):
|
||||
await websocketManager.connect(websocket, item_id)
|
||||
await websocket_manager.connect(websocket, item_id)
|
||||
try:
|
||||
while True:
|
||||
await websocket.receive_text()
|
||||
except WebSocketDisconnect:
|
||||
websocketManager.disconnect(websocket)
|
||||
websocket_manager.disconnect(websocket)
|
||||
|
||||
|
||||
@websocket_router.post("/{item_id}")
|
||||
async def websocket_update_post(item_id: str, data: str):
|
||||
try:
|
||||
await websocketUpdater(item_id, data)
|
||||
await websocket_updater(item_id, data)
|
||||
return {"sent": True, "data": data}
|
||||
except Exception:
|
||||
return {"sent": False, "data": data}
|
||||
|
@ -34,7 +34,7 @@ async def websocket_update_post(item_id: str, data: str):
|
|||
@websocket_router.get("/{item_id}/{data}")
|
||||
async def websocket_update_get(item_id: str, data: str):
|
||||
try:
|
||||
await websocketUpdater(item_id, data)
|
||||
await websocket_updater(item_id, data)
|
||||
return {"sent": True, "data": data}
|
||||
except Exception:
|
||||
return {"sent": False, "data": data}
|
||||
|
|
|
@ -140,14 +140,14 @@ class Connection(Compat):
|
|||
|
||||
def rewrite_values(self, values):
|
||||
# strip html
|
||||
CLEANR = re.compile("<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});")
|
||||
clean_regex = re.compile("<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});")
|
||||
|
||||
# tuple to list and back to tuple
|
||||
raw_values = [values] if isinstance(values, str) else list(values)
|
||||
values = []
|
||||
for raw_value in raw_values:
|
||||
if isinstance(raw_value, str):
|
||||
values.append(re.sub(CLEANR, "", raw_value))
|
||||
values.append(re.sub(clean_regex, "", raw_value))
|
||||
elif isinstance(raw_value, datetime.datetime):
|
||||
ts = raw_value.timestamp()
|
||||
if self.type == SQLITE:
|
||||
|
|
|
@ -138,12 +138,12 @@ async def get_key_type(
|
|||
detail="Invoice (or Admin) key required.",
|
||||
)
|
||||
|
||||
for wallet_type, WalletChecker in zip(
|
||||
for wallet_type, wallet_checker in zip(
|
||||
[WalletType.admin, WalletType.invoice],
|
||||
[WalletAdminKeyChecker, WalletInvoiceKeyChecker],
|
||||
):
|
||||
try:
|
||||
checker = WalletChecker(api_key=token)
|
||||
checker = wallet_checker(api_key=token)
|
||||
await checker.__call__(r)
|
||||
if checker.wallet is None:
|
||||
raise HTTPException(
|
||||
|
|
|
@ -532,10 +532,10 @@ if not settings.lnbits_admin_ui:
|
|||
logger.debug(f"{key}: {value}")
|
||||
|
||||
|
||||
def get_wallet_class():
|
||||
def get_funding_source():
|
||||
"""
|
||||
Backwards compatibility
|
||||
"""
|
||||
from lnbits.wallets import get_wallet_class
|
||||
from lnbits.wallets import get_funding_source
|
||||
|
||||
return get_wallet_class()
|
||||
return get_funding_source()
|
||||
|
|
|
@ -17,7 +17,7 @@ from lnbits.core.crud import (
|
|||
get_standalone_payment,
|
||||
)
|
||||
from lnbits.settings import settings
|
||||
from lnbits.wallets import get_wallet_class
|
||||
from lnbits.wallets import get_funding_source
|
||||
|
||||
tasks: List[asyncio.Task] = []
|
||||
unique_tasks: Dict[str, asyncio.Task] = {}
|
||||
|
@ -119,8 +119,8 @@ async def invoice_listener():
|
|||
|
||||
Called by the app startup sequence.
|
||||
"""
|
||||
WALLET = get_wallet_class()
|
||||
async for checking_id in WALLET.paid_invoices_stream():
|
||||
funding_source = get_funding_source()
|
||||
async for checking_id in funding_source.paid_invoices_stream():
|
||||
logger.info("> got a payment notification", checking_id)
|
||||
create_task(invoice_callback_dispatcher(checking_id))
|
||||
|
||||
|
|
|
@ -28,21 +28,21 @@ from .void import VoidWallet
|
|||
from .zbd import ZBDWallet
|
||||
|
||||
|
||||
def set_wallet_class(class_name: Optional[str] = None):
|
||||
def set_funding_source(class_name: Optional[str] = None):
|
||||
backend_wallet_class = class_name or settings.lnbits_backend_wallet_class
|
||||
wallet_class = getattr(wallets_module, backend_wallet_class)
|
||||
global WALLET
|
||||
WALLET = wallet_class()
|
||||
if WALLET.__node_cls__:
|
||||
set_node_class(WALLET.__node_cls__(WALLET))
|
||||
funding_source_constructor = getattr(wallets_module, backend_wallet_class)
|
||||
global funding_source
|
||||
funding_source = funding_source_constructor()
|
||||
if funding_source.__node_cls__:
|
||||
set_node_class(funding_source.__node_cls__(funding_source))
|
||||
|
||||
|
||||
def get_wallet_class() -> Wallet:
|
||||
return WALLET
|
||||
def get_funding_source() -> Wallet:
|
||||
return funding_source
|
||||
|
||||
|
||||
wallets_module = importlib.import_module("lnbits.wallets")
|
||||
FAKE_WALLET = FakeWallet()
|
||||
fake_wallet = FakeWallet()
|
||||
|
||||
# initialize as fake wallet
|
||||
WALLET: Wallet = FAKE_WALLET
|
||||
funding_source: Wallet = fake_wallet
|
||||
|
|
|
@ -144,5 +144,5 @@ class Wallet(ABC):
|
|||
return endpoint
|
||||
|
||||
|
||||
class Unsupported(Exception):
|
||||
class UnsupportedError(Exception):
|
||||
pass
|
||||
|
|
|
@ -18,7 +18,7 @@ from .base import (
|
|||
PaymentStatus,
|
||||
PaymentSuccessStatus,
|
||||
StatusResponse,
|
||||
Unsupported,
|
||||
UnsupportedError,
|
||||
Wallet,
|
||||
)
|
||||
|
||||
|
@ -73,12 +73,12 @@ class CoreLightningWallet(Wallet):
|
|||
msat: int = int(amount * 1000)
|
||||
try:
|
||||
if description_hash and not unhashed_description:
|
||||
raise Unsupported(
|
||||
raise UnsupportedError(
|
||||
"'description_hash' unsupported by CoreLightning, provide"
|
||||
" 'unhashed_description'"
|
||||
)
|
||||
if unhashed_description and not self.supports_description_hash:
|
||||
raise Unsupported("unhashed_description")
|
||||
raise UnsupportedError("unhashed_description")
|
||||
r: dict = self.ln.invoice( # type: ignore
|
||||
msatoshi=msat,
|
||||
label=label,
|
||||
|
|
|
@ -16,7 +16,7 @@ from .base import (
|
|||
PaymentResponse,
|
||||
PaymentStatus,
|
||||
StatusResponse,
|
||||
Unsupported,
|
||||
UnsupportedError,
|
||||
Wallet,
|
||||
)
|
||||
from .macaroon import load_macaroon
|
||||
|
@ -104,7 +104,7 @@ class CoreLightningRestWallet(Wallet):
|
|||
"label": label,
|
||||
}
|
||||
if description_hash and not unhashed_description:
|
||||
raise Unsupported(
|
||||
raise UnsupportedError(
|
||||
"'description_hash' unsupported by CoreLightningRest, "
|
||||
"provide 'unhashed_description'"
|
||||
)
|
||||
|
|
|
@ -40,8 +40,8 @@ class EclairWallet(Wallet):
|
|||
self.ws_url = f"ws://{urllib.parse.urlsplit(self.url).netloc}/ws"
|
||||
|
||||
password = settings.eclair_pass
|
||||
encodedAuth = base64.b64encode(f":{password}".encode())
|
||||
auth = str(encodedAuth, "utf-8")
|
||||
encoded_auth = base64.b64encode(f":{password}".encode())
|
||||
auth = str(encoded_auth, "utf-8")
|
||||
self.headers = {
|
||||
"Authorization": f"Basic {auth}",
|
||||
"User-Agent": settings.user_agent,
|
||||
|
|
|
@ -164,13 +164,13 @@ class LndRestWallet(Wallet):
|
|||
|
||||
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
|
||||
# set the fee limit for the payment
|
||||
lnrpcFeeLimit = {}
|
||||
lnrpcFeeLimit["fixed_msat"] = f"{fee_limit_msat}"
|
||||
lnrpc_fee_limit = {}
|
||||
lnrpc_fee_limit["fixed_msat"] = f"{fee_limit_msat}"
|
||||
|
||||
try:
|
||||
r = await self.client.post(
|
||||
url="/v1/channels/transactions",
|
||||
json={"payment_request": bolt11, "fee_limit": lnrpcFeeLimit},
|
||||
json={"payment_request": bolt11, "fee_limit": lnrpc_fee_limit},
|
||||
timeout=None,
|
||||
)
|
||||
r.raise_for_status()
|
||||
|
|
|
@ -12,7 +12,7 @@ from .base import (
|
|||
PaymentResponse,
|
||||
PaymentStatus,
|
||||
StatusResponse,
|
||||
Unsupported,
|
||||
UnsupportedError,
|
||||
Wallet,
|
||||
)
|
||||
|
||||
|
@ -74,7 +74,7 @@ class OpenNodeWallet(Wallet):
|
|||
**kwargs,
|
||||
) -> InvoiceResponse:
|
||||
if description_hash or unhashed_description:
|
||||
raise Unsupported("description_hash")
|
||||
raise UnsupportedError("description_hash")
|
||||
|
||||
r = await self.client.post(
|
||||
"/v1/charges",
|
||||
|
|
|
@ -13,7 +13,7 @@ from .base import (
|
|||
PaymentResponse,
|
||||
PaymentStatus,
|
||||
StatusResponse,
|
||||
Unsupported,
|
||||
UnsupportedError,
|
||||
Wallet,
|
||||
)
|
||||
|
||||
|
@ -65,7 +65,7 @@ class ZBDWallet(Wallet):
|
|||
) -> InvoiceResponse:
|
||||
# https://api.zebedee.io/v0/charges
|
||||
if description_hash or unhashed_description:
|
||||
raise Unsupported("description_hash")
|
||||
raise UnsupportedError("description_hash")
|
||||
|
||||
msats_amount = amount * 1000
|
||||
data: Dict = {
|
||||
|
|
|
@ -168,7 +168,8 @@ extend-exclude = [
|
|||
# I - isort
|
||||
# A - flake8-builtins
|
||||
# C - mccabe
|
||||
select = ["F", "E", "W", "I", "A", "C"]
|
||||
# N - naming
|
||||
select = ["F", "E", "W", "I", "A", "C", "N"]
|
||||
ignore = []
|
||||
|
||||
# Allow autofix for all enabled rules (when `--fix`) is provided.
|
||||
|
@ -178,6 +179,12 @@ unfixable = []
|
|||
# Allow unused variables when underscore-prefixed.
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
# needed for pydantic
|
||||
[tool.ruff.lint.pep8-naming]
|
||||
classmethod-decorators = [
|
||||
"root_validator",
|
||||
]
|
||||
|
||||
# Ignore unused imports in __init__.py files.
|
||||
[tool.ruff.lint.extend-per-file-ignores]
|
||||
"__init__.py" = ["F401", "F403"]
|
||||
|
|
|
@ -176,8 +176,8 @@ async def adminkey_headers_to(to_wallet):
|
|||
@pytest_asyncio.fixture(scope="session")
|
||||
async def invoice(to_wallet):
|
||||
data = await get_random_invoice_data()
|
||||
invoiceData = CreateInvoice(**data)
|
||||
invoice = await api_payments_create_invoice(invoiceData, to_wallet)
|
||||
invoice_data = CreateInvoice(**data)
|
||||
invoice = await api_payments_create_invoice(invoice_data, to_wallet)
|
||||
yield invoice
|
||||
del invoice
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ from lnbits.core.services import fee_reserve_total
|
|||
from lnbits.core.views.admin_api import api_auditor
|
||||
from lnbits.core.views.payment_api import api_payment
|
||||
from lnbits.settings import settings
|
||||
from lnbits.wallets import get_wallet_class
|
||||
from lnbits.wallets import get_funding_source
|
||||
|
||||
from ...helpers import (
|
||||
cancel_invoice,
|
||||
|
@ -22,7 +22,7 @@ from ...helpers import (
|
|||
settle_invoice,
|
||||
)
|
||||
|
||||
WALLET = get_wallet_class()
|
||||
funding_source = get_funding_source()
|
||||
|
||||
|
||||
# create account POST /api/v1/account
|
||||
|
@ -407,7 +407,8 @@ async def test_api_payment_with_key(invoice, inkey_headers_from):
|
|||
|
||||
# check POST /api/v1/payments: invoice creation with a description hash
|
||||
@pytest.mark.skipif(
|
||||
WALLET.__class__.__name__ in ["CoreLightningWallet", "CoreLightningRestWallet"],
|
||||
funding_source.__class__.__name__
|
||||
in ["CoreLightningWallet", "CoreLightningRestWallet"],
|
||||
reason="wallet does not support description_hash",
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
|
@ -428,7 +429,7 @@ async def test_create_invoice_with_description_hash(client, inkey_headers_to):
|
|||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
WALLET.__class__.__name__ in ["CoreLightningRestWallet"],
|
||||
funding_source.__class__.__name__ in ["CoreLightningRestWallet"],
|
||||
reason="wallet does not support unhashed_description",
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
|
@ -540,8 +541,8 @@ async def test_pay_real_invoice(
|
|||
payment_status = response.json()
|
||||
assert payment_status["paid"]
|
||||
|
||||
WALLET = get_wallet_class()
|
||||
status = await WALLET.get_payment_status(invoice["payment_hash"])
|
||||
funding_source = get_funding_source()
|
||||
status = await funding_source.get_payment_status(invoice["payment_hash"])
|
||||
assert status.paid
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
@ -571,7 +572,7 @@ async def test_create_real_invoice(client, adminkey_headers_from, inkey_headers_
|
|||
|
||||
async def listen():
|
||||
found_checking_id = False
|
||||
async for checking_id in get_wallet_class().paid_invoices_stream():
|
||||
async for checking_id in get_funding_source().paid_invoices_stream():
|
||||
if checking_id == invoice["checking_id"]:
|
||||
found_checking_id = True
|
||||
return
|
||||
|
@ -622,8 +623,8 @@ async def test_pay_real_invoice_set_pending_and_check_state(
|
|||
assert response["paid"]
|
||||
|
||||
# make sure that the backend also thinks it's paid
|
||||
WALLET = get_wallet_class()
|
||||
status = await WALLET.get_payment_status(invoice["payment_hash"])
|
||||
funding_source = get_funding_source()
|
||||
status = await funding_source.get_payment_status(invoice["payment_hash"])
|
||||
assert status.paid
|
||||
|
||||
# get the outgoing payment from the db
|
||||
|
@ -800,7 +801,7 @@ async def test_receive_real_invoice_set_pending_and_check_state(
|
|||
|
||||
async def listen():
|
||||
found_checking_id = False
|
||||
async for checking_id in get_wallet_class().paid_invoices_stream():
|
||||
async for checking_id in get_funding_source().paid_invoices_stream():
|
||||
if checking_id == invoice["checking_id"]:
|
||||
found_checking_id = True
|
||||
return
|
||||
|
|
|
@ -10,14 +10,15 @@ from lnbits.nodes.base import ChannelPoint, ChannelState, NodeChannel
|
|||
from tests.conftest import pytest_asyncio, settings
|
||||
|
||||
from ...helpers import (
|
||||
WALLET,
|
||||
funding_source,
|
||||
get_random_invoice_data,
|
||||
get_unconnected_node_uri,
|
||||
mine_blocks,
|
||||
)
|
||||
|
||||
pytestmark = pytest.mark.skipif(
|
||||
WALLET.__node_cls__ is None, reason="Cant test if node implementation isnt avilable"
|
||||
funding_source.__node_cls__ is None,
|
||||
reason="Cant test if node implementation isnt available",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ from pydantic import BaseModel
|
|||
|
||||
from lnbits import core
|
||||
from lnbits.db import DB_TYPE, POSTGRES, FromRowModel
|
||||
from lnbits.wallets import get_wallet_class, set_wallet_class
|
||||
from lnbits.wallets import get_funding_source, set_funding_source
|
||||
|
||||
|
||||
class DbTestModel(FromRowModel):
|
||||
|
@ -23,10 +23,10 @@ class DbTestModel(FromRowModel):
|
|||
value: Optional[str] = None
|
||||
|
||||
|
||||
def get_random_string(N: int = 10):
|
||||
def get_random_string(iterations: int = 10):
|
||||
return "".join(
|
||||
random.SystemRandom().choice(string.ascii_uppercase + string.digits)
|
||||
for _ in range(N)
|
||||
for _ in range(iterations)
|
||||
)
|
||||
|
||||
|
||||
|
@ -34,9 +34,9 @@ async def get_random_invoice_data():
|
|||
return {"out": False, "amount": 10, "memo": f"test_memo_{get_random_string(10)}"}
|
||||
|
||||
|
||||
set_wallet_class()
|
||||
WALLET = get_wallet_class()
|
||||
is_fake: bool = WALLET.__class__.__name__ == "FakeWallet"
|
||||
set_funding_source()
|
||||
funding_source = get_funding_source()
|
||||
is_fake: bool = funding_source.__class__.__name__ == "FakeWallet"
|
||||
is_regtest: bool = not is_fake
|
||||
|
||||
|
||||
|
|
|
@ -131,39 +131,39 @@ def migrate_db(file: str, schema: str, exclude_tables: List[str] = []):
|
|||
).fetchall()
|
||||
|
||||
for table in tables:
|
||||
tableName = table[0]
|
||||
print(f"Migrating table {tableName}")
|
||||
table_name = table[0]
|
||||
print(f"Migrating table {table_name}")
|
||||
# hard coded skip for dbversions (already produced during startup)
|
||||
if tableName == "dbversions":
|
||||
if table_name == "dbversions":
|
||||
continue
|
||||
if exclude_tables and tableName in exclude_tables:
|
||||
if exclude_tables and table_name in exclude_tables:
|
||||
continue
|
||||
|
||||
columns = cursor.execute(f"PRAGMA table_info({tableName})").fetchall()
|
||||
q = build_insert_query(schema, tableName, columns)
|
||||
columns = cursor.execute(f"PRAGMA table_info({table_name})").fetchall()
|
||||
q = build_insert_query(schema, table_name, columns)
|
||||
|
||||
data = cursor.execute(f"SELECT * FROM {tableName};").fetchall()
|
||||
data = cursor.execute(f"SELECT * FROM {table_name};").fetchall()
|
||||
|
||||
if len(data) == 0:
|
||||
print(f"🛑 You sneaky dev! Table {tableName} is empty!")
|
||||
print(f"🛑 You sneaky dev! Table {table_name} is empty!")
|
||||
|
||||
insert_to_pg(q, data)
|
||||
cursor.close()
|
||||
|
||||
|
||||
def build_insert_query(schema, tableName, columns):
|
||||
def build_insert_query(schema, table_name, columns):
|
||||
to_columns = ", ".join([f'"{column[1].lower()}"' for column in columns])
|
||||
values = ", ".join([to_column_type(column[2]) for column in columns])
|
||||
return f"""
|
||||
INSERT INTO {schema}.{tableName}({to_columns})
|
||||
INSERT INTO {schema}.{table_name}({to_columns})
|
||||
VALUES ({values});
|
||||
"""
|
||||
|
||||
|
||||
def to_column_type(columnType):
|
||||
if columnType == "TIMESTAMP":
|
||||
def to_column_type(column_type):
|
||||
if column_type == "TIMESTAMP":
|
||||
return "to_timestamp(%s)"
|
||||
if columnType in ["BOOLEAN", "BOOL"]:
|
||||
if column_type in ["BOOLEAN", "BOOL"]:
|
||||
return "%s::boolean"
|
||||
return "%s"
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue