From 83a000d7c9795b81fbe88f9d444c27bc0d20ad40 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 19 Jul 2022 13:06:19 +0200 Subject: [PATCH 01/63] fix up mypy.ini, and properly ignore grpc and extension --- mypy.ini | 7 ++++--- requirements.txt | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mypy.ini b/mypy.ini index 735f89e05..e5a974b51 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,7 +1,8 @@ [mypy] ignore_missing_imports = True -exclude = lnbits/wallets/lnd_grpc_files/ -exclude = lnbits/extensions/ - +exclude = (?x)( + ^lnbits/extensions. + | ^lnbits/wallets/lnd_grpc_files. + ) [mypy-lnbits.wallets.lnd_grpc_files.*] follow_imports = skip diff --git a/requirements.txt b/requirements.txt index 8359456ff..322712f5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -43,9 +43,10 @@ sqlalchemy==1.3.23 sqlalchemy-aio==0.16.0 sse-starlette==0.6.2 starlette==0.14.2 +types-protobuf==3.19.22 typing-extensions==3.10.0.2 uvicorn==0.15.0 uvloop==0.16.0 watchgod==0.7 websockets==10.0 -zipp==3.5.0 \ No newline at end of file +zipp==3.5.0 From 45a13fd807433debcc8c83915b2100aef555bb0a Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 19 Jul 2022 13:36:44 +0200 Subject: [PATCH 02/63] remove commented out and unused code in app object, it is now used in lnbits/__main__.py directly, fixes mypy error --- lnbits/app.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index a7c8fdaf3..eaf8a9db4 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -17,7 +17,6 @@ from loguru import logger import lnbits.settings from lnbits.core.tasks import register_task_listeners -from .commands import db_migrate, handle_assets from .core import core_app from .core.views.generic import core_html_routes from .helpers import ( @@ -84,7 +83,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI: check_funding_source(app) register_assets(app) register_routes(app) - # register_commands(app) register_async_tasks(app) register_exception_handlers(app) @@ -137,12 +135,6 @@ def register_routes(app: FastAPI) -> None: ) -def register_commands(app: FastAPI): - """Register Click commands.""" - app.cli.add_command(db_migrate) - app.cli.add_command(handle_assets) - - def register_assets(app: FastAPI): """Serve each vendored asset separately or a bundle.""" From 8901ff8084ae3638f95c5616086ccbdcf8b175d8 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 19 Jul 2022 18:51:35 +0200 Subject: [PATCH 03/63] some more mypy fixes --- lnbits/core/crud.py | 2 +- lnbits/core/models.py | 2 ++ lnbits/core/views/api.py | 15 +++++++++------ lnbits/core/views/generic.py | 6 +++++- lnbits/helpers.py | 2 +- lnbits/tasks.py | 2 +- lnbits/utils/exchange_rates.py | 8 ++++---- lnbits/wallets/lnpay.py | 2 +- lnbits/wallets/opennode.py | 2 +- 9 files changed, 25 insertions(+), 16 deletions(-) diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index 770e2906a..db802d7bb 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -113,7 +113,7 @@ async def create_wallet( async def update_wallet( wallet_id: str, new_name: str, conn: Optional[Connection] = None ) -> Optional[Wallet]: - await (conn or db).execute( + return await (conn or db).execute( """ UPDATE wallets SET name = ? diff --git a/lnbits/core/models.py b/lnbits/core/models.py index ab73b7020..0f7eba737 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -106,6 +106,8 @@ class Payment(BaseModel): @property def tag(self) -> Optional[str]: + if self.extra is None: + return "" return self.extra.get("tag") @property diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 9fee6063d..bd15ee8d6 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -3,7 +3,7 @@ import hashlib import json from binascii import unhexlify from http import HTTPStatus -from typing import Dict, List, Optional, Union +from typing import Dict, List, Optional, Union, Tuple from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx @@ -185,7 +185,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet): assert ( data.lnurl_balance_check is not None ), "lnurl_balance_check is required" - save_balance_check(wallet.id, data.lnurl_balance_check) + await save_balance_check(wallet.id, data.lnurl_balance_check) async with httpx.AsyncClient() as client: try: @@ -291,7 +291,7 @@ async def api_payments_pay_lnurl( timeout=40, ) if r.is_error: - raise httpx.ConnectError + raise httpx.ConnectError("LNURL Callback Connection Error") except (httpx.ConnectError, httpx.RequestError): raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, @@ -348,7 +348,7 @@ async def subscribe(request: Request, wallet: Wallet): logger.debug("adding sse listener", payment_queue) api_invoice_listeners.append(payment_queue) - send_queue: asyncio.Queue[tuple[str, Payment]] = asyncio.Queue(0) + send_queue: asyncio.Queue[Tuple[str, Payment]] = asyncio.Queue(0) async def payment_received() -> None: while True: @@ -389,10 +389,13 @@ async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)): # If a valid key is given, we also return the field "details", otherwise not wallet = None try: + assert X_Api_Key is not None + # TODO: type above is Optional[str] how can that have .extra? if X_Api_Key.extra: logger.warning("No key") except: - wallet = await get_wallet_for_key(X_Api_Key) + if X_Api_Key is not None: + wallet = await get_wallet_for_key(X_Api_Key) payment = await get_standalone_payment( payment_hash, wallet_id=wallet.id if wallet else None ) # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order @@ -606,7 +609,7 @@ class ConversionData(BaseModel): async def api_fiat_as_sats(data: ConversionData): output = {} if data.from_ == "sat": - output["sats"] = int(data.amount) + output["sats"] = data.amount output["BTC"] = data.amount / 100000000 for currency in data.to.split(","): output[currency.strip().upper()] = await satoshis_amount_as_fiat( diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 4366028d7..d75aa77e2 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -112,9 +112,11 @@ async def wallet( if not user_id: user = await get_user((await create_account()).id) + assert user is not None logger.info(f"Created new account for user {user.id}") else: user = await get_user(user_id) + assert user is not None if not user: return template_renderer().TemplateResponse( "error.html", {"request": request, "err": "User does not exist."} @@ -209,6 +211,7 @@ async def lnurl_full_withdraw_callback(request: Request): @core_html_routes.get("/deletewallet", response_class=RedirectResponse) async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): user = await get_user(usr) + assert user is not None user_wallet_ids = [u.id for u in user.wallets] if wal not in user_wallet_ids: @@ -233,7 +236,7 @@ async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query async def lnurl_balance_notify(request: Request, service: str): bc = await get_balance_check(request.query_params.get("wal"), service) if bc: - redeem_lnurl_withdraw(bc.wallet, bc.url) + await redeem_lnurl_withdraw(bc.wallet, bc.url) @core_html_routes.get( @@ -243,6 +246,7 @@ async def lnurlwallet(request: Request): async with db.connect() as conn: account = await create_account(conn=conn) user = await get_user(account.id, conn=conn) + assert user is not None wallet = await create_wallet(user_id=user.id, conn=conn) asyncio.create_task( diff --git a/lnbits/helpers.py b/lnbits/helpers.py index 71b3dd691..e97fc7bbb 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -34,7 +34,7 @@ class ExtensionManager: @property def extensions(self) -> List[Extension]: - output = [] + output: List[Extension] = [] if "all" in self._disabled: return output diff --git a/lnbits/tasks.py b/lnbits/tasks.py index 86863f98f..f4d0a928d 100644 --- a/lnbits/tasks.py +++ b/lnbits/tasks.py @@ -66,7 +66,7 @@ async def webhook_handler(): raise HTTPException(status_code=HTTPStatus.NO_CONTENT) -internal_invoice_queue = asyncio.Queue(0) +internal_invoice_queue: asyncio.Queue = asyncio.Queue(0) async def internal_invoice_listener(): diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py index 0432b364a..fbb4add22 100644 --- a/lnbits/utils/exchange_rates.py +++ b/lnbits/utils/exchange_rates.py @@ -1,5 +1,5 @@ import asyncio -from typing import Callable, NamedTuple +from typing import Callable, NamedTuple, List import httpx from loguru import logger @@ -227,10 +227,10 @@ async def btc_price(currency: str) -> float: "TO": currency.upper(), "to": currency.lower(), } - rates = [] - tasks = [] + rates: List[float] = [] + tasks: List[asyncio.Task] = [] - send_channel = asyncio.Queue() + send_channel: asyncio.Queue = asyncio.Queue() async def controller(): failures = 0 diff --git a/lnbits/wallets/lnpay.py b/lnbits/wallets/lnpay.py index 807d72538..2ff1afa93 100644 --- a/lnbits/wallets/lnpay.py +++ b/lnbits/wallets/lnpay.py @@ -119,7 +119,7 @@ class LNPayWallet(Wallet): return PaymentStatus(statuses[r.json()["settled"]]) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: - self.queue = asyncio.Queue(0) + self.queue: asyncio.Queue = asyncio.Queue(0) while True: value = await self.queue.get() yield value diff --git a/lnbits/wallets/opennode.py b/lnbits/wallets/opennode.py index 6d3fb02c9..9cd05ebdd 100644 --- a/lnbits/wallets/opennode.py +++ b/lnbits/wallets/opennode.py @@ -127,7 +127,7 @@ class OpenNodeWallet(Wallet): return PaymentStatus(statuses[r.json()["data"]["status"]]) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: - self.queue = asyncio.Queue(0) + self.queue: asyncio.Queue = asyncio.Queue(0) while True: value = await self.queue.get() yield value From 91c5a68cccb254665318adbf128f36e39dda1292 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 19 Jul 2022 19:20:20 +0200 Subject: [PATCH 04/63] mypy fakewallet, macaroon, bolt11 invoice --- lnbits/bolt11.py | 1 + lnbits/wallets/fake.py | 10 +++++++--- lnbits/wallets/macaroon/macaroon.py | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lnbits/bolt11.py b/lnbits/bolt11.py index cc8415852..cc8c60401 100644 --- a/lnbits/bolt11.py +++ b/lnbits/bolt11.py @@ -23,6 +23,7 @@ class Route(NamedTuple): class Invoice(object): payment_hash: str + checking_id: Optional[str] = None amount_msat: int = 0 description: Optional[str] = None description_hash: Optional[str] = None diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py index 3859d33c5..c2b2307ab 100644 --- a/lnbits/wallets/fake.py +++ b/lnbits/wallets/fake.py @@ -28,7 +28,7 @@ class FakeWallet(Wallet): logger.info( "FakeWallet funding source is for using LNbits as a centralised, stand-alone payment system with brrrrrr." ) - return StatusResponse(None, float("inf")) + return StatusResponse(None, 1000000000) async def create_invoice( self, @@ -80,8 +80,12 @@ class FakeWallet(Wallet): async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: invoice = decode(bolt11) + # TODO: no data here? + data: Dict = { + "privkey": "missing" + } if ( - hasattr(invoice, "checking_id") + invoice.checking_id is not None and invoice.checking_id[6:] == data["privkey"][:6] ): return PaymentResponse(True, invoice.payment_hash, 0) @@ -97,7 +101,7 @@ class FakeWallet(Wallet): return PaymentStatus(None) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: - self.queue = asyncio.Queue(0) + self.queue: asyncio.Queue = asyncio.Queue(0) while True: value = await self.queue.get() yield value diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py index 2183dacb8..aa00cd3e6 100644 --- a/lnbits/wallets/macaroon/macaroon.py +++ b/lnbits/wallets/macaroon/macaroon.py @@ -73,7 +73,7 @@ class AESCipher(object): final_key += key return final_key[:output] - def decrypt(self, encrypted: str) -> str: + def decrypt(self, encrypted) -> str: """Decrypts a string using AES-256-CBC.""" passphrase = self.passphrase encrypted = base64.b64decode(encrypted) From 886c74f205ebb725ec98729a956f5a5bab65c331 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 08:43:10 +0200 Subject: [PATCH 05/63] assert Optional[str] --- lnbits/core/tasks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py index 5fea769d4..8234b5fc4 100644 --- a/lnbits/core/tasks.py +++ b/lnbits/core/tasks.py @@ -55,6 +55,7 @@ async def dispatch_webhook(payment: Payment): data = payment.dict() try: logger.debug("sending webhook", payment.webhook) + assert payment.webhook is not None r = await client.post(payment.webhook, json=data, timeout=40) await mark_webhook_sent(payment, r.status_code) except (httpx.ConnectError, httpx.RequestError): From 045212a0701157c252f6425d8b50c48f2191b5d9 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 09:36:13 +0200 Subject: [PATCH 06/63] mypy fixes for generic.py, decurators.py, eclair.py --- lnbits/core/views/generic.py | 16 +++++++-------- lnbits/decorators.py | 40 ++++++++++++++++++++---------------- lnbits/wallets/eclair.py | 4 +++- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index d75aa77e2..7ecedcf82 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -54,9 +54,9 @@ async def home(request: Request, lightning: str = None): ) async def extensions( request: Request, - user: User = Depends(check_user_exists), - enable: str = Query(None), - disable: str = Query(None), + user = Depends(check_user_exists), + enable = Query(None), + disable = Query(None), ): extension_to_enable = enable extension_to_disable = disable @@ -100,10 +100,10 @@ nothing: create everything
""", ) async def wallet( - request: Request = Query(None), - nme: Optional[str] = Query(None), - usr: Optional[UUID4] = Query(None), - wal: Optional[UUID4] = Query(None), + request = Query(None), + nme = Query(None), + usr = Query(None), + wal = Query(None), ): user_id = usr.hex if usr else None wallet_id = wal.hex if wal else None @@ -209,7 +209,7 @@ async def lnurl_full_withdraw_callback(request: Request): @core_html_routes.get("/deletewallet", response_class=RedirectResponse) -async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): +async def deletewallet(request: Request, wal = Query(...), usr = Query(...)): user = await get_user(usr) assert user is not None user_wallet_ids = [u.id for u in user.wallets] diff --git a/lnbits/decorators.py b/lnbits/decorators.py index e65b9041a..77fe32276 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -1,5 +1,7 @@ from http import HTTPStatus +from typing import Optional + from cerberus import Validator # type: ignore from fastapi import status from fastapi.exceptions import HTTPException @@ -29,20 +31,20 @@ class KeyChecker(SecurityBase): self._key_type = "invoice" self._api_key = api_key if api_key: - self.model: APIKey = APIKey( + key = APIKey( **{"in": APIKeyIn.query}, name="X-API-KEY", description="Wallet API Key - QUERY", ) else: - self.model: APIKey = APIKey( + key = APIKey( **{"in": APIKeyIn.header}, name="X-API-KEY", description="Wallet API Key - HEADER", ) - self.wallet = None + self.model: APIKey = key - async def __call__(self, request: Request) -> Wallet: + async def __call__(self, request: Request): try: key_value = ( self._api_key @@ -52,12 +54,13 @@ class KeyChecker(SecurityBase): # FIXME: Find another way to validate the key. A fetch from DB should be avoided here. # Also, we should not return the wallet here - thats silly. # Possibly store it in a Redis DB - self.wallet = await get_wallet_for_key(key_value, self._key_type) - if not self.wallet: + wallet = await get_wallet_for_key(key_value, self._key_type) + if not wallet: raise HTTPException( status_code=HTTPStatus.UNAUTHORIZED, detail="Invalid key or expired key.", ) + self.wallet = wallet except KeyError: raise HTTPException( @@ -120,8 +123,8 @@ api_key_query = APIKeyQuery( async def get_key_type( r: Request, - api_key_header: str = Security(api_key_header), - api_key_query: str = Security(api_key_query), + api_key_header = Security(api_key_header), + api_key_query = Security(api_key_query), ) -> WalletTypeInfo: # 0: admin # 1: invoice @@ -134,9 +137,10 @@ async def get_key_type( token = api_key_header if api_key_header else api_key_query try: - checker = WalletAdminKeyChecker(api_key=token) - await checker.__call__(r) - wallet = WalletTypeInfo(0, checker.wallet) + admin_checker = WalletAdminKeyChecker(api_key=token) + await admin_checker.__call__(r) + wallet = WalletTypeInfo(0, admin_checker.wallet) + assert wallet.wallet is not None if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and ( LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS ): @@ -153,9 +157,9 @@ async def get_key_type( raise try: - checker = WalletInvoiceKeyChecker(api_key=token) - await checker.__call__(r) - wallet = WalletTypeInfo(1, checker.wallet) + invoice_checker = WalletInvoiceKeyChecker(api_key=token) + await invoice_checker.__call__(r) + wallet = WalletTypeInfo(1, invoice_checker.wallet) if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and ( LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS ): @@ -174,8 +178,8 @@ async def get_key_type( async def require_admin_key( r: Request, - api_key_header: str = Security(api_key_header), - api_key_query: str = Security(api_key_query), + api_key_header = Security(api_key_header), + api_key_query = Security(api_key_query), ): token = api_key_header if api_key_header else api_key_query @@ -193,8 +197,8 @@ async def require_admin_key( async def require_invoice_key( r: Request, - api_key_header: str = Security(api_key_header), - api_key_query: str = Security(api_key_query), + api_key_header = Security(api_key_header), + api_key_query = Security(api_key_query), ): token = api_key_header if api_key_header else api_key_query diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index 0ac3fd2a0..bad707ff1 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -7,7 +7,9 @@ from typing import AsyncGenerator, Dict, Optional import httpx from loguru import logger -from websockets import connect + +# mypy https://github.com/aaugustin/websockets/issues/940 +from websockets.client import connect from websockets.exceptions import ( ConnectionClosed, ConnectionClosedError, From e1d1ce7edca9a26a4c956f82031f81f54f98ccbe Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 09:45:08 +0200 Subject: [PATCH 07/63] fix PaymentKwargs class for mypy --- lnbits/core/services.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 0b565ebb9..f4f962504 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -109,18 +109,15 @@ async def pay_invoice( raise ValueError("Amount in invoice is too high.") # put all parameters that don't change here - PaymentKwargs = TypedDict( - "PaymentKwargs", - { - "wallet_id": str, - "payment_request": str, - "payment_hash": str, - "amount": int, - "memo": str, - "extra": Optional[Dict], - }, - ) - payment_kwargs: PaymentKwargs = dict( + class PaymentKwargs(TypedDict): + wallet_id: str + payment_request: str + payment_hash: str + amount: int + memo: str + extra: Optional[Dict] + + payment_kwargs: PaymentKwargs = PaymentKwargs( wallet_id=wallet_id, payment_request=payment_request, payment_hash=invoice.payment_hash, From 68a195952ada90895c11b5f146e643605f2366bf Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 10:05:30 +0200 Subject: [PATCH 08/63] mypy fixes, api, eclair --- lnbits/core/views/api.py | 4 ++-- lnbits/wallets/eclair.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index bd15ee8d6..09eb4f906 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -181,7 +181,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet): lnurl_response: Union[None, bool, str] = None if data.lnurl_callback: - if "lnurl_balance_check" in data: + if hasattr(data, "lnurl_balance_check"): assert ( data.lnurl_balance_check is not None ), "lnurl_balance_check is required" @@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet): ) async def api_payments_create( wallet: WalletTypeInfo = Depends(require_invoice_key), - invoiceData: CreateInvoiceData = Body(...), + invoiceData = Body(...), ): if invoiceData.out is True and wallet.wallet_type == 0: if not invoiceData.bolt11: diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index bad707ff1..b669f8b74 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -9,7 +9,8 @@ import httpx from loguru import logger # mypy https://github.com/aaugustin/websockets/issues/940 -from websockets.client import connect +# from websockets.client import connect +from websockets import connect from websockets.exceptions import ( ConnectionClosed, ConnectionClosedError, From e96944a4fae70dbb8213cb36a63d6eebe1257b3f Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 10:24:40 +0200 Subject: [PATCH 09/63] fully fix decorators, thanks calle --- lnbits/decorators.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index 77fe32276..b74230ef8 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -171,9 +171,10 @@ async def get_key_type( if e.status_code == HTTPStatus.BAD_REQUEST: raise if e.status_code == HTTPStatus.UNAUTHORIZED: - return WalletTypeInfo(2, None) + return WalletTypeInfo(2, Wallet()) except: raise + return wallet async def require_admin_key( From 8dbf64a0d074657eed7517ca085181a3bf867657 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 10:34:01 +0200 Subject: [PATCH 10/63] ignore eclair websockets type --- lnbits/wallets/eclair.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index b669f8b74..b43d90c10 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -8,9 +8,10 @@ from typing import AsyncGenerator, Dict, Optional import httpx from loguru import logger +from websockets import connect # type: ignore +# TODO: https://github.com/lnbits/lnbits-legend/issues/764 # mypy https://github.com/aaugustin/websockets/issues/940 -# from websockets.client import connect -from websockets import connect + from websockets.exceptions import ( ConnectionClosed, ConnectionClosedError, From 56b4b0cf435ae796c641b867162328b83f55b65b Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 11:21:38 +0200 Subject: [PATCH 11/63] fix last views api bug, thanks calle --- lnbits/core/views/api.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 09eb4f906..6766ad8d1 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -387,19 +387,13 @@ async def api_payments_sse( 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 = None - try: - assert X_Api_Key is not None - # TODO: type above is Optional[str] how can that have .extra? - if X_Api_Key.extra: - logger.warning("No key") - except: - if X_Api_Key is not None: - wallet = await get_wallet_for_key(X_Api_Key) + wallet = await get_wallet_for_key(X_Api_Key) if type(X_Api_Key) == str else None + + # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order + # and get_standalone_payment otherwise just fetches the first one, causing unpredictable results payment = await get_standalone_payment( payment_hash, wallet_id=wallet.id if wallet else None - ) # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order - # and get_standalone_payment otherwise just fetches the first one, causing unpredictable results + ) if payment is None: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="Payment does not exist." From 64502c355eff38dd6ba43da4c7a09b055397bba0 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 11:33:37 +0200 Subject: [PATCH 12/63] black formating --- lnbits/core/views/api.py | 2 +- lnbits/core/views/generic.py | 16 ++++++++-------- lnbits/decorators.py | 12 ++++++------ lnbits/wallets/eclair.py | 3 ++- lnbits/wallets/fake.py | 4 +--- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 6766ad8d1..95e9dde3a 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet): ) async def api_payments_create( wallet: WalletTypeInfo = Depends(require_invoice_key), - invoiceData = Body(...), + invoiceData=Body(...), ): if invoiceData.out is True and wallet.wallet_type == 0: if not invoiceData.bolt11: diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 7ecedcf82..00d321af2 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -54,9 +54,9 @@ async def home(request: Request, lightning: str = None): ) async def extensions( request: Request, - user = Depends(check_user_exists), - enable = Query(None), - disable = Query(None), + user=Depends(check_user_exists), + enable=Query(None), + disable=Query(None), ): extension_to_enable = enable extension_to_disable = disable @@ -100,10 +100,10 @@ nothing: create everything
""", ) async def wallet( - request = Query(None), - nme = Query(None), - usr = Query(None), - wal = Query(None), + request=Query(None), + nme=Query(None), + usr=Query(None), + wal=Query(None), ): user_id = usr.hex if usr else None wallet_id = wal.hex if wal else None @@ -209,7 +209,7 @@ async def lnurl_full_withdraw_callback(request: Request): @core_html_routes.get("/deletewallet", response_class=RedirectResponse) -async def deletewallet(request: Request, wal = Query(...), usr = Query(...)): +async def deletewallet(request: Request, wal=Query(...), usr=Query(...)): user = await get_user(usr) assert user is not None user_wallet_ids = [u.id for u in user.wallets] diff --git a/lnbits/decorators.py b/lnbits/decorators.py index b74230ef8..ef689f8d0 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -123,8 +123,8 @@ api_key_query = APIKeyQuery( async def get_key_type( r: Request, - api_key_header = Security(api_key_header), - api_key_query = Security(api_key_query), + api_key_header=Security(api_key_header), + api_key_query=Security(api_key_query), ) -> WalletTypeInfo: # 0: admin # 1: invoice @@ -179,8 +179,8 @@ async def get_key_type( async def require_admin_key( r: Request, - api_key_header = Security(api_key_header), - api_key_query = Security(api_key_query), + api_key_header=Security(api_key_header), + api_key_query=Security(api_key_query), ): token = api_key_header if api_key_header else api_key_query @@ -198,8 +198,8 @@ async def require_admin_key( async def require_invoice_key( r: Request, - api_key_header = Security(api_key_header), - api_key_query = Security(api_key_query), + api_key_header=Security(api_key_header), + api_key_query=Security(api_key_query), ): token = api_key_header if api_key_header else api_key_query diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index b43d90c10..122895178 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -8,7 +8,8 @@ from typing import AsyncGenerator, Dict, Optional import httpx from loguru import logger -from websockets import connect # type: ignore +from websockets import connect # type: ignore + # TODO: https://github.com/lnbits/lnbits-legend/issues/764 # mypy https://github.com/aaugustin/websockets/issues/940 diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py index c2b2307ab..ba2956c58 100644 --- a/lnbits/wallets/fake.py +++ b/lnbits/wallets/fake.py @@ -81,9 +81,7 @@ class FakeWallet(Wallet): async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: invoice = decode(bolt11) # TODO: no data here? - data: Dict = { - "privkey": "missing" - } + data: Dict = {"privkey": "missing"} if ( invoice.checking_id is not None and invoice.checking_id[6:] == data["privkey"][:6] From 627959e30ba83342834f2f02b25c5398d7cde63f Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 11:52:49 +0200 Subject: [PATCH 13/63] add tests for api_payment --- tests/core/views/test_api.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py index 10e659aa4..1f858870b 100644 --- a/tests/core/views/test_api.py +++ b/tests/core/views/test_api.py @@ -140,3 +140,27 @@ async def test_decode_invoice(client, invoice): ) assert response.status_code < 300 assert response.json()["payment_hash"] == invoice["payment_hash"] + + +# check api_payment() internal function call (NOT API): payment status +@pytest.mark.asyncio +async def test_api_payment_without_key(invoice): + # check the payment status + response = await api_payment(invoice["payment_hash"]) + assert type(response) == dict + assert response["paid"] == True + # not key, that's why no "details" + assert "details" not in response + + +# check api_payment() internal function call (NOT API): payment status +@pytest.mark.asyncio +async def test_api_payment_with_key(invoice, inkey_headers_from): + # check the payment status + response = await api_payment( + invoice["payment_hash"], inkey_headers_from["X-Api-Key"] + ) + assert type(response) == dict + assert response["paid"] == True + # not key, that's why no "details" + assert "details" in response From 5f6dc757dcd08f2b248cbd5296616cc6c90502c0 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 12:00:53 +0200 Subject: [PATCH 14/63] types-mock==4.0.15 to requirements, needed for mypy tests --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 322712f5a..8ee1fc6c3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -43,6 +43,7 @@ sqlalchemy==1.3.23 sqlalchemy-aio==0.16.0 sse-starlette==0.6.2 starlette==0.14.2 +types-mock==4.0.15 types-protobuf==3.19.22 typing-extensions==3.10.0.2 uvicorn==0.15.0 From c186e53cef1a5331be2981bce6092f0b5bf5adb9 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 12:05:35 +0200 Subject: [PATCH 15/63] fixup requirements.txt and ad to Pipfile --- Pipfile | 12 +++++++----- requirements.txt | 2 -- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Pipfile b/Pipfile index 8ef241f12..60c79292f 100644 --- a/Pipfile +++ b/Pipfile @@ -35,9 +35,11 @@ pycryptodomex = "*" [dev-packages] black = "==20.8b1" -pytest = "*" -pytest-cov = "*" -mypy = "*" -pytest-asyncio = "*" -requests = "*" mock = "*" +mypy = "*" +pytest = "*" +pytest-asyncio = "*" +pytest-cov = "*" +requests = "*" +types-mock = "*" +types-protobuf = "*" diff --git a/requirements.txt b/requirements.txt index 8ee1fc6c3..3603ab036 100644 --- a/requirements.txt +++ b/requirements.txt @@ -43,8 +43,6 @@ sqlalchemy==1.3.23 sqlalchemy-aio==0.16.0 sse-starlette==0.6.2 starlette==0.14.2 -types-mock==4.0.15 -types-protobuf==3.19.22 typing-extensions==3.10.0.2 uvicorn==0.15.0 uvloop==0.16.0 From 2e0276bc605a1b704cbfeee39e3d5fd03881376e Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 12:17:34 +0200 Subject: [PATCH 16/63] make test run again, introduce mypy again --- lnbits/core/views/api.py | 2 +- tests/core/views/test_api.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 95e9dde3a..c81f623a7 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet): ) async def api_payments_create( wallet: WalletTypeInfo = Depends(require_invoice_key), - invoiceData=Body(...), + invoiceData: CreateInvoiceData = Body(...), ): if invoiceData.out is True and wallet.wallet_type == 0: if not invoiceData.bolt11: diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py index 1f858870b..01427cbac 100644 --- a/tests/core/views/test_api.py +++ b/tests/core/views/test_api.py @@ -1,6 +1,7 @@ import pytest import pytest_asyncio from lnbits.core.crud import get_wallet +from lnbits.core.views.api import api_payment from ...helpers import get_random_invoice_data From 06b1f71d3954d0dcb9de193a91845041a8de2ca2 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 12:21:39 +0200 Subject: [PATCH 17/63] fix up decorators for tests --- lnbits/decorators.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index ef689f8d0..adfbd6649 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -1,6 +1,6 @@ from http import HTTPStatus -from typing import Optional +from typing import Union from cerberus import Validator # type: ignore from fastapi import status @@ -102,9 +102,9 @@ class WalletAdminKeyChecker(KeyChecker): class WalletTypeInfo: wallet_type: int - wallet: Wallet + wallet: Union[Wallet, None] - def __init__(self, wallet_type: int, wallet: Wallet) -> None: + def __init__(self, wallet_type: int, wallet: Union[Wallet, None]) -> None: self.wallet_type = wallet_type self.wallet = wallet @@ -171,7 +171,7 @@ async def get_key_type( if e.status_code == HTTPStatus.BAD_REQUEST: raise if e.status_code == HTTPStatus.UNAUTHORIZED: - return WalletTypeInfo(2, Wallet()) + return WalletTypeInfo(2, None) except: raise return wallet From f4e3a3980e33ccca01de4502e8882993d106555c Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 13:12:55 +0200 Subject: [PATCH 18/63] fix isort check --- lnbits/core/views/api.py | 2 +- lnbits/decorators.py | 1 - lnbits/utils/exchange_rates.py | 2 +- lnbits/wallets/eclair.py | 4 +--- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index c81f623a7..d7be3e554 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -3,7 +3,7 @@ import hashlib import json from binascii import unhexlify from http import HTTPStatus -from typing import Dict, List, Optional, Union, Tuple +from typing import Dict, List, Optional, Tuple, Union from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx diff --git a/lnbits/decorators.py b/lnbits/decorators.py index adfbd6649..b62e456af 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -1,5 +1,4 @@ from http import HTTPStatus - from typing import Union from cerberus import Validator # type: ignore diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py index fbb4add22..2801146b5 100644 --- a/lnbits/utils/exchange_rates.py +++ b/lnbits/utils/exchange_rates.py @@ -1,5 +1,5 @@ import asyncio -from typing import Callable, NamedTuple, List +from typing import Callable, List, NamedTuple import httpx from loguru import logger diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index 122895178..1ba81385e 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -8,11 +8,9 @@ from typing import AsyncGenerator, Dict, Optional import httpx from loguru import logger -from websockets import connect # type: ignore - # TODO: https://github.com/lnbits/lnbits-legend/issues/764 # mypy https://github.com/aaugustin/websockets/issues/940 - +from websockets import connect # type: ignore from websockets.exceptions import ( ConnectionClosed, ConnectionClosedError, From 0784ae02b1fa940000ecab917869c16bf28055de Mon Sep 17 00:00:00 2001 From: Daniel Krahofer Date: Wed, 20 Jul 2022 13:15:06 +0200 Subject: [PATCH 19/63] Update tests/core/views/test_api.py Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> --- tests/core/views/test_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py index 01427cbac..d5eeeeae0 100644 --- a/tests/core/views/test_api.py +++ b/tests/core/views/test_api.py @@ -163,5 +163,4 @@ async def test_api_payment_with_key(invoice, inkey_headers_from): ) assert type(response) == dict assert response["paid"] == True - # not key, that's why no "details" assert "details" in response From 613c5b466727ce77b1f81c8102f674dfe6f39536 Mon Sep 17 00:00:00 2001 From: Daniel Krahofer Date: Wed, 20 Jul 2022 13:15:18 +0200 Subject: [PATCH 20/63] Update tests/core/views/test_api.py Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> --- tests/core/views/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py index d5eeeeae0..61a71d31f 100644 --- a/tests/core/views/test_api.py +++ b/tests/core/views/test_api.py @@ -150,7 +150,7 @@ async def test_api_payment_without_key(invoice): response = await api_payment(invoice["payment_hash"]) assert type(response) == dict assert response["paid"] == True - # not key, that's why no "details" + # no key, that's why no "details" assert "details" not in response From 04df2129b0fd26ec2bb3e81ce5e6bb82985cee50 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 13:21:42 +0200 Subject: [PATCH 21/63] add missing Pipfile.lock --- Pipfile.lock | 168 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 132 insertions(+), 36 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 6a89abb32..2e6d3a141 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "81bd288eea338c3bf1b70b8d30c1185b84c13a25a595bcddd77f74f7bc090032" + "sha256": "35eac2d1e375057cb654888c4414ec0bd479a0df1dc6b51bf5b72ba78d52ea36" }, "pipfile-spec": 6, "requires": { @@ -29,6 +29,7 @@ "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b", "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be" ], + "markers": "python_full_version >= '3.6.2'", "version": "==3.6.1" }, "asyncio": { @@ -46,6 +47,7 @@ "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==21.4.0" }, "bech32": { @@ -53,6 +55,7 @@ "sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899", "sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981" ], + "markers": "python_version >= '3.5'", "version": "==1.2.0" }, "bitstring": { @@ -76,6 +79,7 @@ "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d", "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412" ], + "markers": "python_version >= '3.6'", "version": "==2022.6.15" }, "cffi": { @@ -152,6 +156,7 @@ "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" ], + "markers": "python_version >= '3.7'", "version": "==8.1.3" }, "ecdsa": { @@ -179,17 +184,18 @@ }, "fastapi": { "hashes": [ - "sha256:15fcabd5c78c266fa7ae7d8de9b384bfc2375ee0503463a6febbe3bab69d6f65", - "sha256:3233d4a789ba018578658e2af1a4bb5e38bdd122ff722b313666a9b2c6786a83" + "sha256:cf0ff6db25b91d321050c4112baab0908c90f19b40bf257f9591d2f9780d1f22", + "sha256:d337563424ceada23857f73d5abe8dae0c28e4cccb53b2af06e78b7bb4a1c7d7" ], "index": "pypi", - "version": "==0.78.0" + "version": "==0.79.0" }, "h11": { "hashes": [ "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6", "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042" ], + "markers": "python_version >= '3.6'", "version": "==0.12.0" }, "httpcore": { @@ -197,6 +203,7 @@ "sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6", "sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b" ], + "markers": "python_version >= '3.7'", "version": "==0.15.0" }, "httptools": { @@ -251,8 +258,17 @@ "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" ], + "markers": "python_version >= '3.5'", "version": "==3.3" }, + "importlib-metadata": { + "hashes": [ + "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670", + "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23" + ], + "markers": "python_version < '3.8'", + "version": "==4.12.0" + }, "jinja2": { "hashes": [ "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4", @@ -320,6 +336,7 @@ "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" ], + "markers": "python_version >= '3.7'", "version": "==2.1.1" }, "marshmallow": { @@ -327,6 +344,7 @@ "sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb", "sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7" ], + "markers": "python_version >= '3.7'", "version": "==3.17.0" }, "outcome": { @@ -334,6 +352,7 @@ "sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672", "sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5" ], + "markers": "python_version >= '3.7'", "version": "==1.2.0" }, "packaging": { @@ -341,6 +360,7 @@ "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" ], + "markers": "python_version >= '3.6'", "version": "==21.3" }, "psycopg2-binary": { @@ -486,6 +506,7 @@ "sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e", "sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6" ], + "markers": "python_full_version >= '3.6.1'", "version": "==1.9.1" }, "pyngrok": { @@ -500,14 +521,16 @@ "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" ], + "markers": "python_full_version >= '3.6.8'", "version": "==3.0.9" }, "pypng": { "hashes": [ - "sha256:76f8a1539ec56451da7ab7121f12a361969fe0f2d48d703d198ce2a99d6c5afd" + "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c", + "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1" ], "index": "pypi", - "version": "==0.0.21" + "version": "==0.20220715.0" }, "pyqrcode": { "hashes": [ @@ -574,9 +597,13 @@ "sha256:026c0de2ee8385d1255b9c2426cd4f03fe9177ac94c09979bc601946c8493aa0", "sha256:99142650756ef1998ce0661568f54a47dac8c638fb27e3816c02536575dbba8c" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.6.0.post0" }, "rfc3986": { + "extras": [ + "idna2008" + ], "hashes": [ "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835", "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97" @@ -612,6 +639,14 @@ "index": "pypi", "version": "==0.14.0" }, + "setuptools": { + "hashes": [ + "sha256:0d33c374d41c7863419fc8f6c10bfe25b7b498aa34164d135c622e52580c6b16", + "sha256:c04b44a57a6265fe34a4a444e965884716d34bae963119a76353434d6f18e450" + ], + "markers": "python_version >= '3.7'", + "version": "==63.2.0" + }, "shortuuid": { "hashes": [ "sha256:459f12fa1acc34ff213b1371467c0325169645a31ed989e268872339af7563d5", @@ -625,6 +660,7 @@ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.16.0" }, "sniffio": { @@ -632,6 +668,7 @@ "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663", "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de" ], + "markers": "python_version >= '3.5'", "version": "==1.2.0" }, "sqlalchemy": { @@ -699,6 +736,7 @@ "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf", "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7" ], + "markers": "python_version >= '3.6'", "version": "==0.19.1" }, "typing-extensions": { @@ -707,10 +745,12 @@ "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6" ], "index": "pypi", - "markers": "python_version < '3.10'", "version": "==4.3.0" }, "uvicorn": { + "extras": [ + "standard" + ], "hashes": [ "sha256:c19a057deb1c5bb060946e2e5c262fc01590c6529c0af2c3d9ce941e89bc30e0", "sha256:cade07c403c397f9fe275492a48c1b869efd175d5d8a692df649e6e7e2ed8f4e" @@ -808,6 +848,14 @@ "sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4" ], "version": "==10.3" + }, + "zipp": { + "hashes": [ + "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2", + "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009" + ], + "markers": "python_version >= '3.7'", + "version": "==3.8.1" } }, "develop": { @@ -823,6 +871,7 @@ "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==21.4.0" }, "black": { @@ -837,6 +886,7 @@ "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d", "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412" ], + "markers": "python_version >= '3.6'", "version": "==2022.6.15" }, "charset-normalizer": { @@ -844,6 +894,7 @@ "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5", "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413" ], + "markers": "python_version >= '3.6'", "version": "==2.1.0" }, "click": { @@ -851,9 +902,13 @@ "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" ], + "markers": "python_version >= '3.7'", "version": "==8.1.3" }, "coverage": { + "extras": [ + "toml" + ], "hashes": [ "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32", "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7", @@ -897,6 +952,7 @@ "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39", "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452" ], + "markers": "python_version >= '3.7'", "version": "==6.4.2" }, "idna": { @@ -904,8 +960,17 @@ "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" ], + "markers": "python_version >= '3.5'", "version": "==3.3" }, + "importlib-metadata": { + "hashes": [ + "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670", + "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23" + ], + "markers": "python_version < '3.8'", + "version": "==4.12.0" + }, "iniconfig": { "hashes": [ "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", @@ -923,32 +988,32 @@ }, "mypy": { "hashes": [ - "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5", - "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66", - "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e", - "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56", - "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e", - "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d", - "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813", - "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932", - "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569", - "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b", - "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0", - "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648", - "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6", - "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950", - "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15", - "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723", - "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a", - "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3", - "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6", - "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24", - "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b", - "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d", - "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492" + "sha256:02ef476f6dcb86e6f502ae39a16b93285fef97e7f1ff22932b657d1ef1f28655", + "sha256:0d054ef16b071149917085f51f89555a576e2618d5d9dd70bd6eea6410af3ac9", + "sha256:19830b7dba7d5356d3e26e2427a2ec91c994cd92d983142cbd025ebe81d69cf3", + "sha256:1f7656b69974a6933e987ee8ffb951d836272d6c0f81d727f1d0e2696074d9e6", + "sha256:23488a14a83bca6e54402c2e6435467a4138785df93ec85aeff64c6170077fb0", + "sha256:23c7ff43fff4b0df93a186581885c8512bc50fc4d4910e0f838e35d6bb6b5e58", + "sha256:25c5750ba5609a0c7550b73a33deb314ecfb559c350bb050b655505e8aed4103", + "sha256:2ad53cf9c3adc43cf3bea0a7d01a2f2e86db9fe7596dfecb4496a5dda63cbb09", + "sha256:3fa7a477b9900be9b7dd4bab30a12759e5abe9586574ceb944bc29cddf8f0417", + "sha256:40b0f21484238269ae6a57200c807d80debc6459d444c0489a102d7c6a75fa56", + "sha256:4b21e5b1a70dfb972490035128f305c39bc4bc253f34e96a4adf9127cf943eb2", + "sha256:5a361d92635ad4ada1b1b2d3630fc2f53f2127d51cf2def9db83cba32e47c856", + "sha256:77a514ea15d3007d33a9e2157b0ba9c267496acf12a7f2b9b9f8446337aac5b0", + "sha256:855048b6feb6dfe09d3353466004490b1872887150c5bb5caad7838b57328cc8", + "sha256:9796a2ba7b4b538649caa5cecd398d873f4022ed2333ffde58eaf604c4d2cb27", + "sha256:98e02d56ebe93981c41211c05adb630d1d26c14195d04d95e49cd97dbc046dc5", + "sha256:b793b899f7cf563b1e7044a5c97361196b938e92f0a4343a5d27966a53d2ec71", + "sha256:d1ea5d12c8e2d266b5fb8c7a5d2e9c0219fedfeb493b7ed60cd350322384ac27", + "sha256:d2022bfadb7a5c2ef410d6a7c9763188afdb7f3533f22a0a32be10d571ee4bbe", + "sha256:d3348e7eb2eea2472db611486846742d5d52d1290576de99d59edeb7cd4a42ca", + "sha256:d744f72eb39f69312bc6c2abf8ff6656973120e2eb3f3ec4f758ed47e414a4bf", + "sha256:ef943c72a786b0f8d90fd76e9b39ce81fb7171172daf84bf43eaf937e9f220a9", + "sha256:f2899a3cbd394da157194f913a931edfd4be5f274a88041c9dc2d9cdcb1c315c" ], "index": "pypi", - "version": "==0.961" + "version": "==0.971" }, "mypy-extensions": { "hashes": [ @@ -962,6 +1027,7 @@ "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" ], + "markers": "python_version >= '3.6'", "version": "==21.3" }, "pathspec": { @@ -976,6 +1042,7 @@ "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" ], + "markers": "python_version >= '3.6'", "version": "==1.0.0" }, "py": { @@ -983,6 +1050,7 @@ "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==1.11.0" }, "pyparsing": { @@ -990,6 +1058,7 @@ "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" ], + "markers": "python_full_version >= '3.6.8'", "version": "==3.0.9" }, "pytest": { @@ -1002,12 +1071,11 @@ }, "pytest-asyncio": { "hashes": [ - "sha256:16cf40bdf2b4fb7fc8e4b82bd05ce3fbcd454cbf7b92afc445fe299dabb88213", - "sha256:7659bdb0a9eb9c6e3ef992eef11a2b3e69697800ad02fb06374a210d85b29f91", - "sha256:8fafa6c52161addfd41ee7ab35f11836c5a16ec208f93ee388f752bea3493a84" + "sha256:7a97e37cfe1ed296e2e84941384bdd37c376453912d397ed39293e0916f521fa", + "sha256:ac4ebf3b6207259750bc32f4c1d8fcd7e79739edbc67ad0c58dd150b1d072fed" ], "index": "pypi", - "version": "==0.18.3" + "version": "==0.19.0" }, "pytest-cov": { "hashes": [ @@ -1094,6 +1162,7 @@ "sha256:f8a2fd2f62a77536e4e3193303bec380df40d99e253b1c8f9b6eafa07eaeff67", "sha256:fbdf4fc6adf38fab1091c579ece3fe9f493bd0f1cfc3d2c76d2e52461ca4f8a9" ], + "markers": "python_version >= '3.6'", "version": "==2022.7.9" }, "requests": { @@ -1109,6 +1178,7 @@ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.10.2" }, "tomli": { @@ -1116,6 +1186,7 @@ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" ], + "markers": "python_version < '3.11'", "version": "==2.0.1" }, "typed-ast": { @@ -1145,15 +1216,31 @@ "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3", "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66" ], + "markers": "python_version >= '3.6'", "version": "==1.5.4" }, + "types-mock": { + "hashes": [ + "sha256:4535fbb3912b88a247d43cdb41db0c8b2e187138986f6f01a989717e56105848", + "sha256:a849bc2d966063f4946013bf404822ee2b96f77a8dccda4174b70ab61c5293fe" + ], + "index": "pypi", + "version": "==4.0.15" + }, + "types-protobuf": { + "hashes": [ + "sha256:d291388678af91bb045fafa864f142dc4ac22f5d4cdca097c7d8d8a32fa9b3ab", + "sha256:d2b26861b0cb46a3c8669b0df507b7ef72e487da66d61f9f3576aa76ce028a83" + ], + "index": "pypi", + "version": "==3.19.22" + }, "typing-extensions": { "hashes": [ "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02", "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6" ], "index": "pypi", - "markers": "python_version < '3.10'", "version": "==4.3.0" }, "urllib3": { @@ -1161,7 +1248,16 @@ "sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec", "sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", "version": "==1.26.10" + }, + "zipp": { + "hashes": [ + "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2", + "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009" + ], + "markers": "python_version >= '3.7'", + "version": "==3.8.1" } } } From 76fdce4328a946fa7aa78be27a870adbf5846f4d Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 13:23:50 +0200 Subject: [PATCH 22/63] enable mypy checks --- .github/workflows/mypy.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 4d6c6d4da..bf90a8e31 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -5,7 +5,6 @@ on: [push, pull_request] jobs: check: runs-on: ubuntu-latest - if: ${{ 'false' == 'true' }} # skip mypy for now steps: - uses: actions/checkout@v1 - uses: jpetrucciani/mypy-check@master From 5aa30c843c6604a9fb013f99094b8c454d650b1a Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 13:41:13 +0200 Subject: [PATCH 23/63] added FIXME tags and fix WalletTypeInfo --- lnbits/core/services.py | 4 ++++ lnbits/core/views/api.py | 23 +++++++++++++++++++++++ lnbits/decorators.py | 2 ++ 3 files changed, 29 insertions(+) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index f4f962504..2416ed315 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -269,6 +269,10 @@ async def perform_lnurlauth( cb = urlparse(callback) k1 = unhexlify(parse_qs(cb.query)["k1"][0]) + + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None + key = wallet.wallet.lnurlauth_key(cb.netloc) def int_to_bytes_suitable_der(x: int) -> bytes: diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index d7be3e554..a3a59a5f0 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -56,12 +56,16 @@ from ..tasks import api_invoice_listeners @core_app.get("/api/v1/wallet") async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)): if wallet.wallet_type == 0: + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return { "id": wallet.wallet.id, "name": wallet.wallet.name, "balance": wallet.wallet.balance_msat, } else: + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return {"name": wallet.wallet.name, "balance": wallet.wallet.balance_msat} @@ -69,6 +73,9 @@ async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)): async def api_update_balance( amount: int, wallet: WalletTypeInfo = Depends(get_key_type) ): + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None + if wallet.wallet.user not in LNBITS_ADMIN_USERS: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user" @@ -98,6 +105,9 @@ async def api_update_balance( async def api_update_wallet( new_name: str, wallet: WalletTypeInfo = Depends(require_admin_key) ): + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None + await update_wallet(wallet.wallet.id, new_name) return { "id": wallet.wallet.id, @@ -112,6 +122,9 @@ async def api_payments( offset: Optional[int] = None, wallet: WalletTypeInfo = Depends(get_key_type), ): + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None + pendingPayments = await get_payments( wallet_id=wallet.wallet.id, pending=True, @@ -256,11 +269,15 @@ async def api_payments_create( status_code=HTTPStatus.BAD_REQUEST, detail="BOLT11 string is invalid or not given", ) + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return await api_payments_pay_invoice( invoiceData.bolt11, wallet.wallet ) # admin key elif not invoiceData.out: # invoice key + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return await api_payments_create_invoice(invoiceData, wallet.wallet) else: raise HTTPException( @@ -325,6 +342,8 @@ async def api_payments_pay_lnurl( if data.comment: extra["comment"] = data.comment assert data.description is not None, "description is required" + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None payment_hash = await pay_invoice( wallet_id=wallet.wallet.id, payment_request=params["pr"], @@ -378,6 +397,8 @@ async def subscribe(request: Request, wallet: Wallet): async def api_payments_sse( request: Request, wallet: WalletTypeInfo = Depends(get_key_type) ): + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return EventSourceResponse( subscribe(request, wallet.wallet), ping=20, media_type="text/event-stream" ) @@ -456,6 +477,8 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type params.update(kind="auth") params.update(callback=url) # with k1 already in it + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None lnurlauth_key = wallet.wallet.lnurlauth_key(domain) params.update(pubkey=lnurlauth_key.verifying_key.to_string("compressed").hex()) else: diff --git a/lnbits/decorators.py b/lnbits/decorators.py index b62e456af..9f51ce646 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -159,6 +159,8 @@ async def get_key_type( invoice_checker = WalletInvoiceKeyChecker(api_key=token) await invoice_checker.__call__(r) wallet = WalletTypeInfo(1, invoice_checker.wallet) + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and ( LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS ): From 2891a421f196e7cf4db41c70e2cae460ac02157f Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 19 Jul 2022 13:06:19 +0200 Subject: [PATCH 24/63] fix up mypy.ini, and properly ignore grpc and extension --- mypy.ini | 7 ++++--- requirements.txt | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mypy.ini b/mypy.ini index 735f89e05..e5a974b51 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,7 +1,8 @@ [mypy] ignore_missing_imports = True -exclude = lnbits/wallets/lnd_grpc_files/ -exclude = lnbits/extensions/ - +exclude = (?x)( + ^lnbits/extensions. + | ^lnbits/wallets/lnd_grpc_files. + ) [mypy-lnbits.wallets.lnd_grpc_files.*] follow_imports = skip diff --git a/requirements.txt b/requirements.txt index f8ccf47cb..512011ec2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -44,6 +44,7 @@ sqlalchemy==1.3.23 sqlalchemy-aio==0.16.0 sse-starlette==0.6.2 starlette==0.14.2 +types-protobuf==3.19.22 typing-extensions==3.10.0.2 uvicorn==0.15.0 uvloop==0.16.0 From 895770e26ace770a2822ed8dddba29557dea7c5f Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 19 Jul 2022 13:36:44 +0200 Subject: [PATCH 25/63] remove commented out and unused code in app object, it is now used in lnbits/__main__.py directly, fixes mypy error --- lnbits/app.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index a7c8fdaf3..eaf8a9db4 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -17,7 +17,6 @@ from loguru import logger import lnbits.settings from lnbits.core.tasks import register_task_listeners -from .commands import db_migrate, handle_assets from .core import core_app from .core.views.generic import core_html_routes from .helpers import ( @@ -84,7 +83,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI: check_funding_source(app) register_assets(app) register_routes(app) - # register_commands(app) register_async_tasks(app) register_exception_handlers(app) @@ -137,12 +135,6 @@ def register_routes(app: FastAPI) -> None: ) -def register_commands(app: FastAPI): - """Register Click commands.""" - app.cli.add_command(db_migrate) - app.cli.add_command(handle_assets) - - def register_assets(app: FastAPI): """Serve each vendored asset separately or a bundle.""" From 808bd36e23765ca502b37c720349bdfe1da039bc Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 19 Jul 2022 18:51:35 +0200 Subject: [PATCH 26/63] some more mypy fixes --- lnbits/core/crud.py | 2 +- lnbits/core/models.py | 2 ++ lnbits/core/views/api.py | 15 +++++++++------ lnbits/core/views/generic.py | 6 +++++- lnbits/helpers.py | 2 +- lnbits/tasks.py | 2 +- lnbits/utils/exchange_rates.py | 8 ++++---- lnbits/wallets/lnpay.py | 2 +- lnbits/wallets/opennode.py | 2 +- 9 files changed, 25 insertions(+), 16 deletions(-) diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index 770e2906a..db802d7bb 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -113,7 +113,7 @@ async def create_wallet( async def update_wallet( wallet_id: str, new_name: str, conn: Optional[Connection] = None ) -> Optional[Wallet]: - await (conn or db).execute( + return await (conn or db).execute( """ UPDATE wallets SET name = ? diff --git a/lnbits/core/models.py b/lnbits/core/models.py index ab73b7020..0f7eba737 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -106,6 +106,8 @@ class Payment(BaseModel): @property def tag(self) -> Optional[str]: + if self.extra is None: + return "" return self.extra.get("tag") @property diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 9fee6063d..bd15ee8d6 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -3,7 +3,7 @@ import hashlib import json from binascii import unhexlify from http import HTTPStatus -from typing import Dict, List, Optional, Union +from typing import Dict, List, Optional, Union, Tuple from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx @@ -185,7 +185,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet): assert ( data.lnurl_balance_check is not None ), "lnurl_balance_check is required" - save_balance_check(wallet.id, data.lnurl_balance_check) + await save_balance_check(wallet.id, data.lnurl_balance_check) async with httpx.AsyncClient() as client: try: @@ -291,7 +291,7 @@ async def api_payments_pay_lnurl( timeout=40, ) if r.is_error: - raise httpx.ConnectError + raise httpx.ConnectError("LNURL Callback Connection Error") except (httpx.ConnectError, httpx.RequestError): raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, @@ -348,7 +348,7 @@ async def subscribe(request: Request, wallet: Wallet): logger.debug("adding sse listener", payment_queue) api_invoice_listeners.append(payment_queue) - send_queue: asyncio.Queue[tuple[str, Payment]] = asyncio.Queue(0) + send_queue: asyncio.Queue[Tuple[str, Payment]] = asyncio.Queue(0) async def payment_received() -> None: while True: @@ -389,10 +389,13 @@ async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)): # If a valid key is given, we also return the field "details", otherwise not wallet = None try: + assert X_Api_Key is not None + # TODO: type above is Optional[str] how can that have .extra? if X_Api_Key.extra: logger.warning("No key") except: - wallet = await get_wallet_for_key(X_Api_Key) + if X_Api_Key is not None: + wallet = await get_wallet_for_key(X_Api_Key) payment = await get_standalone_payment( payment_hash, wallet_id=wallet.id if wallet else None ) # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order @@ -606,7 +609,7 @@ class ConversionData(BaseModel): async def api_fiat_as_sats(data: ConversionData): output = {} if data.from_ == "sat": - output["sats"] = int(data.amount) + output["sats"] = data.amount output["BTC"] = data.amount / 100000000 for currency in data.to.split(","): output[currency.strip().upper()] = await satoshis_amount_as_fiat( diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 44666ce16..7ef306dc8 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -121,9 +121,11 @@ async def wallet( if not user_id: user = await get_user((await create_account()).id) + assert user is not None logger.info(f"Create user {user.id}") else: user = await get_user(user_id) + assert user is not None if not user: return template_renderer().TemplateResponse( "error.html", {"request": request, "err": "User does not exist."} @@ -218,6 +220,7 @@ async def lnurl_full_withdraw_callback(request: Request): @core_html_routes.get("/deletewallet", response_class=RedirectResponse) async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): user = await get_user(usr) + assert user is not None user_wallet_ids = [u.id for u in user.wallets] if wal not in user_wallet_ids: @@ -242,7 +245,7 @@ async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query async def lnurl_balance_notify(request: Request, service: str): bc = await get_balance_check(request.query_params.get("wal"), service) if bc: - redeem_lnurl_withdraw(bc.wallet, bc.url) + await redeem_lnurl_withdraw(bc.wallet, bc.url) @core_html_routes.get( @@ -252,6 +255,7 @@ async def lnurlwallet(request: Request): async with db.connect() as conn: account = await create_account(conn=conn) user = await get_user(account.id, conn=conn) + assert user is not None wallet = await create_wallet(user_id=user.id, conn=conn) asyncio.create_task( diff --git a/lnbits/helpers.py b/lnbits/helpers.py index 71b3dd691..e97fc7bbb 100644 --- a/lnbits/helpers.py +++ b/lnbits/helpers.py @@ -34,7 +34,7 @@ class ExtensionManager: @property def extensions(self) -> List[Extension]: - output = [] + output: List[Extension] = [] if "all" in self._disabled: return output diff --git a/lnbits/tasks.py b/lnbits/tasks.py index 86863f98f..f4d0a928d 100644 --- a/lnbits/tasks.py +++ b/lnbits/tasks.py @@ -66,7 +66,7 @@ async def webhook_handler(): raise HTTPException(status_code=HTTPStatus.NO_CONTENT) -internal_invoice_queue = asyncio.Queue(0) +internal_invoice_queue: asyncio.Queue = asyncio.Queue(0) async def internal_invoice_listener(): diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py index 0432b364a..fbb4add22 100644 --- a/lnbits/utils/exchange_rates.py +++ b/lnbits/utils/exchange_rates.py @@ -1,5 +1,5 @@ import asyncio -from typing import Callable, NamedTuple +from typing import Callable, NamedTuple, List import httpx from loguru import logger @@ -227,10 +227,10 @@ async def btc_price(currency: str) -> float: "TO": currency.upper(), "to": currency.lower(), } - rates = [] - tasks = [] + rates: List[float] = [] + tasks: List[asyncio.Task] = [] - send_channel = asyncio.Queue() + send_channel: asyncio.Queue = asyncio.Queue() async def controller(): failures = 0 diff --git a/lnbits/wallets/lnpay.py b/lnbits/wallets/lnpay.py index 807d72538..2ff1afa93 100644 --- a/lnbits/wallets/lnpay.py +++ b/lnbits/wallets/lnpay.py @@ -119,7 +119,7 @@ class LNPayWallet(Wallet): return PaymentStatus(statuses[r.json()["settled"]]) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: - self.queue = asyncio.Queue(0) + self.queue: asyncio.Queue = asyncio.Queue(0) while True: value = await self.queue.get() yield value diff --git a/lnbits/wallets/opennode.py b/lnbits/wallets/opennode.py index 6d3fb02c9..9cd05ebdd 100644 --- a/lnbits/wallets/opennode.py +++ b/lnbits/wallets/opennode.py @@ -127,7 +127,7 @@ class OpenNodeWallet(Wallet): return PaymentStatus(statuses[r.json()["data"]["status"]]) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: - self.queue = asyncio.Queue(0) + self.queue: asyncio.Queue = asyncio.Queue(0) while True: value = await self.queue.get() yield value From 8e9b3190e4d1ff49131e0f07b4d6ffe811c99221 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 19 Jul 2022 19:20:20 +0200 Subject: [PATCH 27/63] mypy fakewallet, macaroon, bolt11 invoice --- lnbits/bolt11.py | 1 + lnbits/wallets/fake.py | 10 +++++++--- lnbits/wallets/macaroon/macaroon.py | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lnbits/bolt11.py b/lnbits/bolt11.py index cc8415852..cc8c60401 100644 --- a/lnbits/bolt11.py +++ b/lnbits/bolt11.py @@ -23,6 +23,7 @@ class Route(NamedTuple): class Invoice(object): payment_hash: str + checking_id: Optional[str] = None amount_msat: int = 0 description: Optional[str] = None description_hash: Optional[str] = None diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py index 3859d33c5..c2b2307ab 100644 --- a/lnbits/wallets/fake.py +++ b/lnbits/wallets/fake.py @@ -28,7 +28,7 @@ class FakeWallet(Wallet): logger.info( "FakeWallet funding source is for using LNbits as a centralised, stand-alone payment system with brrrrrr." ) - return StatusResponse(None, float("inf")) + return StatusResponse(None, 1000000000) async def create_invoice( self, @@ -80,8 +80,12 @@ class FakeWallet(Wallet): async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: invoice = decode(bolt11) + # TODO: no data here? + data: Dict = { + "privkey": "missing" + } if ( - hasattr(invoice, "checking_id") + invoice.checking_id is not None and invoice.checking_id[6:] == data["privkey"][:6] ): return PaymentResponse(True, invoice.payment_hash, 0) @@ -97,7 +101,7 @@ class FakeWallet(Wallet): return PaymentStatus(None) async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: - self.queue = asyncio.Queue(0) + self.queue: asyncio.Queue = asyncio.Queue(0) while True: value = await self.queue.get() yield value diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py index 2183dacb8..aa00cd3e6 100644 --- a/lnbits/wallets/macaroon/macaroon.py +++ b/lnbits/wallets/macaroon/macaroon.py @@ -73,7 +73,7 @@ class AESCipher(object): final_key += key return final_key[:output] - def decrypt(self, encrypted: str) -> str: + def decrypt(self, encrypted) -> str: """Decrypts a string using AES-256-CBC.""" passphrase = self.passphrase encrypted = base64.b64decode(encrypted) From 7ad9ad799e782acb7f60695ea4767e095c36ebb8 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 08:43:10 +0200 Subject: [PATCH 28/63] assert Optional[str] --- lnbits/core/tasks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py index 5fea769d4..8234b5fc4 100644 --- a/lnbits/core/tasks.py +++ b/lnbits/core/tasks.py @@ -55,6 +55,7 @@ async def dispatch_webhook(payment: Payment): data = payment.dict() try: logger.debug("sending webhook", payment.webhook) + assert payment.webhook is not None r = await client.post(payment.webhook, json=data, timeout=40) await mark_webhook_sent(payment, r.status_code) except (httpx.ConnectError, httpx.RequestError): From 56560fca02c335534d0974b133814c4811ae88d8 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 09:36:13 +0200 Subject: [PATCH 29/63] mypy fixes for generic.py, decurators.py, eclair.py --- lnbits/core/views/generic.py | 16 +++++++-------- lnbits/decorators.py | 40 ++++++++++++++++++++---------------- lnbits/wallets/eclair.py | 4 +++- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 7ef306dc8..5f44131fc 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -55,9 +55,9 @@ async def home(request: Request, lightning: str = None): ) async def extensions( request: Request, - user: User = Depends(check_user_exists), - enable: str = Query(None), - disable: str = Query(None), + user = Depends(check_user_exists), + enable = Query(None), + disable = Query(None), ): extension_to_enable = enable extension_to_disable = disable @@ -109,10 +109,10 @@ nothing: create everything
""", ) async def wallet( - request: Request = Query(None), - nme: Optional[str] = Query(None), - usr: Optional[UUID4] = Query(None), - wal: Optional[UUID4] = Query(None), + request = Query(None), + nme = Query(None), + usr = Query(None), + wal = Query(None), ): user_id = usr.hex if usr else None wallet_id = wal.hex if wal else None @@ -218,7 +218,7 @@ async def lnurl_full_withdraw_callback(request: Request): @core_html_routes.get("/deletewallet", response_class=RedirectResponse) -async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): +async def deletewallet(request: Request, wal = Query(...), usr = Query(...)): user = await get_user(usr) assert user is not None user_wallet_ids = [u.id for u in user.wallets] diff --git a/lnbits/decorators.py b/lnbits/decorators.py index e65b9041a..77fe32276 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -1,5 +1,7 @@ from http import HTTPStatus +from typing import Optional + from cerberus import Validator # type: ignore from fastapi import status from fastapi.exceptions import HTTPException @@ -29,20 +31,20 @@ class KeyChecker(SecurityBase): self._key_type = "invoice" self._api_key = api_key if api_key: - self.model: APIKey = APIKey( + key = APIKey( **{"in": APIKeyIn.query}, name="X-API-KEY", description="Wallet API Key - QUERY", ) else: - self.model: APIKey = APIKey( + key = APIKey( **{"in": APIKeyIn.header}, name="X-API-KEY", description="Wallet API Key - HEADER", ) - self.wallet = None + self.model: APIKey = key - async def __call__(self, request: Request) -> Wallet: + async def __call__(self, request: Request): try: key_value = ( self._api_key @@ -52,12 +54,13 @@ class KeyChecker(SecurityBase): # FIXME: Find another way to validate the key. A fetch from DB should be avoided here. # Also, we should not return the wallet here - thats silly. # Possibly store it in a Redis DB - self.wallet = await get_wallet_for_key(key_value, self._key_type) - if not self.wallet: + wallet = await get_wallet_for_key(key_value, self._key_type) + if not wallet: raise HTTPException( status_code=HTTPStatus.UNAUTHORIZED, detail="Invalid key or expired key.", ) + self.wallet = wallet except KeyError: raise HTTPException( @@ -120,8 +123,8 @@ api_key_query = APIKeyQuery( async def get_key_type( r: Request, - api_key_header: str = Security(api_key_header), - api_key_query: str = Security(api_key_query), + api_key_header = Security(api_key_header), + api_key_query = Security(api_key_query), ) -> WalletTypeInfo: # 0: admin # 1: invoice @@ -134,9 +137,10 @@ async def get_key_type( token = api_key_header if api_key_header else api_key_query try: - checker = WalletAdminKeyChecker(api_key=token) - await checker.__call__(r) - wallet = WalletTypeInfo(0, checker.wallet) + admin_checker = WalletAdminKeyChecker(api_key=token) + await admin_checker.__call__(r) + wallet = WalletTypeInfo(0, admin_checker.wallet) + assert wallet.wallet is not None if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and ( LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS ): @@ -153,9 +157,9 @@ async def get_key_type( raise try: - checker = WalletInvoiceKeyChecker(api_key=token) - await checker.__call__(r) - wallet = WalletTypeInfo(1, checker.wallet) + invoice_checker = WalletInvoiceKeyChecker(api_key=token) + await invoice_checker.__call__(r) + wallet = WalletTypeInfo(1, invoice_checker.wallet) if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and ( LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS ): @@ -174,8 +178,8 @@ async def get_key_type( async def require_admin_key( r: Request, - api_key_header: str = Security(api_key_header), - api_key_query: str = Security(api_key_query), + api_key_header = Security(api_key_header), + api_key_query = Security(api_key_query), ): token = api_key_header if api_key_header else api_key_query @@ -193,8 +197,8 @@ async def require_admin_key( async def require_invoice_key( r: Request, - api_key_header: str = Security(api_key_header), - api_key_query: str = Security(api_key_query), + api_key_header = Security(api_key_header), + api_key_query = Security(api_key_query), ): token = api_key_header if api_key_header else api_key_query diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index 0ac3fd2a0..bad707ff1 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -7,7 +7,9 @@ from typing import AsyncGenerator, Dict, Optional import httpx from loguru import logger -from websockets import connect + +# mypy https://github.com/aaugustin/websockets/issues/940 +from websockets.client import connect from websockets.exceptions import ( ConnectionClosed, ConnectionClosedError, From 704b572349bf08c2fa07de7fc2daae753ee33c4d Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 09:45:08 +0200 Subject: [PATCH 30/63] fix PaymentKwargs class for mypy --- lnbits/core/services.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 0b565ebb9..f4f962504 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -109,18 +109,15 @@ async def pay_invoice( raise ValueError("Amount in invoice is too high.") # put all parameters that don't change here - PaymentKwargs = TypedDict( - "PaymentKwargs", - { - "wallet_id": str, - "payment_request": str, - "payment_hash": str, - "amount": int, - "memo": str, - "extra": Optional[Dict], - }, - ) - payment_kwargs: PaymentKwargs = dict( + class PaymentKwargs(TypedDict): + wallet_id: str + payment_request: str + payment_hash: str + amount: int + memo: str + extra: Optional[Dict] + + payment_kwargs: PaymentKwargs = PaymentKwargs( wallet_id=wallet_id, payment_request=payment_request, payment_hash=invoice.payment_hash, From 25d19244be6a0eb05f25ada658b63a15a34663df Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 10:05:30 +0200 Subject: [PATCH 31/63] mypy fixes, api, eclair --- lnbits/core/views/api.py | 4 ++-- lnbits/wallets/eclair.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index bd15ee8d6..09eb4f906 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -181,7 +181,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet): lnurl_response: Union[None, bool, str] = None if data.lnurl_callback: - if "lnurl_balance_check" in data: + if hasattr(data, "lnurl_balance_check"): assert ( data.lnurl_balance_check is not None ), "lnurl_balance_check is required" @@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet): ) async def api_payments_create( wallet: WalletTypeInfo = Depends(require_invoice_key), - invoiceData: CreateInvoiceData = Body(...), + invoiceData = Body(...), ): if invoiceData.out is True and wallet.wallet_type == 0: if not invoiceData.bolt11: diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index bad707ff1..b669f8b74 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -9,7 +9,8 @@ import httpx from loguru import logger # mypy https://github.com/aaugustin/websockets/issues/940 -from websockets.client import connect +# from websockets.client import connect +from websockets import connect from websockets.exceptions import ( ConnectionClosed, ConnectionClosedError, From f677777f07b2439605230bbb7ac0952f8546c4e9 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 10:24:40 +0200 Subject: [PATCH 32/63] fully fix decorators, thanks calle --- lnbits/decorators.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index 77fe32276..b74230ef8 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -171,9 +171,10 @@ async def get_key_type( if e.status_code == HTTPStatus.BAD_REQUEST: raise if e.status_code == HTTPStatus.UNAUTHORIZED: - return WalletTypeInfo(2, None) + return WalletTypeInfo(2, Wallet()) except: raise + return wallet async def require_admin_key( From 7f2c25336ca65c0835c387d2aefaf9153be4dc55 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 10:34:01 +0200 Subject: [PATCH 33/63] ignore eclair websockets type --- lnbits/wallets/eclair.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index b669f8b74..b43d90c10 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -8,9 +8,10 @@ from typing import AsyncGenerator, Dict, Optional import httpx from loguru import logger +from websockets import connect # type: ignore +# TODO: https://github.com/lnbits/lnbits-legend/issues/764 # mypy https://github.com/aaugustin/websockets/issues/940 -# from websockets.client import connect -from websockets import connect + from websockets.exceptions import ( ConnectionClosed, ConnectionClosedError, From 9a17e4896be598a6f0294e6c54b37ba0fd7290b5 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 11:21:38 +0200 Subject: [PATCH 34/63] fix last views api bug, thanks calle --- lnbits/core/views/api.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 09eb4f906..6766ad8d1 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -387,19 +387,13 @@ async def api_payments_sse( 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 = None - try: - assert X_Api_Key is not None - # TODO: type above is Optional[str] how can that have .extra? - if X_Api_Key.extra: - logger.warning("No key") - except: - if X_Api_Key is not None: - wallet = await get_wallet_for_key(X_Api_Key) + wallet = await get_wallet_for_key(X_Api_Key) if type(X_Api_Key) == str else None + + # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order + # and get_standalone_payment otherwise just fetches the first one, causing unpredictable results payment = await get_standalone_payment( payment_hash, wallet_id=wallet.id if wallet else None - ) # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order - # and get_standalone_payment otherwise just fetches the first one, causing unpredictable results + ) if payment is None: raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="Payment does not exist." From a80fd5de7aff9f8387021bf3b414569cb510b874 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 11:33:37 +0200 Subject: [PATCH 35/63] black formating --- lnbits/core/views/api.py | 2 +- lnbits/core/views/generic.py | 16 ++++++++-------- lnbits/decorators.py | 12 ++++++------ lnbits/wallets/eclair.py | 3 ++- lnbits/wallets/fake.py | 4 +--- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 6766ad8d1..95e9dde3a 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet): ) async def api_payments_create( wallet: WalletTypeInfo = Depends(require_invoice_key), - invoiceData = Body(...), + invoiceData=Body(...), ): if invoiceData.out is True and wallet.wallet_type == 0: if not invoiceData.bolt11: diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 5f44131fc..01bc47b66 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -55,9 +55,9 @@ async def home(request: Request, lightning: str = None): ) async def extensions( request: Request, - user = Depends(check_user_exists), - enable = Query(None), - disable = Query(None), + user=Depends(check_user_exists), + enable=Query(None), + disable=Query(None), ): extension_to_enable = enable extension_to_disable = disable @@ -109,10 +109,10 @@ nothing: create everything
""", ) async def wallet( - request = Query(None), - nme = Query(None), - usr = Query(None), - wal = Query(None), + request=Query(None), + nme=Query(None), + usr=Query(None), + wal=Query(None), ): user_id = usr.hex if usr else None wallet_id = wal.hex if wal else None @@ -218,7 +218,7 @@ async def lnurl_full_withdraw_callback(request: Request): @core_html_routes.get("/deletewallet", response_class=RedirectResponse) -async def deletewallet(request: Request, wal = Query(...), usr = Query(...)): +async def deletewallet(request: Request, wal=Query(...), usr=Query(...)): user = await get_user(usr) assert user is not None user_wallet_ids = [u.id for u in user.wallets] diff --git a/lnbits/decorators.py b/lnbits/decorators.py index b74230ef8..ef689f8d0 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -123,8 +123,8 @@ api_key_query = APIKeyQuery( async def get_key_type( r: Request, - api_key_header = Security(api_key_header), - api_key_query = Security(api_key_query), + api_key_header=Security(api_key_header), + api_key_query=Security(api_key_query), ) -> WalletTypeInfo: # 0: admin # 1: invoice @@ -179,8 +179,8 @@ async def get_key_type( async def require_admin_key( r: Request, - api_key_header = Security(api_key_header), - api_key_query = Security(api_key_query), + api_key_header=Security(api_key_header), + api_key_query=Security(api_key_query), ): token = api_key_header if api_key_header else api_key_query @@ -198,8 +198,8 @@ async def require_admin_key( async def require_invoice_key( r: Request, - api_key_header = Security(api_key_header), - api_key_query = Security(api_key_query), + api_key_header=Security(api_key_header), + api_key_query=Security(api_key_query), ): token = api_key_header if api_key_header else api_key_query diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index b43d90c10..122895178 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -8,7 +8,8 @@ from typing import AsyncGenerator, Dict, Optional import httpx from loguru import logger -from websockets import connect # type: ignore +from websockets import connect # type: ignore + # TODO: https://github.com/lnbits/lnbits-legend/issues/764 # mypy https://github.com/aaugustin/websockets/issues/940 diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py index c2b2307ab..ba2956c58 100644 --- a/lnbits/wallets/fake.py +++ b/lnbits/wallets/fake.py @@ -81,9 +81,7 @@ class FakeWallet(Wallet): async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: invoice = decode(bolt11) # TODO: no data here? - data: Dict = { - "privkey": "missing" - } + data: Dict = {"privkey": "missing"} if ( invoice.checking_id is not None and invoice.checking_id[6:] == data["privkey"][:6] From b9373f72834065068fc41544037572b2002b547a Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 11:52:49 +0200 Subject: [PATCH 36/63] add tests for api_payment --- tests/core/views/test_api.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py index 6a5f82ecb..949d20527 100644 --- a/tests/core/views/test_api.py +++ b/tests/core/views/test_api.py @@ -155,3 +155,27 @@ async def test_decode_invoice(client, invoice): ) assert response.status_code < 300 assert response.json()["payment_hash"] == invoice["payment_hash"] + + +# check api_payment() internal function call (NOT API): payment status +@pytest.mark.asyncio +async def test_api_payment_without_key(invoice): + # check the payment status + response = await api_payment(invoice["payment_hash"]) + assert type(response) == dict + assert response["paid"] == True + # not key, that's why no "details" + assert "details" not in response + + +# check api_payment() internal function call (NOT API): payment status +@pytest.mark.asyncio +async def test_api_payment_with_key(invoice, inkey_headers_from): + # check the payment status + response = await api_payment( + invoice["payment_hash"], inkey_headers_from["X-Api-Key"] + ) + assert type(response) == dict + assert response["paid"] == True + # not key, that's why no "details" + assert "details" in response From a0707f8a32df9146d38fad8f97ce37fa3abd673e Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 12:00:53 +0200 Subject: [PATCH 37/63] types-mock==4.0.15 to requirements, needed for mypy tests --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 512011ec2..edcd737d6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -44,6 +44,7 @@ sqlalchemy==1.3.23 sqlalchemy-aio==0.16.0 sse-starlette==0.6.2 starlette==0.14.2 +types-mock==4.0.15 types-protobuf==3.19.22 typing-extensions==3.10.0.2 uvicorn==0.15.0 From 88ee5fd6cc8c281526b65a640f5370fd9a0b98d6 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 12:05:35 +0200 Subject: [PATCH 38/63] fixup requirements.txt and ad to Pipfile --- Pipfile | 12 +++++++----- requirements.txt | 2 -- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Pipfile b/Pipfile index f8c42a9dd..a0cef805a 100644 --- a/Pipfile +++ b/Pipfile @@ -36,9 +36,11 @@ pycryptodomex = "*" [dev-packages] black = "==20.8b1" -pytest = "*" -pytest-cov = "*" -mypy = "*" -pytest-asyncio = "*" -requests = "*" mock = "*" +mypy = "*" +pytest = "*" +pytest-asyncio = "*" +pytest-cov = "*" +requests = "*" +types-mock = "*" +types-protobuf = "*" diff --git a/requirements.txt b/requirements.txt index edcd737d6..f8ccf47cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -44,8 +44,6 @@ sqlalchemy==1.3.23 sqlalchemy-aio==0.16.0 sse-starlette==0.6.2 starlette==0.14.2 -types-mock==4.0.15 -types-protobuf==3.19.22 typing-extensions==3.10.0.2 uvicorn==0.15.0 uvloop==0.16.0 From 463fbb39bd070d54615bbe43b392255383261a89 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 12:17:34 +0200 Subject: [PATCH 39/63] make test run again, introduce mypy again --- lnbits/core/views/api.py | 2 +- tests/core/views/test_api.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 95e9dde3a..c81f623a7 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet): ) async def api_payments_create( wallet: WalletTypeInfo = Depends(require_invoice_key), - invoiceData=Body(...), + invoiceData: CreateInvoiceData = Body(...), ): if invoiceData.out is True and wallet.wallet_type == 0: if not invoiceData.bolt11: diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py index 949d20527..7c1b134d5 100644 --- a/tests/core/views/test_api.py +++ b/tests/core/views/test_api.py @@ -1,6 +1,7 @@ import pytest import pytest_asyncio from lnbits.core.crud import get_wallet +from lnbits.core.views.api import api_payment from ...helpers import get_random_invoice_data From 0e393111deb6f12c0ca8fb0d04122813cf4b915a Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 12:21:39 +0200 Subject: [PATCH 40/63] fix up decorators for tests --- lnbits/decorators.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index ef689f8d0..adfbd6649 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -1,6 +1,6 @@ from http import HTTPStatus -from typing import Optional +from typing import Union from cerberus import Validator # type: ignore from fastapi import status @@ -102,9 +102,9 @@ class WalletAdminKeyChecker(KeyChecker): class WalletTypeInfo: wallet_type: int - wallet: Wallet + wallet: Union[Wallet, None] - def __init__(self, wallet_type: int, wallet: Wallet) -> None: + def __init__(self, wallet_type: int, wallet: Union[Wallet, None]) -> None: self.wallet_type = wallet_type self.wallet = wallet @@ -171,7 +171,7 @@ async def get_key_type( if e.status_code == HTTPStatus.BAD_REQUEST: raise if e.status_code == HTTPStatus.UNAUTHORIZED: - return WalletTypeInfo(2, Wallet()) + return WalletTypeInfo(2, None) except: raise return wallet From 4dfa0873e5c36fd61019820efe1c138b1923396f Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 13:12:55 +0200 Subject: [PATCH 41/63] fix isort check --- lnbits/core/views/api.py | 2 +- lnbits/decorators.py | 1 - lnbits/utils/exchange_rates.py | 2 +- lnbits/wallets/eclair.py | 4 +--- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index c81f623a7..d7be3e554 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -3,7 +3,7 @@ import hashlib import json from binascii import unhexlify from http import HTTPStatus -from typing import Dict, List, Optional, Union, Tuple +from typing import Dict, List, Optional, Tuple, Union from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx diff --git a/lnbits/decorators.py b/lnbits/decorators.py index adfbd6649..b62e456af 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -1,5 +1,4 @@ from http import HTTPStatus - from typing import Union from cerberus import Validator # type: ignore diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py index fbb4add22..2801146b5 100644 --- a/lnbits/utils/exchange_rates.py +++ b/lnbits/utils/exchange_rates.py @@ -1,5 +1,5 @@ import asyncio -from typing import Callable, NamedTuple, List +from typing import Callable, List, NamedTuple import httpx from loguru import logger diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py index 122895178..1ba81385e 100644 --- a/lnbits/wallets/eclair.py +++ b/lnbits/wallets/eclair.py @@ -8,11 +8,9 @@ from typing import AsyncGenerator, Dict, Optional import httpx from loguru import logger -from websockets import connect # type: ignore - # TODO: https://github.com/lnbits/lnbits-legend/issues/764 # mypy https://github.com/aaugustin/websockets/issues/940 - +from websockets import connect # type: ignore from websockets.exceptions import ( ConnectionClosed, ConnectionClosedError, From a3e4f175dd61e6ee22f7f05aaff828a93dc9572d Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 13:21:42 +0200 Subject: [PATCH 42/63] add missing Pipfile.lock --- Pipfile.lock | 224 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 145 insertions(+), 79 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 42d471c6f..2704d4156 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "503e9942306106e40621c59f37a3ab866b483f8c5f27b879c1c6783dca30949f" + "sha256": "08e53fc6f1fcc021b33f9b2c5ae3bd6dbde815d4b317e9341ab02cf5b625acbc" }, "pipfile-spec": 6, "requires": { @@ -29,6 +29,7 @@ "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b", "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be" ], + "markers": "python_full_version >= '3.6.2'", "version": "==3.6.1" }, "asyncio": { @@ -46,6 +47,7 @@ "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==21.4.0" }, "bech32": { @@ -53,6 +55,7 @@ "sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899", "sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981" ], + "markers": "python_version >= '3.5'", "version": "==1.2.0" }, "bitstring": { @@ -76,6 +79,7 @@ "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d", "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412" ], + "markers": "python_version >= '3.6'", "version": "==2022.6.15" }, "cffi": { @@ -139,6 +143,7 @@ "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" ], + "markers": "python_version >= '3.7'", "version": "==8.1.3" }, "ecdsa": { @@ -177,6 +182,7 @@ "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6", "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042" ], + "markers": "python_version >= '3.6'", "version": "==0.12.0" }, "httpcore": { @@ -184,6 +190,7 @@ "sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6", "sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b" ], + "markers": "python_version >= '3.7'", "version": "==0.15.0" }, "httptools": { @@ -238,6 +245,7 @@ "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" ], + "markers": "python_version >= '3.5'", "version": "==3.3" }, "jinja2": { @@ -307,6 +315,7 @@ "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" ], + "markers": "python_version >= '3.7'", "version": "==2.1.1" }, "marshmallow": { @@ -314,6 +323,7 @@ "sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb", "sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7" ], + "markers": "python_version >= '3.7'", "version": "==3.17.0" }, "outcome": { @@ -321,6 +331,7 @@ "sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672", "sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5" ], + "markers": "python_version >= '3.7'", "version": "==1.2.0" }, "packaging": { @@ -328,6 +339,7 @@ "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" ], + "markers": "python_version >= '3.6'", "version": "==21.3" }, "psycopg2-binary": { @@ -473,6 +485,7 @@ "sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e", "sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6" ], + "markers": "python_full_version >= '3.6.1'", "version": "==1.9.1" }, "pyngrok": { @@ -487,6 +500,7 @@ "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" ], + "markers": "python_full_version >= '3.6.8'", "version": "==3.0.9" }, "pypng": { @@ -562,9 +576,13 @@ "sha256:026c0de2ee8385d1255b9c2426cd4f03fe9177ac94c09979bc601946c8493aa0", "sha256:99142650756ef1998ce0661568f54a47dac8c638fb27e3816c02536575dbba8c" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.6.0.post0" }, "rfc3986": { + "extras": [ + "idna2008" + ], "hashes": [ "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835", "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97" @@ -600,6 +618,14 @@ "index": "pypi", "version": "==0.14.0" }, + "setuptools": { + "hashes": [ + "sha256:0d33c374d41c7863419fc8f6c10bfe25b7b498aa34164d135c622e52580c6b16", + "sha256:c04b44a57a6265fe34a4a444e965884716d34bae963119a76353434d6f18e450" + ], + "markers": "python_version >= '3.7'", + "version": "==63.2.0" + }, "shortuuid": { "hashes": [ "sha256:459f12fa1acc34ff213b1371467c0325169645a31ed989e268872339af7563d5", @@ -613,6 +639,7 @@ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", "version": "==1.16.0" }, "sniffio": { @@ -620,6 +647,7 @@ "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663", "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de" ], + "markers": "python_version >= '3.5'", "version": "==1.2.0" }, "sqlalchemy": { @@ -676,17 +704,18 @@ }, "sse-starlette": { "hashes": [ - "sha256:840607fed361360cc76f8408a25f0eca887e7cab3c3ee44f9762f179428e2bd4", - "sha256:ca2de945af80b83f1efda6144df9e13db83880b3b87c660044b64f199395e8b7" + "sha256:14608559d40e3e7c6385e8c5a7b88468f7fc40c2277673a1fe8d26568e8d7c65", + "sha256:72438ed39b1612d1ea6d89a7c0af8afee6de0389dcbe2e77539001e78b5aa89c" ], "index": "pypi", - "version": "==0.10.3" + "version": "==1.0.0" }, "starlette": { "hashes": [ "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf", "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7" ], + "markers": "python_version >= '3.6'", "version": "==0.19.1" }, "typing-extensions": { @@ -698,6 +727,9 @@ "version": "==4.3.0" }, "uvicorn": { + "extras": [ + "standard" + ], "hashes": [ "sha256:c19a057deb1c5bb060946e2e5c262fc01590c6529c0af2c3d9ce941e89bc30e0", "sha256:cade07c403c397f9fe275492a48c1b869efd175d5d8a692df649e6e7e2ed8f4e" @@ -815,6 +847,7 @@ "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==21.4.0" }, "black": { @@ -829,6 +862,7 @@ "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d", "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412" ], + "markers": "python_version >= '3.6'", "version": "==2022.6.15" }, "charset-normalizer": { @@ -836,6 +870,7 @@ "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5", "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413" ], + "markers": "python_full_version >= '3.6.0'", "version": "==2.1.0" }, "click": { @@ -843,9 +878,13 @@ "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" ], + "markers": "python_version >= '3.7'", "version": "==8.1.3" }, "coverage": { + "extras": [ + "toml" + ], "hashes": [ "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32", "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7", @@ -889,6 +928,7 @@ "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39", "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452" ], + "markers": "python_version >= '3.7'", "version": "==6.4.2" }, "idna": { @@ -896,6 +936,7 @@ "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" ], + "markers": "python_version >= '3.5'", "version": "==3.3" }, "iniconfig": { @@ -954,6 +995,7 @@ "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" ], + "markers": "python_version >= '3.6'", "version": "==21.3" }, "pathspec": { @@ -968,6 +1010,7 @@ "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" ], + "markers": "python_full_version >= '3.6.0'", "version": "==1.0.0" }, "py": { @@ -975,6 +1018,7 @@ "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==1.11.0" }, "pyparsing": { @@ -982,6 +1026,7 @@ "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" ], + "markers": "python_full_version >= '3.6.8'", "version": "==3.0.9" }, "pytest": { @@ -1010,82 +1055,83 @@ }, "regex": { "hashes": [ - "sha256:00d2e907d3c5e4f85197c8d2263a9cc2d34bf234a9c6236ae42a3fb0bc09b759", - "sha256:0186edcda692c38381db8ac257c2d023fd2e08818d45dc5bee4ed84212045f51", - "sha256:06c509bd7dcb7966bdb03974457d548e54d8327bad5b0c917e87248edc43e2eb", - "sha256:0a3f3f45c5902eb4d90266002ccb035531ae9b9278f6d5e8028247c34d192099", - "sha256:0c1821146b429e6fdbd13ea10f26765e48d5284bc79749468cfbfe3ceb929f0d", - "sha256:0d93167b7d7731fa9ff9fdc1bae84ec9c7133b01a35f8cc04e926d48da6ce1f7", - "sha256:0fd8c3635fa03ef79d07c7b3ed693b3f3930ccb52c0c51761c3296a7525b135c", - "sha256:119091c675e6ad19da8770f89aa1d52f4ad2a2018d631956f3e90c45882df880", - "sha256:121981ba84309dabefd5e1debd49be6d51624e54b4d44bfc184cd8d555ff1df1", - "sha256:1244e9b9b4b81c9c34e8a84273ffaeebdc78abc98a5b02dcdd49845eb3c63bd7", - "sha256:12e1404dfb4e928d3273a10e3468877fe84bdcd3c50b655a2c9613cfc5d9fe63", - "sha256:13d74951c14708f00700bb29475129ecbc40e01b4029c62ee7bfe9d1f59f31ce", - "sha256:162a5939a6fdf48658d3565eeff35acdd207e07367bf5caaff3d9ea7cb77d7a9", - "sha256:1703490c5b850fa9cef1af00c58966756042e6ca22f4fb5bb857345cd535834f", - "sha256:18e6203cfd81df42a987175aaeed7ba46bcb42130cd81763e2d5edcff0006d5d", - "sha256:192c2784833aea6fc7b004730bf1b91b8b8c6b998b30271aaf3bd8adfef20a96", - "sha256:1948d3ceac5b2d55bc93159c1e0679a256a87a54c735be5cef4543a9e692dbb9", - "sha256:206a327e628bc529d64b21ff79a5e2564f5aec7dc7abcd4b2e8a4b271ec10550", - "sha256:2e5db20412f0db8798ff72473d16da5f13ec808e975b49188badb2462f529fa9", - "sha256:2f94b0befc811fe74a972b1739fffbf74c0dc1a91102aca8e324aa4f2c6991bd", - "sha256:303676797c4c7978726e74eb8255d68f7125a3a29da71ff453448f2117290e9a", - "sha256:34ae4f35db30caa4caf85c55069fcb7a05966a3a5ba6e9e1dab5477d84fbb08a", - "sha256:3c6df8be7d1dd35a0d9a200fbc29f888c4452c8882d284f87608046152e049e6", - "sha256:402fa998c5988d11ed34585eb65740dcebd0fd11844d12eb0a6b4be178eb9c64", - "sha256:40a28759d345c0bb1f5b0ac74ac04f5d48136019522c95c0ec4b07786f67ce20", - "sha256:414ae507ba88264444baf771fec43ce0adcd4c5dbb304d3e0716f3f4d4499d2e", - "sha256:42da079e31ae9818ffa7a35cdd16ab7104e3f7eca9c0958040aede827b2e55c6", - "sha256:473a7d21932ce7c314953b33c32e63df690181860edcdf14bba1278cdf71b07f", - "sha256:49fcb45931a693b0e901972c5e077ea2cf30ec39da699645c43cb8b1542c6e14", - "sha256:4c5913cb9769038bd03e42318955c2f15a688384a6a0b807bcfc8271603d9277", - "sha256:4cfeb71095c8d8380a5df5a38ff94d27a3f483717e509130a822b4d6400b7991", - "sha256:4dc74f0171eede67d79a79c06eca0fe5b7b280dbb8c27ad1fae4ced2ad66268f", - "sha256:5b1cffff2d9f832288fe516021cb81c95c57c0067b13a82f1d2daabdbc2f4270", - "sha256:601c99ac775b6c89699a48976f3dbb000b47d3ca59362c8abc9582e6d0780d91", - "sha256:667a06bb8d72b6da3d9cf38dac4ba969688868ed2279a692e993d2c0e1c30aba", - "sha256:673549a0136c7893f567ed71ab5225ed3701c79b17c0a7faee846c645fc24010", - "sha256:67bd3bdd27db7a6460384869dd4b9c54267d805b67d70b20495bb5767f8e051c", - "sha256:727edff0a4eaff3b6d26cbb50216feac9055aba7e6290eec23c061c2fe2fab55", - "sha256:782627a1cb8fbb1c78d8e841f5b71c2c683086c038f975bebdac7cce7678a96f", - "sha256:7d462ba84655abeddae4dfc517fe1afefb5430b3b5acb0a954de12a47aea7183", - "sha256:8ab39aa445d00902c43a1e951871bedc7f18d095a21eccba153d594faac34aea", - "sha256:8e2075ed4ea2e231e2e98b16cfa5dae87e9a6045a71104525e1efc29aa8faa8e", - "sha256:9daeccb2764bf4cc280c40c6411ae176bb0876948e536590a052b3d647254c95", - "sha256:9e4006942334fa954ebd32fa0728718ec870f95f4ba7cda9edc46dd49c294f22", - "sha256:9f1c8fffd4def0b76c0947b8cb261b266e31041785dc2dc2db7569407a2f54fe", - "sha256:a00cd58a30a1041c193777cb1bc090200b05ff4b073d5935738afd1023e63069", - "sha256:a0220a7a16fd4bfc700661f920510defd31ef7830ce992d5cc51777aa8ccd724", - "sha256:a048f91823862270905cb22ef88038b08aac852ce48e0ecc4b4bf1b895ec37d9", - "sha256:a3c47c71fde0c5d584402e67546c81af9951540f1f622d821e9c20761556473a", - "sha256:a6d9ea727fd1233ee746bf44dd37e7d4320b3ed8ff09e73d7638c969b28d280f", - "sha256:ab0709daedc1099bbd4371ae17eeedd4efc1cf70fcdcfe5de1374a0944b61f80", - "sha256:ab1cb36b411f16da6e057ef8e6657dd0af36f59a667f07e0b4b617e44e53d7b2", - "sha256:ae1c5b435d44aa91d48cc710f20c3485e0584a3ad3565d5ae031d61a35f674f4", - "sha256:b279b9bb401af41130fd2a09427105100bc8c624ed45da1c81c1c0d0aa639734", - "sha256:b72a4ec79a15f6066d14ae1c472b743af4b4ecee14420e8d6e4a336b49b8f21c", - "sha256:c2cd93725911c0159d597b90c96151070ef7e0e67604637e2f2abe06c34bf079", - "sha256:c7c5f914b0eb5242c09f91058b80295525897e873b522575ab235b48db125597", - "sha256:d07d849c9e2eca80adb85d3567302a47195a603ad7b1f0a07508e253c041f954", - "sha256:d2672d68cf6c8452b6758fc3cd2d8feac966d511eed79a68182a5297b473af9c", - "sha256:d35bbcbf70d14f724e7489746cf68efe122796578addd98f91428e144d0ad266", - "sha256:d40b4447784dbe0896a6d10a178f6724598161f942c56f5a60dc0ef7fe63f7a1", - "sha256:d561dcb0fb0ab858291837d51330696a45fd3ba6912a332a4ee130e5484b9e47", - "sha256:d7f5ccfff648093152cadf6d886c7bd922047532f72024c953a79c7553aac2fe", - "sha256:dce6b2ad817e3eb107f8704782b091b0631dd3adf47f14bdc086165d05b528b0", - "sha256:e1fdda3ec7e9785065b67941693995cab95b54023a21db9bf39e54cc7b2c3526", - "sha256:e2a262ec85c595fc8e1f3162cafc654d2219125c00ea3a190c173cea70d2cc7a", - "sha256:e2fc1e3928c1189c0382c547c17717c6d9f425fffe619ef94270fe4c6c8be0a6", - "sha256:ea27acd97a752cfefa9907da935e583efecb302e6e9866f37565968c8407ad58", - "sha256:ee769a438827e443ed428e66d0aa7131c653ecd86ddc5d4644a81ed1d93af0e7", - "sha256:f32e0d1c7e7b0b9c3cac76f3d278e7ee6b99c95672d2c1c6ea625033431837c0", - "sha256:f355caec5bbce20421dc26e53787b10e32fd0df68db2b795435217210c08d69c", - "sha256:f87e9108bb532f8a1fc6bf7e69b930a35c7b0267b8fef0a3ede0bcb4c5aaa531", - "sha256:f8a2fd2f62a77536e4e3193303bec380df40d99e253b1c8f9b6eafa07eaeff67", - "sha256:fbdf4fc6adf38fab1091c579ece3fe9f493bd0f1cfc3d2c76d2e52461ca4f8a9" + "sha256:045682b6457f0224deb10c9720b8008dc798b1ad4331de9302fd4b615211e5a5", + "sha256:04e3869fc6fc24b75d38e7547797bb0a82d6cccd49e8ce6ae21a0b87aeb9fac7", + "sha256:073bd76a1f03e05a6ca0df705b6117f75b10c340af068b55becb1334fc6426c2", + "sha256:0aa48740a1385cb668ffd828a7e2c8078ce29c72d64651c8226bd2b7cb5dba0c", + "sha256:0e380ebd841201f980ab022d07033be12c9438a9b2e0f60324c9e3ba31790918", + "sha256:0f9b8ef2e46d627b2a85d3f4fe433ead283c420adcf9461906c3db10766dd3b1", + "sha256:10d3a5dda21a125cfdc31e45ae6ce6bdd1e45cb194199801248d6580be8dc337", + "sha256:11965ae779a0ccfb6d17996d531e2f522124e04d98cc65742b96bf8f50758ab9", + "sha256:1c61175730596266015c4e005c65cbfeeb1ef019ccd024870169c6593a26bdb0", + "sha256:1d1d79a87a33fbd6573e30eb53969a4190035894c4390a0787fb823e9e86b72f", + "sha256:1f0bf228c948f543876f4fb310322a4ff7e398667dd58aeb4815dc9e30bf867e", + "sha256:2026a1c108752e48577f9720076bf6e31a60aaf0c3000fffad4e2527fdffbe95", + "sha256:204705b7ec16267d39870a19e72e832b12739dc48a26d923a9cb94043660d50f", + "sha256:248b16534d1ef8f10a72cda0f97b3dfa25b3d9123a7e726d1594cb07a541bba0", + "sha256:27b5011449213dfd880e592ea6d311d00739e87d9512bad507ee18c9c92a20ac", + "sha256:2880d21e9507869ab1636e50461afb9ffb08797f1cb76f70d3ad52e7dd13a335", + "sha256:2d3f9fc885ecd8b0eb248d0e190aa7264b977cc23b6da7c08444065170c57e2e", + "sha256:2d8b50ae3cbaaa2e5ed89ed81fc025ec64b1a54c4f34e6bdaad9dc63fc2afa6b", + "sha256:3ff5b2b6a136307a5551e7821d83ab12c46f57c32bf23a27877c9c6bdb55aa61", + "sha256:40b4436466d47271fe3b4df63e55307c91a40cda0875a9ff3b7231a08394b283", + "sha256:435c94d5939a7cf4b0af1cce30d196451bae441ebe64f63d08517ab490ceb385", + "sha256:4c0b7c413d4a8a55d72df18acbeb50276eee19cec7e2f54ed3bddc46bc3b3aeb", + "sha256:4cfe87490d0a801749b42491ef7e968342e5787decbf57d5402fc2c17f7302e3", + "sha256:4f480661cd0809a1177b09581c12c9ceff9ca989e4a0c8c0f10379dffa3b4c4c", + "sha256:53370620db6058dbb464324b053fee8608518d76ff6352b2835d71e2ae8ef293", + "sha256:53eca0cece2ddb592b8dd9746f0b258d0c8a45f5a3ac8eb96833058f64778fca", + "sha256:5751bfe0d939d7110396510a39e48ee928b36b55177207c47766b093886a3945", + "sha256:5d750f99c40a7e994df1cf1295bbb3e873417ca69508664fe9f65db92e46ca40", + "sha256:6019737db5c46a24f307eae5069fced0752e3a22380398bbefbef77b068b9537", + "sha256:605ca47681c7405723a4970d66d13fa3a3a66efa6b8499d7ab7bce1ddb44a36f", + "sha256:69120a8fb1eb932b6e3ededb16448be6444eb952f9350c21dddbef947fea5690", + "sha256:72713de336b8d895f91aad34f5591f33d1d8727bd739af3ae2657411ef6e0739", + "sha256:74077b462a9b255c5fca247484f46b0f25c32676fe4645cb6b5304b2d997357a", + "sha256:74f0067495a842f7cc198b14031a2893d377bed38e19d785f35095082ab5a556", + "sha256:817a0618c149d77e493963cd98851ff49d6ab8bcab247fdbf85bb89a14dca5c3", + "sha256:869a0f6405ec569863e09909617138af575b5e2bc5181184e60f339a4c8a6d7a", + "sha256:86b0cf786efd587c27abd1d07020c555a82275bba3506d916d42aed7a3744967", + "sha256:8cbc407c44003a1cb4aaa2d48cc19b45dde07ba0ae2f541c6750ad18f8c118f5", + "sha256:90e082f262cf858cbcde330999ea5612e12918982033b716d2c5d8b1bc7a01db", + "sha256:95d8f1083ff4546ec14fb46dc41b042d372258f8c319df1e2316d8fe1bd3f085", + "sha256:97211bae1bc51f153764485a54d8d1130196cae33d02285c33732b26c5328b8d", + "sha256:98dbaf6a86991e2b09f4d8a7669b4304755bda519565971dc3b87ee00fd6eab2", + "sha256:9e297ff33172853e9a9e46dbb0c2ebf44fb38ebefce659698df4eb9dfce0a748", + "sha256:9e64492c8105312f080e25e457db70f9b0d02e6ba3c1ea14468087b0e3aa876f", + "sha256:a11241808e59deec8314792bbd8a6f0a8a7a95b742709e134c73a3216dbb26ae", + "sha256:a33c36dcc1760d66f1969d7d3dc8956f45a3d502178053074b8489f67718138f", + "sha256:a4e1e7ba8c58c1f0b828418f9a2635c0f6344bf107308b8fe65f234a13c8462a", + "sha256:a55c4d5e5076cc5ece625dd1f7015c9a0818ba1f9ad9db421b495d7ece088e56", + "sha256:a6f5cc82e1fa380eb6b8040d626df6ba9f492b6886527f53d59838b11e9caaef", + "sha256:a96826b5c9dc68417ddb29843998473f9c2c047911e6fca36a9f81a898087b01", + "sha256:acf6cbcc19d86f44e8a9d3cf1f6946a71dc55c2ec8ae374c547b1eefd83b954f", + "sha256:b4be25d3c640a35671431d8ef8cd522319254074b150147fcacad90c91ea42c2", + "sha256:b5a31abc27f9bda7a455bfa1e1bd623da50e3a343a040084c879d07394a93481", + "sha256:bb812e590e3881a93d4d291270440b3795fa4c0bc1b03ba15fe1cc88d2ea4347", + "sha256:bbd542b4afd2ed5491a480e9b15f4bca13e0170ef1895064fd15741382fdebe0", + "sha256:bf27111f56b762238bd3ce4c5e8ad34167d85dd3c077721a0c093517526a94af", + "sha256:c7a4dc9436b7a55c36daa3959e92d70337c547651ebeda685dd6bb083f0b77ab", + "sha256:c88a1068ac8e5dc579d5104903fa2c488448c1137e580a77d1438d98070c4243", + "sha256:c8cffb5040bf432355cfe51378072f20087609694066ace80710bdba04cf9ce2", + "sha256:c944921b0e77f1923dd89cda65534223ac107e24d71b1dfe174237faa5efd32a", + "sha256:ca2a7233275acf0087ecd15e5fb0eeb722a1f4de453b49bb1443edf2c2f5a997", + "sha256:cf0a3b9744f94693f3ebcca1c259354f0043c19a4ce938f80ad6d1816b8fd8f0", + "sha256:dbe2f16a66f64a00dc9ccc0db7f8b5ff014f409840e03675eb431f03b50ddffd", + "sha256:deb7b067b3b9751c60dc7f6de68476138d550c074a5016ba944cc55863fa86d1", + "sha256:df669bacbda209e9b00928f1d00432b27a16c3e051f9f7e5ea306f9b78bf3e7c", + "sha256:e2dd4ca82c2241be9582d2ae060070f2bccb0c98295b608009d5cc6e6041eaed", + "sha256:e432cf909c53506da4c8308753b2671ee37d2d8d1de8b4b54ab76e91ca7ba0b5", + "sha256:e4e7f1aba3aaf08e11d33fd5c2d8dd8cbf573049474e11256c91e3ba3d5e1642", + "sha256:e51ab7fbfe5ac3002b9aee527bcb164b17fd92f5663ebf2a4e5917dd9d577864", + "sha256:ea00c7f86405d88995e7bab5609e343fdedfe1ffc8191d3b5ed0f8c7f5eb17ec", + "sha256:f7006d7c74e25f8bc592604a5a72ba624f10ebd5c0683ab4d3e940a88ac0098c", + "sha256:f93e3e5acf82812ea92a1ccdcce690aab18c4044dd824f6b959d2b6069d84312", + "sha256:fa8a4bc81b15f49c57ede3fd636786c6619179661acf2430fcc387d75bf28d33", + "sha256:fc44c49f33dd75e58b5ff2a5ac50c96c84b6b209d36b4790c85bca08a3b9017d" ], - "version": "==2022.7.9" + "markers": "python_full_version >= '3.6.0'", + "version": "==2022.7.24" }, "requests": { "hashes": [ @@ -1100,6 +1146,7 @@ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'", "version": "==0.10.2" }, "tomli": { @@ -1107,6 +1154,7 @@ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" ], + "markers": "python_version >= '3.7'", "version": "==2.0.1" }, "typed-ast": { @@ -1136,8 +1184,25 @@ "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3", "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66" ], + "markers": "python_full_version >= '3.6.0'", "version": "==1.5.4" }, + "types-mock": { + "hashes": [ + "sha256:4535fbb3912b88a247d43cdb41db0c8b2e187138986f6f01a989717e56105848", + "sha256:a849bc2d966063f4946013bf404822ee2b96f77a8dccda4174b70ab61c5293fe" + ], + "index": "pypi", + "version": "==4.0.15" + }, + "types-protobuf": { + "hashes": [ + "sha256:d291388678af91bb045fafa864f142dc4ac22f5d4cdca097c7d8d8a32fa9b3ab", + "sha256:d2b26861b0cb46a3c8669b0df507b7ef72e487da66d61f9f3576aa76ce028a83" + ], + "index": "pypi", + "version": "==3.19.22" + }, "typing-extensions": { "hashes": [ "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02", @@ -1151,6 +1216,7 @@ "sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec", "sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", "version": "==1.26.10" } } From e939b9a5aab55bbae8cd9f5c85d919bdf075da6a Mon Sep 17 00:00:00 2001 From: Daniel Krahofer Date: Wed, 20 Jul 2022 13:15:06 +0200 Subject: [PATCH 43/63] Update tests/core/views/test_api.py Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> --- tests/core/views/test_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py index 7c1b134d5..cfbd77104 100644 --- a/tests/core/views/test_api.py +++ b/tests/core/views/test_api.py @@ -178,5 +178,4 @@ async def test_api_payment_with_key(invoice, inkey_headers_from): ) assert type(response) == dict assert response["paid"] == True - # not key, that's why no "details" assert "details" in response From b865fad95e597c027deceec409568f60fb3e0582 Mon Sep 17 00:00:00 2001 From: Daniel Krahofer Date: Wed, 20 Jul 2022 13:15:18 +0200 Subject: [PATCH 44/63] Update tests/core/views/test_api.py Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> --- tests/core/views/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py index cfbd77104..dfd2b32ae 100644 --- a/tests/core/views/test_api.py +++ b/tests/core/views/test_api.py @@ -165,7 +165,7 @@ async def test_api_payment_without_key(invoice): response = await api_payment(invoice["payment_hash"]) assert type(response) == dict assert response["paid"] == True - # not key, that's why no "details" + # no key, that's why no "details" assert "details" not in response From 66477bc392a5e8594122fdb526e9fe160c6b5488 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 13:23:50 +0200 Subject: [PATCH 45/63] enable mypy checks --- .github/workflows/mypy.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 4d6c6d4da..bf90a8e31 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -5,7 +5,6 @@ on: [push, pull_request] jobs: check: runs-on: ubuntu-latest - if: ${{ 'false' == 'true' }} # skip mypy for now steps: - uses: actions/checkout@v1 - uses: jpetrucciani/mypy-check@master From df00a018f0b8f78edfa478a9f21d91cff95204aa Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 20 Jul 2022 13:41:13 +0200 Subject: [PATCH 46/63] added FIXME tags and fix WalletTypeInfo --- lnbits/core/services.py | 4 ++++ lnbits/core/views/api.py | 23 +++++++++++++++++++++++ lnbits/decorators.py | 2 ++ 3 files changed, 29 insertions(+) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index f4f962504..2416ed315 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -269,6 +269,10 @@ async def perform_lnurlauth( cb = urlparse(callback) k1 = unhexlify(parse_qs(cb.query)["k1"][0]) + + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None + key = wallet.wallet.lnurlauth_key(cb.netloc) def int_to_bytes_suitable_der(x: int) -> bytes: diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index d7be3e554..a3a59a5f0 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -56,12 +56,16 @@ from ..tasks import api_invoice_listeners @core_app.get("/api/v1/wallet") async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)): if wallet.wallet_type == 0: + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return { "id": wallet.wallet.id, "name": wallet.wallet.name, "balance": wallet.wallet.balance_msat, } else: + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return {"name": wallet.wallet.name, "balance": wallet.wallet.balance_msat} @@ -69,6 +73,9 @@ async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)): async def api_update_balance( amount: int, wallet: WalletTypeInfo = Depends(get_key_type) ): + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None + if wallet.wallet.user not in LNBITS_ADMIN_USERS: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user" @@ -98,6 +105,9 @@ async def api_update_balance( async def api_update_wallet( new_name: str, wallet: WalletTypeInfo = Depends(require_admin_key) ): + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None + await update_wallet(wallet.wallet.id, new_name) return { "id": wallet.wallet.id, @@ -112,6 +122,9 @@ async def api_payments( offset: Optional[int] = None, wallet: WalletTypeInfo = Depends(get_key_type), ): + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None + pendingPayments = await get_payments( wallet_id=wallet.wallet.id, pending=True, @@ -256,11 +269,15 @@ async def api_payments_create( status_code=HTTPStatus.BAD_REQUEST, detail="BOLT11 string is invalid or not given", ) + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return await api_payments_pay_invoice( invoiceData.bolt11, wallet.wallet ) # admin key elif not invoiceData.out: # invoice key + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return await api_payments_create_invoice(invoiceData, wallet.wallet) else: raise HTTPException( @@ -325,6 +342,8 @@ async def api_payments_pay_lnurl( if data.comment: extra["comment"] = data.comment assert data.description is not None, "description is required" + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None payment_hash = await pay_invoice( wallet_id=wallet.wallet.id, payment_request=params["pr"], @@ -378,6 +397,8 @@ async def subscribe(request: Request, wallet: Wallet): async def api_payments_sse( request: Request, wallet: WalletTypeInfo = Depends(get_key_type) ): + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None return EventSourceResponse( subscribe(request, wallet.wallet), ping=20, media_type="text/event-stream" ) @@ -456,6 +477,8 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type params.update(kind="auth") params.update(callback=url) # with k1 already in it + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None lnurlauth_key = wallet.wallet.lnurlauth_key(domain) params.update(pubkey=lnurlauth_key.verifying_key.to_string("compressed").hex()) else: diff --git a/lnbits/decorators.py b/lnbits/decorators.py index b62e456af..9f51ce646 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -159,6 +159,8 @@ async def get_key_type( invoice_checker = WalletInvoiceKeyChecker(api_key=token) await invoice_checker.__call__(r) wallet = WalletTypeInfo(1, invoice_checker.wallet) + # FIXME: wallet.wallet can be None here + assert wallet.wallet is not None if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and ( LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS ): From 9cecd42467dc5bac66b841611b97b574f54749bb Mon Sep 17 00:00:00 2001 From: dni Date: Mon, 25 Jul 2022 12:09:29 +0200 Subject: [PATCH 47/63] ignore Body type --- lnbits/core/views/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index a3a59a5f0..59ce61a9d 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -261,7 +261,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet): ) async def api_payments_create( wallet: WalletTypeInfo = Depends(require_invoice_key), - invoiceData: CreateInvoiceData = Body(...), + invoiceData: CreateInvoiceData = Body(...), #type: ignore ): if invoiceData.out is True and wallet.wallet_type == 0: if not invoiceData.bolt11: From a7c10dde4ad7bc88a63f2f5805049fc29d5cf8b1 Mon Sep 17 00:00:00 2001 From: dni Date: Mon, 25 Jul 2022 13:13:50 +0200 Subject: [PATCH 48/63] fixup types --- .github/workflows/mypy.yml | 2 +- lnbits/core/views/generic.py | 13 +++++++------ lnbits/decorators.py | 12 ++++++------ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index bf90a8e31..71713d88f 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -10,4 +10,4 @@ jobs: - uses: jpetrucciani/mypy-check@master with: mypy_flags: '--install-types --non-interactive' - path: lnbits + path: 'lnbits' diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 01bc47b66..433c6f62e 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -55,9 +55,9 @@ async def home(request: Request, lightning: str = None): ) async def extensions( request: Request, - user=Depends(check_user_exists), - enable=Query(None), - disable=Query(None), + user: User = Depends(check_user_exists), #type: ignore + enable: str = Query(None), #type: ignore + disable: str = Query(None), #type: ignore ): extension_to_enable = enable extension_to_disable = disable @@ -108,11 +108,12 @@ just **user_id**: return the first user wallet or create one if none found (with nothing: create everything
""", ) + async def wallet( request=Query(None), - nme=Query(None), - usr=Query(None), - wal=Query(None), + nme: Optional[str] = Query(None), #type: ignore + usr: Optional[UUID4] = Query(None), #type: ignore + wal: Optional[UUID4] = Query(None), #type: ignore ): user_id = usr.hex if usr else None wallet_id = wal.hex if wal else None diff --git a/lnbits/decorators.py b/lnbits/decorators.py index 9f51ce646..b65c43a1c 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -122,8 +122,8 @@ api_key_query = APIKeyQuery( async def get_key_type( r: Request, - api_key_header=Security(api_key_header), - api_key_query=Security(api_key_query), + api_key_header: str = Security(api_key_header), #type: ignore + api_key_query: str = Security(api_key_query) #type: ignore ) -> WalletTypeInfo: # 0: admin # 1: invoice @@ -180,8 +180,8 @@ async def get_key_type( async def require_admin_key( r: Request, - api_key_header=Security(api_key_header), - api_key_query=Security(api_key_query), + api_key_header: str = Security(api_key_header), #type: ignore + api_key_query: str = Security(api_key_query) #type: ignore ): token = api_key_header if api_key_header else api_key_query @@ -199,8 +199,8 @@ async def require_admin_key( async def require_invoice_key( r: Request, - api_key_header=Security(api_key_header), - api_key_query=Security(api_key_query), + api_key_header: str = Security(api_key_header), #type: ignore + api_key_query: str = Security(api_key_query) #type: ignore ): token = api_key_header if api_key_header else api_key_query From 0bf4457c2f1289784fd60066233f12c7835e807d Mon Sep 17 00:00:00 2001 From: dni Date: Mon, 25 Jul 2022 13:19:35 +0200 Subject: [PATCH 49/63] fixup types --- lnbits/core/views/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 433c6f62e..3058d115f 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -110,7 +110,7 @@ nothing: create everything
) async def wallet( - request=Query(None), + request: Request = Query(None), #type: ignore nme: Optional[str] = Query(None), #type: ignore usr: Optional[UUID4] = Query(None), #type: ignore wal: Optional[UUID4] = Query(None), #type: ignore From 3d1abfa35a5f5672f0aa736d0dab742f8cec5b8a Mon Sep 17 00:00:00 2001 From: dni Date: Mon, 25 Jul 2022 13:27:31 +0200 Subject: [PATCH 50/63] last mypy fails --- lnbits/core/views/generic.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 3058d115f..0d8a7b0e4 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -88,7 +88,7 @@ async def extensions( # Update user as his extensions have been updated if extension_to_enable or extension_to_disable: - user = await get_user(user.id) + user = await get_user(user.id) #type: ignore return template_renderer().TemplateResponse( "core/extensions.html", {"request": request, "user": user.dict()} @@ -152,8 +152,8 @@ async def wallet( ) logger.debug(f"Access wallet {wallet_name}{'of user '+ user.id if user else ''}") - wallet = user.get_wallet(wallet_id) - if not wallet: + userwallet = user.get_wallet(wallet_id) + if not userwallet: return template_renderer().TemplateResponse( "error.html", {"request": request, "err": "Wallet not found"} ) @@ -163,7 +163,7 @@ async def wallet( { "request": request, "user": user.dict(), - "wallet": wallet.dict(), + "wallet": userwallet.dict(), "service_fee": service_fee, "web_manifest": f"/manifest/{user.id}.webmanifest", }, From 5779243d3efcde9b9b4c1d3eb61343474922d073 Mon Sep 17 00:00:00 2001 From: dni Date: Mon, 25 Jul 2022 13:30:45 +0200 Subject: [PATCH 51/63] blacked --- lnbits/core/views/api.py | 2 +- lnbits/core/views/generic.py | 17 ++++++++--------- lnbits/decorators.py | 12 ++++++------ 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 59ce61a9d..97dff480d 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -261,7 +261,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet): ) async def api_payments_create( wallet: WalletTypeInfo = Depends(require_invoice_key), - invoiceData: CreateInvoiceData = Body(...), #type: ignore + invoiceData: CreateInvoiceData = Body(...), # type: ignore ): if invoiceData.out is True and wallet.wallet_type == 0: if not invoiceData.bolt11: diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 0d8a7b0e4..8b9437166 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -55,9 +55,9 @@ async def home(request: Request, lightning: str = None): ) async def extensions( request: Request, - user: User = Depends(check_user_exists), #type: ignore - enable: str = Query(None), #type: ignore - disable: str = Query(None), #type: ignore + user: User = Depends(check_user_exists), # type: ignore + enable: str = Query(None), # type: ignore + disable: str = Query(None), # type: ignore ): extension_to_enable = enable extension_to_disable = disable @@ -88,7 +88,7 @@ async def extensions( # Update user as his extensions have been updated if extension_to_enable or extension_to_disable: - user = await get_user(user.id) #type: ignore + user = await get_user(user.id) # type: ignore return template_renderer().TemplateResponse( "core/extensions.html", {"request": request, "user": user.dict()} @@ -108,12 +108,11 @@ just **user_id**: return the first user wallet or create one if none found (with nothing: create everything
""", ) - async def wallet( - request: Request = Query(None), #type: ignore - nme: Optional[str] = Query(None), #type: ignore - usr: Optional[UUID4] = Query(None), #type: ignore - wal: Optional[UUID4] = Query(None), #type: ignore + request: Request = Query(None), # type: ignore + nme: Optional[str] = Query(None), # type: ignore + usr: Optional[UUID4] = Query(None), # type: ignore + wal: Optional[UUID4] = Query(None), # type: ignore ): user_id = usr.hex if usr else None wallet_id = wal.hex if wal else None diff --git a/lnbits/decorators.py b/lnbits/decorators.py index b65c43a1c..e95e53f16 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -122,8 +122,8 @@ api_key_query = APIKeyQuery( async def get_key_type( r: Request, - api_key_header: str = Security(api_key_header), #type: ignore - api_key_query: str = Security(api_key_query) #type: ignore + api_key_header: str = Security(api_key_header), # type: ignore + api_key_query: str = Security(api_key_query), # type: ignore ) -> WalletTypeInfo: # 0: admin # 1: invoice @@ -180,8 +180,8 @@ async def get_key_type( async def require_admin_key( r: Request, - api_key_header: str = Security(api_key_header), #type: ignore - api_key_query: str = Security(api_key_query) #type: ignore + api_key_header: str = Security(api_key_header), # type: ignore + api_key_query: str = Security(api_key_query), # type: ignore ): token = api_key_header if api_key_header else api_key_query @@ -199,8 +199,8 @@ async def require_admin_key( async def require_invoice_key( r: Request, - api_key_header: str = Security(api_key_header), #type: ignore - api_key_query: str = Security(api_key_query) #type: ignore + api_key_header: str = Security(api_key_header), # type: ignore + api_key_query: str = Security(api_key_query), # type: ignore ): token = api_key_header if api_key_header else api_key_query From 5810a4993b6bb90107aa07146476e0a557f08721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 26 Jul 2022 09:08:16 +0200 Subject: [PATCH 52/63] Update lnbits/core/views/api.py Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> --- lnbits/core/views/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 97dff480d..3fc82382e 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -308,7 +308,7 @@ async def api_payments_pay_lnurl( timeout=40, ) if r.is_error: - raise httpx.ConnectError("LNURL Callback Connection Error") + raise httpx.ConnectError("LNURL callback connection error") except (httpx.ConnectError, httpx.RequestError): raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, From e4078910c43c87dc58a91cd8c250e526db57471d Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 26 Jul 2022 09:38:31 +0200 Subject: [PATCH 53/63] fix another fastapi type --- lnbits/core/views/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index 8b9437166..e9ef38778 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -218,7 +218,7 @@ async def lnurl_full_withdraw_callback(request: Request): @core_html_routes.get("/deletewallet", response_class=RedirectResponse) -async def deletewallet(request: Request, wal=Query(...), usr=Query(...)): +async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): # type: ignore user = await get_user(usr) assert user is not None user_wallet_ids = [u.id for u in user.wallets] From 345cf8654432faae7600c44fe24304d2aeddbf29 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 26 Jul 2022 09:52:57 +0200 Subject: [PATCH 54/63] fix another fastapi type --- lnbits/wallets/macaroon/macaroon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py index aa00cd3e6..84c4909d1 100644 --- a/lnbits/wallets/macaroon/macaroon.py +++ b/lnbits/wallets/macaroon/macaroon.py @@ -73,7 +73,7 @@ class AESCipher(object): final_key += key return final_key[:output] - def decrypt(self, encrypted) -> str: + def decrypt(self, encrypted: str) -> str: #type: ignore """Decrypts a string using AES-256-CBC.""" passphrase = self.passphrase encrypted = base64.b64decode(encrypted) From 1c543e3a848f514cc548116897002e8046ea4b79 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 26 Jul 2022 09:56:46 +0200 Subject: [PATCH 55/63] ignore macaroon type errors --- lnbits/wallets/macaroon/macaroon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py index 84c4909d1..1c69ca0b1 100644 --- a/lnbits/wallets/macaroon/macaroon.py +++ b/lnbits/wallets/macaroon/macaroon.py @@ -76,7 +76,7 @@ class AESCipher(object): def decrypt(self, encrypted: str) -> str: #type: ignore """Decrypts a string using AES-256-CBC.""" passphrase = self.passphrase - encrypted = base64.b64decode(encrypted) + encrypted = base64.b64decode(encrypted) #type: ignore assert encrypted[0:8] == b"Salted__" salt = encrypted[8:16] key_iv = self.bytes_to_key(passphrase.encode(), salt, 32 + 16) @@ -84,7 +84,7 @@ class AESCipher(object): iv = key_iv[32:] aes = AES.new(key, AES.MODE_CBC, iv) try: - return self.unpad(aes.decrypt(encrypted[16:])).decode() + return self.unpad(aes.decrypt(encrypted[16:])).decode() #type: ignore except UnicodeDecodeError: raise ValueError("Wrong passphrase") From 27ce17cd74534d59ef68286a1fcae0ffb89c3f14 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 26 Jul 2022 10:10:07 +0200 Subject: [PATCH 56/63] blacked --- lnbits/wallets/macaroon/macaroon.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py index 1c69ca0b1..091551238 100644 --- a/lnbits/wallets/macaroon/macaroon.py +++ b/lnbits/wallets/macaroon/macaroon.py @@ -73,10 +73,10 @@ class AESCipher(object): final_key += key return final_key[:output] - def decrypt(self, encrypted: str) -> str: #type: ignore + def decrypt(self, encrypted: str) -> str: # type: ignore """Decrypts a string using AES-256-CBC.""" passphrase = self.passphrase - encrypted = base64.b64decode(encrypted) #type: ignore + encrypted = base64.b64decode(encrypted) # type: ignore assert encrypted[0:8] == b"Salted__" salt = encrypted[8:16] key_iv = self.bytes_to_key(passphrase.encode(), salt, 32 + 16) @@ -84,7 +84,7 @@ class AESCipher(object): iv = key_iv[32:] aes = AES.new(key, AES.MODE_CBC, iv) try: - return self.unpad(aes.decrypt(encrypted[16:])).decode() #type: ignore + return self.unpad(aes.decrypt(encrypted[16:])).decode() # type: ignore except UnicodeDecodeError: raise ValueError("Wrong passphrase") From e63c4c9fa29e8d286ff7b56273bdf01a0ea14d74 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 26 Jul 2022 12:07:16 +0200 Subject: [PATCH 57/63] revert self.wallet change --- lnbits/decorators.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lnbits/decorators.py b/lnbits/decorators.py index e95e53f16..3a67e9439 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -53,13 +53,12 @@ class KeyChecker(SecurityBase): # FIXME: Find another way to validate the key. A fetch from DB should be avoided here. # Also, we should not return the wallet here - thats silly. # Possibly store it in a Redis DB - wallet = await get_wallet_for_key(key_value, self._key_type) - if not wallet: + self.wallet = await get_wallet_for_key(key_value, self._key_type) + if not self.wallet: raise HTTPException( status_code=HTTPStatus.UNAUTHORIZED, detail="Invalid key or expired key.", ) - self.wallet = wallet except KeyError: raise HTTPException( From 00dba54ac88740513fa467f07194e0674baf13e9 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 26 Jul 2022 12:21:21 +0200 Subject: [PATCH 58/63] remove FIXME add couple of ignores --- lnbits/core/services.py | 3 --- lnbits/core/views/api.py | 23 ----------------------- lnbits/decorators.py | 16 +++++++--------- 3 files changed, 7 insertions(+), 35 deletions(-) diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 2416ed315..b9aec8997 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -270,9 +270,6 @@ async def perform_lnurlauth( k1 = unhexlify(parse_qs(cb.query)["k1"][0]) - # FIXME: wallet.wallet can be None here - assert wallet.wallet is not None - key = wallet.wallet.lnurlauth_key(cb.netloc) def int_to_bytes_suitable_der(x: int) -> bytes: diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 3fc82382e..d63e802db 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -56,16 +56,12 @@ from ..tasks import api_invoice_listeners @core_app.get("/api/v1/wallet") async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)): if wallet.wallet_type == 0: - # FIXME: wallet.wallet can be None here - assert wallet.wallet is not None return { "id": wallet.wallet.id, "name": wallet.wallet.name, "balance": wallet.wallet.balance_msat, } else: - # FIXME: wallet.wallet can be None here - assert wallet.wallet is not None return {"name": wallet.wallet.name, "balance": wallet.wallet.balance_msat} @@ -73,9 +69,6 @@ async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)): async def api_update_balance( amount: int, wallet: WalletTypeInfo = Depends(get_key_type) ): - # FIXME: wallet.wallet can be None here - assert wallet.wallet is not None - if wallet.wallet.user not in LNBITS_ADMIN_USERS: raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user" @@ -105,9 +98,6 @@ async def api_update_balance( async def api_update_wallet( new_name: str, wallet: WalletTypeInfo = Depends(require_admin_key) ): - # FIXME: wallet.wallet can be None here - assert wallet.wallet is not None - await update_wallet(wallet.wallet.id, new_name) return { "id": wallet.wallet.id, @@ -122,9 +112,6 @@ async def api_payments( offset: Optional[int] = None, wallet: WalletTypeInfo = Depends(get_key_type), ): - # FIXME: wallet.wallet can be None here - assert wallet.wallet is not None - pendingPayments = await get_payments( wallet_id=wallet.wallet.id, pending=True, @@ -269,15 +256,11 @@ async def api_payments_create( status_code=HTTPStatus.BAD_REQUEST, detail="BOLT11 string is invalid or not given", ) - # FIXME: wallet.wallet can be None here - assert wallet.wallet is not None return await api_payments_pay_invoice( invoiceData.bolt11, wallet.wallet ) # admin key elif not invoiceData.out: # invoice key - # FIXME: wallet.wallet can be None here - assert wallet.wallet is not None return await api_payments_create_invoice(invoiceData, wallet.wallet) else: raise HTTPException( @@ -342,8 +325,6 @@ async def api_payments_pay_lnurl( if data.comment: extra["comment"] = data.comment assert data.description is not None, "description is required" - # FIXME: wallet.wallet can be None here - assert wallet.wallet is not None payment_hash = await pay_invoice( wallet_id=wallet.wallet.id, payment_request=params["pr"], @@ -397,8 +378,6 @@ async def subscribe(request: Request, wallet: Wallet): async def api_payments_sse( request: Request, wallet: WalletTypeInfo = Depends(get_key_type) ): - # FIXME: wallet.wallet can be None here - assert wallet.wallet is not None return EventSourceResponse( subscribe(request, wallet.wallet), ping=20, media_type="text/event-stream" ) @@ -477,8 +456,6 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type params.update(kind="auth") params.update(callback=url) # with k1 already in it - # FIXME: wallet.wallet can be None here - assert wallet.wallet is not None lnurlauth_key = wallet.wallet.lnurlauth_key(domain) params.update(pubkey=lnurlauth_key.verifying_key.to_string("compressed").hex()) else: diff --git a/lnbits/decorators.py b/lnbits/decorators.py index 3a67e9439..6685cfb2d 100644 --- a/lnbits/decorators.py +++ b/lnbits/decorators.py @@ -41,6 +41,7 @@ class KeyChecker(SecurityBase): name="X-API-KEY", description="Wallet API Key - HEADER", ) + self.wallet = None # type: ignore self.model: APIKey = key async def __call__(self, request: Request): @@ -53,7 +54,7 @@ class KeyChecker(SecurityBase): # FIXME: Find another way to validate the key. A fetch from DB should be avoided here. # Also, we should not return the wallet here - thats silly. # Possibly store it in a Redis DB - self.wallet = await get_wallet_for_key(key_value, self._key_type) + self.wallet = await get_wallet_for_key(key_value, self._key_type) # type: ignore if not self.wallet: raise HTTPException( status_code=HTTPStatus.UNAUTHORIZED, @@ -100,9 +101,9 @@ class WalletAdminKeyChecker(KeyChecker): class WalletTypeInfo: wallet_type: int - wallet: Union[Wallet, None] + wallet: Wallet - def __init__(self, wallet_type: int, wallet: Union[Wallet, None]) -> None: + def __init__(self, wallet_type: int, wallet: Wallet) -> None: self.wallet_type = wallet_type self.wallet = wallet @@ -137,8 +138,7 @@ async def get_key_type( try: admin_checker = WalletAdminKeyChecker(api_key=token) await admin_checker.__call__(r) - wallet = WalletTypeInfo(0, admin_checker.wallet) - assert wallet.wallet is not None + wallet = WalletTypeInfo(0, admin_checker.wallet) # type: ignore if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and ( LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS ): @@ -157,9 +157,7 @@ async def get_key_type( try: invoice_checker = WalletInvoiceKeyChecker(api_key=token) await invoice_checker.__call__(r) - wallet = WalletTypeInfo(1, invoice_checker.wallet) - # FIXME: wallet.wallet can be None here - assert wallet.wallet is not None + wallet = WalletTypeInfo(1, invoice_checker.wallet) # type: ignore if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and ( LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS ): @@ -171,7 +169,7 @@ async def get_key_type( if e.status_code == HTTPStatus.BAD_REQUEST: raise if e.status_code == HTTPStatus.UNAUTHORIZED: - return WalletTypeInfo(2, None) + return WalletTypeInfo(2, None) # type: ignore except: raise return wallet From 7667c2f5318286fd54271dbe4be62fc62e7a1308 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 26 Jul 2022 12:31:01 +0200 Subject: [PATCH 59/63] doesnt touch class invoice and leave fake wallet as is --- lnbits/bolt11.py | 1 - lnbits/wallets/fake.py | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lnbits/bolt11.py b/lnbits/bolt11.py index cc8c60401..cc8415852 100644 --- a/lnbits/bolt11.py +++ b/lnbits/bolt11.py @@ -23,7 +23,6 @@ class Route(NamedTuple): class Invoice(object): payment_hash: str - checking_id: Optional[str] = None amount_msat: int = 0 description: Optional[str] = None description_hash: Optional[str] = None diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py index ba2956c58..3126ee46a 100644 --- a/lnbits/wallets/fake.py +++ b/lnbits/wallets/fake.py @@ -80,11 +80,9 @@ class FakeWallet(Wallet): async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: invoice = decode(bolt11) - # TODO: no data here? - data: Dict = {"privkey": "missing"} if ( - invoice.checking_id is not None - and invoice.checking_id[6:] == data["privkey"][:6] + hasattr(invoice, "checking_id") + and invoice.checking_id[6:] == data["privkey"][:6] # type: ignore ): return PaymentResponse(True, invoice.payment_hash, 0) else: From 36cb1eb47befe8a5a8631bb263b6b85a2a8b3bc8 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 26 Jul 2022 12:46:43 +0200 Subject: [PATCH 60/63] fix int type by changing the order --- lnbits/core/views/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index d63e802db..627d3aed3 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -603,8 +603,8 @@ class ConversionData(BaseModel): async def api_fiat_as_sats(data: ConversionData): output = {} if data.from_ == "sat": - output["sats"] = data.amount output["BTC"] = data.amount / 100000000 + output["sats"] = int(data.amount) for currency in data.to.split(","): output[currency.strip().upper()] = await satoshis_amount_as_fiat( data.amount, currency.strip() From 7da496dbb4a13f7924f812dda82cd3c50a197a50 Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 27 Jul 2022 09:39:52 +0200 Subject: [PATCH 61/63] remove asserts and add ignore instead for now --- lnbits/core/tasks.py | 3 +-- lnbits/core/views/generic.py | 32 ++++++++++++++------------------ 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py index 8234b5fc4..07b8a8933 100644 --- a/lnbits/core/tasks.py +++ b/lnbits/core/tasks.py @@ -55,8 +55,7 @@ async def dispatch_webhook(payment: Payment): data = payment.dict() try: logger.debug("sending webhook", payment.webhook) - assert payment.webhook is not None - r = await client.post(payment.webhook, json=data, timeout=40) + r = await client.post(payment.webhook, json=data, timeout=40) # type: ignore await mark_webhook_sent(payment, r.status_code) except (httpx.ConnectError, httpx.RequestError): await mark_webhook_sent(payment, -1) diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index e9ef38778..21572b285 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -121,11 +121,9 @@ async def wallet( if not user_id: user = await get_user((await create_account()).id) - assert user is not None - logger.info(f"Create user {user.id}") + logger.info(f"Create user {user.id}") # type: ignore else: user = await get_user(user_id) - assert user is not None if not user: return template_renderer().TemplateResponse( "error.html", {"request": request, "err": "User does not exist."} @@ -137,21 +135,21 @@ async def wallet( if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS: user.admin = True if not wallet_id: - if user.wallets and not wallet_name: - wallet = user.wallets[0] + if user.wallets and not wallet_name: # type: ignore + wallet = user.wallets[0] # type: ignore else: - wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name) + wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name) # type: ignore logger.info( - f"Created new wallet {wallet_name if wallet_name else '(no name)'} for user {user.id}" + f"Created new wallet {wallet_name if wallet_name else '(no name)'} for user {user.id}" # type: ignore ) return RedirectResponse( - f"/wallet?usr={user.id}&wal={wallet.id}", + f"/wallet?usr={user.id}&wal={wallet.id}", # type: ignore status_code=status.HTTP_307_TEMPORARY_REDIRECT, ) logger.debug(f"Access wallet {wallet_name}{'of user '+ user.id if user else ''}") - userwallet = user.get_wallet(wallet_id) + userwallet = user.get_wallet(wallet_id) # type: ignore if not userwallet: return template_renderer().TemplateResponse( "error.html", {"request": request, "err": "Wallet not found"} @@ -161,10 +159,10 @@ async def wallet( "core/wallet.html", { "request": request, - "user": user.dict(), + "user": user.dict(), # type: ignore "wallet": userwallet.dict(), "service_fee": service_fee, - "web_manifest": f"/manifest/{user.id}.webmanifest", + "web_manifest": f"/manifest/{user.id}.webmanifest", # type: ignore }, ) @@ -220,19 +218,18 @@ async def lnurl_full_withdraw_callback(request: Request): @core_html_routes.get("/deletewallet", response_class=RedirectResponse) async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): # type: ignore user = await get_user(usr) - assert user is not None - user_wallet_ids = [u.id for u in user.wallets] + user_wallet_ids = [u.id for u in user.wallets] # type: ignore if wal not in user_wallet_ids: raise HTTPException(HTTPStatus.FORBIDDEN, "Not your wallet.") else: - await delete_wallet(user_id=user.id, wallet_id=wal) + await delete_wallet(user_id=user.id, wallet_id=wal) # type: ignore user_wallet_ids.remove(wal) logger.debug("Deleted wallet {wal} of user {user.id}") if user_wallet_ids: return RedirectResponse( - url_for("/wallet", usr=user.id, wal=user_wallet_ids[0]), + url_for("/wallet", usr=user.id, wal=user_wallet_ids[0]), # type: ignore status_code=status.HTTP_307_TEMPORARY_REDIRECT, ) @@ -255,8 +252,7 @@ async def lnurlwallet(request: Request): async with db.connect() as conn: account = await create_account(conn=conn) user = await get_user(account.id, conn=conn) - assert user is not None - wallet = await create_wallet(user_id=user.id, conn=conn) + wallet = await create_wallet(user_id=user.id, conn=conn) # type: ignore asyncio.create_task( redeem_lnurl_withdraw( @@ -269,7 +265,7 @@ async def lnurlwallet(request: Request): ) return RedirectResponse( - f"/wallet?usr={user.id}&wal={wallet.id}", + f"/wallet?usr={user.id}&wal={wallet.id}", # type: ignore status_code=status.HTTP_307_TEMPORARY_REDIRECT, ) From c72834e47ea0611764753cf4ac26da5a509095aa Mon Sep 17 00:00:00 2001 From: dni Date: Wed, 27 Jul 2022 09:50:16 +0200 Subject: [PATCH 62/63] revert attribute check --- lnbits/core/views/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 3ef92ea60..b1366b077 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -181,7 +181,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet): lnurl_response: Union[None, bool, str] = None if data.lnurl_callback: - if hasattr(data, "lnurl_balance_check"): + if "lnurl_balance_check" in data: assert ( data.lnurl_balance_check is not None ), "lnurl_balance_check is required" From 354fedc0b8ca9e5cb27ff085d2b67d30fe09498e Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 27 Jul 2022 20:16:32 +0100 Subject: [PATCH 63/63] prettier/black --- lnbits/extensions/events/views_api.py | 3 ++- .../lnurlpayout/templates/lnurlpayout/_api_docs.html | 8 +------- result | 1 + 3 files changed, 4 insertions(+), 8 deletions(-) create mode 120000 result diff --git a/lnbits/extensions/events/views_api.py b/lnbits/extensions/events/views_api.py index 45ee4de05..56e6b06ca 100644 --- a/lnbits/extensions/events/views_api.py +++ b/lnbits/extensions/events/views_api.py @@ -133,7 +133,8 @@ async def api_ticket_send_ticket(event_id, payment_hash, data: CreateTicket): if not ticket: raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail=f"Event could not be fetched." + status_code=HTTPStatus.NOT_FOUND, + detail=f"Event could not be fetched.", ) return {"paid": True, "ticket_id": ticket.id} diff --git a/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html b/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html index 4f921bb57..afe24c423 100644 --- a/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html +++ b/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html @@ -4,12 +4,7 @@ label="API info" :content-inset-level="0.5" > - + @@ -38,7 +33,6 @@ expand-separator label="Create a lnurlpayout" > -