From a6c84ceb6117d14a1352cf8861a65e9e4f4c7863 Mon Sep 17 00:00:00 2001 From: benarc Date: Sat, 16 Apr 2022 11:14:34 +0100 Subject: [PATCH 01/78] Geberic QRcode maker Added generic qrcode maker endpoint extensions can use to make embedable qrcodes --- lnbits/core/views/api.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 0e88b5c8..b147c212 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -7,6 +7,7 @@ from typing import Dict, List, Optional, Union from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx +import pyqrcode from fastapi import Query, Request, Header from fastapi.exceptions import HTTPException from fastapi.param_functions import Depends @@ -580,3 +581,23 @@ async def api_fiat_as_sats(data: ConversionData): output["sats"] = await fiat_amount_as_satoshis(data.amount, data.from_) output["BTC"] = output["sats"] / 100000000 return output + +@withdraw_ext.get("/api/v1/qrcode/{data}", response_class=StreamingResponse) +async def img(request: Request, data): + qr = pyqrcode.create(data) + stream = BytesIO() + qr.svg(stream, scale=3) + stream.seek(0) + + async def _generator(stream: BytesIO): + yield stream.getvalue() + + return StreamingResponse( + _generator(stream), + headers={ + "Content-Type": "image/svg+xml", + "Cache-Control": "no-cache, no-store, must-revalidate", + "Pragma": "no-cache", + "Expires": "0", + }, + ) From c7322fd071db545e88078d7e1dbc0ca33b68564a Mon Sep 17 00:00:00 2001 From: benarc Date: Sat, 16 Apr 2022 11:15:42 +0100 Subject: [PATCH 02/78] correct route --- 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 b147c212..aca5a458 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -582,7 +582,7 @@ async def api_fiat_as_sats(data: ConversionData): output["BTC"] = output["sats"] / 100000000 return output -@withdraw_ext.get("/api/v1/qrcode/{data}", response_class=StreamingResponse) +@core_app.get("/api/v1/qrcode/{data}", response_class=StreamingResponse) async def img(request: Request, data): qr = pyqrcode.create(data) stream = BytesIO() From b5703e0dfc43ab9013070a433ca8e41e245ec084 Mon Sep 17 00:00:00 2001 From: benarc Date: Sat, 16 Apr 2022 11:17:03 +0100 Subject: [PATCH 03/78] added dependency --- lnbits/core/views/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index aca5a458..9cb005fe 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -8,6 +8,7 @@ from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx import pyqrcode +from starlette.responses import HTMLResponse, StreamingResponse from fastapi import Query, Request, Header from fastapi.exceptions import HTTPException from fastapi.param_functions import Depends From 829e220096ed7696d5aa87a7cd37c88c6c1ae01a Mon Sep 17 00:00:00 2001 From: benarc Date: Sat, 16 Apr 2022 11:18:27 +0100 Subject: [PATCH 04/78] another dependency --- lnbits/core/views/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 9cb005fe..4123a70b 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -8,6 +8,7 @@ from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx import pyqrcode +from io import BytesIO from starlette.responses import HTMLResponse, StreamingResponse from fastapi import Query, Request, Header from fastapi.exceptions import HTTPException From 83a000d7c9795b81fbe88f9d444c27bc0d20ad40 Mon Sep 17 00:00:00 2001 From: dni Date: Tue, 19 Jul 2022 13:06:19 +0200 Subject: [PATCH 05/78] 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 735f89e0..e5a974b5 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 8359456f..322712f5 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 06/78] 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 a7c8fdaf..eaf8a9db 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 07/78] 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 770e2906..db802d7b 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 ab73b702..0f7eba73 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 9fee6063..bd15ee8d 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 4366028d..d75aa77e 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 71b3dd69..e97fc7bb 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 86863f98..f4d0a928 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 0432b364..fbb4add2 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 807d7253..2ff1afa9 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 6d3fb02c..9cd05ebd 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 08/78] 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 cc841585..cc8c6040 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 3859d33c..c2b2307a 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 2183dacb..aa00cd3e 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 09/78] 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 5fea769d..8234b5fc 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 10/78] 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 d75aa77e..7ecedcf8 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 e65b9041..77fe3227 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 0ac3fd2a..bad707ff 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 11/78] 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 0b565ebb..f4f96250 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 12/78] 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 bd15ee8d..09eb4f90 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 bad707ff..b669f8b7 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 13/78] 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 77fe3227..b74230ef 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 14/78] 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 b669f8b7..b43d90c1 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 15/78] 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 09eb4f90..6766ad8d 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 16/78] 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 6766ad8d..95e9dde3 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 7ecedcf8..00d321af 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 b74230ef..ef689f8d 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 b43d90c1..12289517 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 c2b2307a..ba2956c5 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 17/78] 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 10e659aa..1f858870 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 18/78] 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 322712f5..8ee1fc6c 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 19/78] 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 8ef241f1..60c79292 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 8ee1fc6c..3603ab03 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 20/78] 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 95e9dde3..c81f623a 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 1f858870..01427cba 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 21/78] 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 ef689f8d..adfbd664 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 22/78] 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 c81f623a..d7be3e55 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 adfbd664..b62e456a 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 fbb4add2..2801146b 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 12289517..1ba81385 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 23/78] 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 01427cba..d5eeeeae 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 24/78] 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 d5eeeeae..61a71d31 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 25/78] add missing Pipfile.lock --- Pipfile.lock | 168 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 132 insertions(+), 36 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 6a89abb3..2e6d3a14 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 26/78] 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 4d6c6d4d..bf90a8e3 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 27/78] 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 f4f96250..2416ed31 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 d7be3e55..a3a59a5f 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 b62e456a..9f51ce64 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 28/78] 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 735f89e0..e5a974b5 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 f8ccf47c..512011ec 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 29/78] 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 a7c8fdaf..eaf8a9db 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 30/78] 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 770e2906..db802d7b 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 ab73b702..0f7eba73 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 9fee6063..bd15ee8d 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 44666ce1..7ef306dc 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 71b3dd69..e97fc7bb 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 86863f98..f4d0a928 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 0432b364..fbb4add2 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 807d7253..2ff1afa9 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 6d3fb02c..9cd05ebd 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 31/78] 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 cc841585..cc8c6040 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 3859d33c..c2b2307a 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 2183dacb..aa00cd3e 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 32/78] 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 5fea769d..8234b5fc 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 33/78] 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 7ef306dc..5f44131f 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 e65b9041..77fe3227 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 0ac3fd2a..bad707ff 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 34/78] 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 0b565ebb..f4f96250 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 35/78] 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 bd15ee8d..09eb4f90 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 bad707ff..b669f8b7 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 36/78] 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 77fe3227..b74230ef 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 37/78] 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 b669f8b7..b43d90c1 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 38/78] 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 09eb4f90..6766ad8d 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 39/78] 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 6766ad8d..95e9dde3 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 5f44131f..01bc47b6 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 b74230ef..ef689f8d 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 b43d90c1..12289517 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 c2b2307a..ba2956c5 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 40/78] 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 6a5f82ec..949d2052 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 41/78] 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 512011ec..edcd737d 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 42/78] 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 f8c42a9d..a0cef805 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 edcd737d..f8ccf47c 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 43/78] 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 95e9dde3..c81f623a 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 949d2052..7c1b134d 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 44/78] 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 ef689f8d..adfbd664 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 45/78] 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 c81f623a..d7be3e55 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 adfbd664..b62e456a 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 fbb4add2..2801146b 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 12289517..1ba81385 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 46/78] add missing Pipfile.lock --- Pipfile.lock | 224 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 145 insertions(+), 79 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 42d471c6..2704d415 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 47/78] 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 7c1b134d..cfbd7710 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 48/78] 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 cfbd7710..dfd2b32a 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 49/78] 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 4d6c6d4d..bf90a8e3 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 50/78] 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 f4f96250..2416ed31 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 d7be3e55..a3a59a5f 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 b62e456a..9f51ce64 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 51/78] 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 a3a59a5f..59ce61a9 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 52/78] 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 bf90a8e3..71713d88 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 01bc47b6..433c6f62 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 9f51ce64..b65c43a1 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 53/78] 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 433c6f62..3058d115 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 54/78] 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 3058d115..0d8a7b0e 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 55/78] 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 59ce61a9..97dff480 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 0d8a7b0e..8b943716 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 b65c43a1..e95e53f1 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 56/78] 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 97dff480..3fc82382 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 57/78] 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 8b943716..e9ef3877 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 58/78] 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 aa00cd3e..84c4909d 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 59/78] 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 84c4909d..1c69ca0b 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 60/78] 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 1c69ca0b..09155123 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 61/78] 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 e95e53f1..3a67e943 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 62/78] 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 2416ed31..b9aec899 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 3fc82382..d63e802d 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 3a67e943..6685cfb2 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 63/78] 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 cc8c6040..cc841585 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 ba2956c5..3126ee46 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 64/78] 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 d63e802d..627d3aed 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 65/78] 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 8234b5fc..07b8a893 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 e9ef3877..21572b28 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 66/78] 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 3ef92ea6..b1366b07 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 67/78] 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 45ee4de0..56e6b06c 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 4f921bb5..afe24c42 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" > - Date: Thu, 28 Jul 2022 09:30:52 +0100 Subject: [PATCH 68/78] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0f9df62..5c6b702a 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ LNbits ![Lightning network wallet](https://i.imgur.com/EHvK6Lq.png) -# LNbits v0.3 BETA, free and open-source lightning-network wallet/accounts system +# LNbits v0.9 BETA, free and open-source lightning-network wallet/accounts system (Join us on [https://t.me/lnbits](https://t.me/lnbits)) From 783502ebf805efe188a216a8eba7413e5cc499d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 28 Jul 2022 11:09:03 +0200 Subject: [PATCH 69/78] faster regtest, docker build is not needed anymore (#794) * docker build is not needed anymore as regtest now does not include lnbits * faster regtest, because docker build -t lnbits . is not needed anymore Co-authored-by: dni --- .github/workflows/regtest.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml index f26e6c38..fb114ff8 100644 --- a/.github/workflows/regtest.yml +++ b/.github/workflows/regtest.yml @@ -16,9 +16,9 @@ jobs: python-version: ${{ matrix.python-version }} - name: Setup Regtest run: | - docker build -t lnbits-legend . git clone https://github.com/lnbits/legend-regtest-enviroment.git docker cd docker + git checkout removelnbits chmod +x ./tests ./tests sudo chmod -R a+rwx . @@ -39,8 +39,8 @@ jobs: LNBITS_DATA_FOLDER: ./data LNBITS_BACKEND_WALLET_CLASS: LndRestWallet LND_REST_ENDPOINT: https://localhost:8081/ - LND_REST_CERT: docker/data/lnd-1/tls.cert - LND_REST_MACAROON: docker/data/lnd-1/data/chain/bitcoin/regtest/admin.macaroon + LND_REST_CERT: ./docker/data/lnd-1/tls.cert + LND_REST_MACAROON: ./docker/data/lnd-1/data/chain/bitcoin/regtest/admin.macaroon run: | sudo chmod -R a+rwx . && rm -rf ./data && mkdir -p ./data make test-real-wallet @@ -57,9 +57,9 @@ jobs: python-version: ${{ matrix.python-version }} - name: Setup Regtest run: | - docker build -t lnbits-legend . git clone https://github.com/lnbits/legend-regtest-enviroment.git docker cd docker + git checkout removelnbits chmod +x ./tests ./tests sudo chmod -R a+rwx . @@ -79,7 +79,7 @@ jobs: PORT: 5123 LNBITS_DATA_FOLDER: ./data LNBITS_BACKEND_WALLET_CLASS: CLightningWallet - CLIGHTNING_RPC: docker/data/clightning-1/regtest/lightning-rpc + CLIGHTNING_RPC: ./docker/data/clightning-1/regtest/lightning-rpc run: | sudo chmod -R a+rwx . && rm -rf ./data && mkdir -p ./data make test-real-wallet From 3ec9cb6c893ca3f965744c797408676f43ed6df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 28 Jul 2022 11:11:54 +0200 Subject: [PATCH 70/78] use main branch of regtest --- .github/workflows/regtest.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml index fb114ff8..0d9afdac 100644 --- a/.github/workflows/regtest.yml +++ b/.github/workflows/regtest.yml @@ -18,7 +18,6 @@ jobs: run: | git clone https://github.com/lnbits/legend-regtest-enviroment.git docker cd docker - git checkout removelnbits chmod +x ./tests ./tests sudo chmod -R a+rwx . @@ -59,7 +58,6 @@ jobs: run: | git clone https://github.com/lnbits/legend-regtest-enviroment.git docker cd docker - git checkout removelnbits chmod +x ./tests ./tests sudo chmod -R a+rwx . From 730add511697073800b78be5573786da953cc433 Mon Sep 17 00:00:00 2001 From: calle <93376500+callebtc@users.noreply.github.com> Date: Thu, 28 Jul 2022 11:53:40 +0200 Subject: [PATCH 71/78] Core/tag check get (#797) * safer tag checking * annotate --- lnbits/core/views/api.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index b1366b07..623b979a 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -485,7 +485,8 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type ) try: - tag = data["tag"] + tag: str = data.get("tag") + params.update(**data) if tag == "channelRequest": raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, @@ -495,10 +496,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type "message": "unsupported", }, ) - - params.update(**data) - - if tag == "withdrawRequest": + elif tag == "withdrawRequest": params.update(kind="withdraw") params.update(fixed=data["minWithdrawable"] == data["maxWithdrawable"]) @@ -516,8 +514,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type query=urlencode(qs, doseq=True) ) params.update(callback=urlunparse(parsed_callback)) - - if tag == "payRequest": + elif tag == "payRequest": params.update(kind="pay") params.update(fixed=data["minSendable"] == data["maxSendable"]) @@ -535,8 +532,8 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type params.update(image=data_uri) if k == "text/email" or k == "text/identifier": params.update(targetUser=v) - params.update(commentAllowed=data.get("commentAllowed", 0)) + except KeyError as exc: raise HTTPException( status_code=HTTPStatus.SERVICE_UNAVAILABLE, From 4199f1bf9842594e64dfd7889d9d63a1a2758219 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 28 Jul 2022 11:02:49 +0100 Subject: [PATCH 72/78] Make format --- lnbits/core/views/api.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 35806a2b..52b0837f 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -3,20 +3,20 @@ import hashlib import json from binascii import unhexlify from http import HTTPStatus +from io import BytesIO from typing import Dict, List, Optional, Tuple, Union from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse import httpx -from fastapi import Depends, Header, Query, Request import pyqrcode -from io import BytesIO -from starlette.responses import HTMLResponse, StreamingResponse +from fastapi import Depends, Header, Query, Request from fastapi.exceptions import HTTPException from fastapi.params import Body from loguru import logger from pydantic import BaseModel from pydantic.fields import Field from sse_starlette.sse import EventSourceResponse +from starlette.responses import HTMLResponse, StreamingResponse from lnbits import bolt11, lnurl from lnbits.core.models import Payment, Wallet @@ -625,6 +625,7 @@ async def api_fiat_as_sats(data: ConversionData): output["BTC"] = output["sats"] / 100000000 return output + @core_app.get("/api/v1/qrcode/{data}", response_class=StreamingResponse) async def img(request: Request, data): qr = pyqrcode.create(data) From 7f79b5ac7fba8c483f0db4125a369a5012a847a1 Mon Sep 17 00:00:00 2001 From: Arc <33088785+arcbtc@users.noreply.github.com> Date: Thu, 28 Jul 2022 11:06:42 +0100 Subject: [PATCH 73/78] Update installation.md --- docs/guide/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/installation.md b/docs/guide/installation.md index f38f606d..b8b8c219 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -90,7 +90,7 @@ If you want to host LNbits on the internet, run with the option `--host 0.0.0.0` git clone https://github.com/lnbits/lnbits-legend.git cd lnbits-legend/ # Install nix, modern debian distros usually already include -sh <(curl -L https://nixos.org/nix/install) --daemon +# otherwise run 'sh <(curl -L https://nixos.org/nix/install) --daemon', or use setup here https://nixos.org/download.html#nix-verify-installation nix build .#lnbits mkdir data From 04114c82f2961eafac315f72dba5f1603ebcc7a0 Mon Sep 17 00:00:00 2001 From: Arc <33088785+arcbtc@users.noreply.github.com> Date: Thu, 28 Jul 2022 11:07:44 +0100 Subject: [PATCH 74/78] Update installation.md --- docs/guide/installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide/installation.md b/docs/guide/installation.md index b8b8c219..782a0829 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -89,8 +89,8 @@ If you want to host LNbits on the internet, run with the option `--host 0.0.0.0` ```sh git clone https://github.com/lnbits/lnbits-legend.git cd lnbits-legend/ -# Install nix, modern debian distros usually already include -# otherwise run 'sh <(curl -L https://nixos.org/nix/install) --daemon', or use setup here https://nixos.org/download.html#nix-verify-installation +# Modern debian distros usually include Nix, however you can install with: +# 'sh <(curl -L https://nixos.org/nix/install) --daemon', or use setup here https://nixos.org/download.html#nix-verify-installation nix build .#lnbits mkdir data From a3a183b86c8b4f003e490b11e78e34fd2ea6e6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Thu, 28 Jul 2022 12:15:27 +0200 Subject: [PATCH 75/78] fix test warnings when using < python3.8 (#798) * jinja warning * fix regex Co-authored-by: dni --- lnbits/bolt11.py | 4 ++-- lnbits/jinja2_templating.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lnbits/bolt11.py b/lnbits/bolt11.py index cc841585..67039740 100644 --- a/lnbits/bolt11.py +++ b/lnbits/bolt11.py @@ -61,7 +61,7 @@ def decode(pr: str) -> Invoice: invoice = Invoice() # decode the amount from the hrp - m = re.search("[^\d]+", hrp[2:]) + m = re.search(r"[^\d]+", hrp[2:]) if m: amountstr = hrp[2 + m.end() :] if amountstr != "": @@ -296,7 +296,7 @@ def _unshorten_amount(amount: str) -> int: # BOLT #11: # A reader SHOULD fail if `amount` contains a non-digit, or is followed by # anything except a `multiplier` in the table above. - if not re.fullmatch("\d+[pnum]?", str(amount)): + if not re.fullmatch(r"\d+[pnum]?", str(amount)): raise ValueError("Invalid amount '{}'".format(amount)) if unit in units: diff --git a/lnbits/jinja2_templating.py b/lnbits/jinja2_templating.py index 5abcd0bf..385703d1 100644 --- a/lnbits/jinja2_templating.py +++ b/lnbits/jinja2_templating.py @@ -21,7 +21,7 @@ class Jinja2Templates(templating.Jinja2Templates): self.env = self.get_environment(loader) def get_environment(self, loader: "jinja2.BaseLoader") -> "jinja2.Environment": - @jinja2.contextfunction + @jinja2.pass_context def url_for(context: dict, name: str, **path_params: typing.Any) -> str: request: Request = context["request"] return request.app.url_path_for(name, **path_params) From eafefce709685e79407c8cbf5e22d09ad00379c1 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 28 Jul 2022 11:19:30 +0100 Subject: [PATCH 76/78] Bye pipenv Its not personal its just business --- Pipfile | 46 -- Pipfile.lock | 1224 ------------------------------------ docs/guide/installation.md | 52 +- 3 files changed, 11 insertions(+), 1311 deletions(-) delete mode 100644 Pipfile delete mode 100644 Pipfile.lock diff --git a/Pipfile b/Pipfile deleted file mode 100644 index a0cef805..00000000 --- a/Pipfile +++ /dev/null @@ -1,46 +0,0 @@ -[[source]] -name = "pypi" -url = "https://pypi.org/simple" -verify_ssl = true - -[requires] -python_version = "3.8" - -[packages] -bitstring = "*" -cerberus = "*" -ecdsa = "*" -environs = "*" -lnurl = "==0.3.6" -loguru = "*" -pyscss = "*" -shortuuid = "*" -typing-extensions = "*" -httpx = "*" -sqlalchemy-aio = "*" -embit = "*" -pyqrcode = "*" -pypng = "*" -sqlalchemy = "==1.3.23" -psycopg2-binary = "*" -aiofiles = "*" -asyncio = "*" -fastapi = "*" -uvicorn = {extras = ["standard"], version = "*"} -sse-starlette = "*" -jinja2 = "==3.0.1" -pyngrok = "*" -secp256k1 = "==0.14.0" -cffi = "==1.15.0" -pycryptodomex = "*" - -[dev-packages] -black = "==20.8b1" -mock = "*" -mypy = "*" -pytest = "*" -pytest-asyncio = "*" -pytest-cov = "*" -requests = "*" -types-mock = "*" -types-protobuf = "*" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 6a478e4c..00000000 --- a/Pipfile.lock +++ /dev/null @@ -1,1224 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "08e53fc6f1fcc021b33f9b2c5ae3bd6dbde815d4b317e9341ab02cf5b625acbc" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.8" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "aiofiles": { - "hashes": [ - "sha256:7a973fc22b29e9962d0897805ace5856e6a566ab1f0c8e5c91ff6c866519c937", - "sha256:8334f23235248a3b2e83b2c3a78a22674f39969b96397126cc93664d9a901e59" - ], - "index": "pypi", - "version": "==0.8.0" - }, - "anyio": { - "hashes": [ - "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b", - "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be" - ], - "markers": "python_full_version >= '3.6.2'", - "version": "==3.6.1" - }, - "asyncio": { - "hashes": [ - "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41", - "sha256:b62c9157d36187eca799c378e572c969f0da87cd5fc42ca372d92cdb06e7e1de", - "sha256:c46a87b48213d7464f22d9a497b9eef8c1928b68320a2fa94240f969f6fec08c", - "sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d" - ], - "index": "pypi", - "version": "==3.4.3" - }, - "attrs": { - "hashes": [ - "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": { - "hashes": [ - "sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899", - "sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981" - ], - "markers": "python_version >= '3.5'", - "version": "==1.2.0" - }, - "bitstring": { - "hashes": [ - "sha256:0de167daa6a00c9386255a7cac931b45e6e24e0ad7ea64f1f92a64ac23ad4578", - "sha256:a5848a3f63111785224dca8bb4c0a75b62ecdef56a042c8d6be74b16f7e860e7", - "sha256:e3e340e58900a948787a05e8c08772f1ccbe133f6f41fe3f0fa19a18a22bbf4f" - ], - "index": "pypi", - "version": "==3.1.9" - }, - "cerberus": { - "hashes": [ - "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c" - ], - "index": "pypi", - "version": "==1.3.4" - }, - "certifi": { - "hashes": [ - "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d", - "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412" - ], - "markers": "python_version >= '3.6'", - "version": "==2022.6.15" - }, - "cffi": { - "hashes": [ - "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3", - "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2", - "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636", - "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20", - "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728", - "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27", - "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66", - "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443", - "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0", - "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7", - "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39", - "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605", - "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a", - "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37", - "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029", - "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139", - "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc", - "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df", - "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14", - "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880", - "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2", - "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a", - "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e", - "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474", - "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024", - "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8", - "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0", - "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e", - "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a", - "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e", - "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032", - "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6", - "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e", - "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b", - "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e", - "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954", - "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962", - "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c", - "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4", - "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55", - "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962", - "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023", - "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c", - "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6", - "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8", - "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382", - "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7", - "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc", - "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997", - "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796" - ], - "index": "pypi", - "version": "==1.15.0" - }, - "click": { - "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.3" - }, - "ecdsa": { - "hashes": [ - "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49", - "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd" - ], - "index": "pypi", - "version": "==0.18.0" - }, - "embit": { - "hashes": [ - "sha256:5644ae6ed07bb71bf7fb15daf7f5af73d889180e623f5ff1f35a20ad01f0405e" - ], - "index": "pypi", - "version": "==0.5.0" - }, - "environs": { - "hashes": [ - "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124", - "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9" - ], - "index": "pypi", - "version": "==9.5.0" - }, - "fastapi": { - "hashes": [ - "sha256:cf0ff6db25b91d321050c4112baab0908c90f19b40bf257f9591d2f9780d1f22", - "sha256:d337563424ceada23857f73d5abe8dae0c28e4cccb53b2af06e78b7bb4a1c7d7" - ], - "index": "pypi", - "version": "==0.79.0" - }, - "h11": { - "hashes": [ - "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6", - "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042" - ], - "markers": "python_version >= '3.6'", - "version": "==0.12.0" - }, - "httpcore": { - "hashes": [ - "sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6", - "sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b" - ], - "markers": "python_version >= '3.7'", - "version": "==0.15.0" - }, - "httptools": { - "hashes": [ - "sha256:1a99346ebcb801b213c591540837340bdf6fd060a8687518d01c607d338b7424", - "sha256:1ee0b459257e222b878a6c09ccf233957d3a4dcb883b0847640af98d2d9aac23", - "sha256:20a45bcf22452a10fa8d58b7dbdb474381f6946bf5b8933e3662d572bc61bae4", - "sha256:29bf97a5c532da9c7a04de2c7a9c31d1d54f3abd65a464119b680206bbbb1055", - "sha256:2c9a930c378b3d15d6b695fb95ebcff81a7395b4f9775c4f10a076beb0b2c1ff", - "sha256:2db44a0b294d317199e9f80123e72c6b005c55b625b57fae36de68670090fa48", - "sha256:3194f6d6443befa8d4db16c1946b2fc428a3ceb8ab32eb6f09a59f86104dc1a0", - "sha256:34d2903dd2a3dd85d33705b6fde40bf91fc44411661283763fd0746723963c83", - "sha256:48e48530d9b995a84d1d89ae6b3ec4e59ea7d494b150ac3bbc5e2ac4acce92cd", - "sha256:54bbd295f031b866b9799dd39cb45deee81aca036c9bff9f58ca06726f6494f1", - "sha256:5d1fe6b6661022fd6cac541f54a4237496b246e6f1c0a6b41998ee08a1135afe", - "sha256:645373c070080e632480a3d251d892cb795be3d3a15f86975d0f1aca56fd230d", - "sha256:6a1a7dfc1f9c78a833e2c4904757a0f47ce25d08634dd2a52af394eefe5f9777", - "sha256:701e66b59dd21a32a274771238025d58db7e2b6ecebbab64ceff51b8e31527ae", - "sha256:72aa3fbe636b16d22e04b5a9d24711b043495e0ecfe58080addf23a1a37f3409", - "sha256:7af6bdbd21a2a25d6784f6d67f44f5df33ef39b6159543b9f9064d365c01f919", - "sha256:7ee9f226acab9085037582c059d66769862706e8e8cd2340470ceb8b3850873d", - "sha256:7f7bfb74718f52d5ed47d608d507bf66d3bc01d4a8b3e6dd7134daaae129357b", - "sha256:8e2eb957787cbb614a0f006bfc5798ff1d90ac7c4dd24854c84edbdc8c02369e", - "sha256:903f739c9fb78dab8970b0f3ea51f21955b24b45afa77b22ff0e172fc11ef111", - "sha256:98993805f1e3cdb53de4eed02b55dcc953cdf017ba7bbb2fd89226c086a6d855", - "sha256:9967d9758df505975913304c434cb9ab21e2c609ad859eb921f2f615a038c8de", - "sha256:a113789e53ac1fa26edf99856a61e4c493868e125ae0dd6354cf518948fbbd5c", - "sha256:a522d12e2ddbc2e91842ffb454a1aeb0d47607972c7d8fc88bd0838d97fb8a2a", - "sha256:abe829275cdd4174b4c4e65ad718715d449e308d59793bf3a931ee1bf7e7b86c", - "sha256:c286985b5e194ca0ebb2908d71464b9be8f17cc66d6d3e330e8d5407248f56ad", - "sha256:cd1295f52971097f757edfbfce827b6dbbfb0f7a74901ee7d4933dff5ad4c9af", - "sha256:ceafd5e960b39c7e0d160a1936b68eb87c5e79b3979d66e774f0c77d4d8faaed", - "sha256:d1f27bb0f75bef722d6e22dc609612bfa2f994541621cd2163f8c943b6463dfe", - "sha256:d3a4e165ca6204f34856b765d515d558dc84f1352033b8721e8d06c3e44930c3", - "sha256:d9b90bf58f3ba04e60321a23a8723a1ff2a9377502535e70495e5ada8e6e6722", - "sha256:f72b5d24d6730035128b238decdc4c0f2104b7056a7ca55cf047c106842ec890", - "sha256:fcddfe70553be717d9745990dfdb194e22ee0f60eb8f48c0794e7bfeda30d2d5", - "sha256:fdb9f9ed79bc6f46b021b3319184699ba1a22410a82204e6e89c774530069683" - ], - "version": "==0.4.0" - }, - "httpx": { - "hashes": [ - "sha256:42974f577483e1e932c3cdc3cd2303e883cbfba17fe228b0f63589764d7b9c4b", - "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef" - ], - "index": "pypi", - "version": "==0.23.0" - }, - "idna": { - "hashes": [ - "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", - "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" - ], - "markers": "python_version >= '3.5'", - "version": "==3.3" - }, - "jinja2": { - "hashes": [ - "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4", - "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4" - ], - "index": "pypi", - "version": "==3.0.1" - }, - "lnurl": { - "hashes": [ - "sha256:579982fd8c4d25bc84c61c74ec45cb7999fa1fa2426f5d5aeb0160ba333b9c92", - "sha256:8af07460115a48f3122a5a9c9a6062bee3897d5f6ab4c9a60f6561a83a8234f6" - ], - "index": "pypi", - "version": "==0.3.6" - }, - "loguru": { - "hashes": [ - "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c", - "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3" - ], - "index": "pypi", - "version": "==0.6.0" - }, - "markupsafe": { - "hashes": [ - "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", - "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", - "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", - "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", - "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", - "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", - "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", - "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", - "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", - "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", - "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", - "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", - "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", - "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", - "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", - "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", - "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", - "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", - "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", - "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", - "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", - "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", - "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", - "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", - "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", - "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", - "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", - "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", - "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", - "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", - "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", - "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", - "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", - "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", - "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", - "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", - "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", - "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", - "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", - "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.1" - }, - "marshmallow": { - "hashes": [ - "sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb", - "sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7" - ], - "markers": "python_version >= '3.7'", - "version": "==3.17.0" - }, - "outcome": { - "hashes": [ - "sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672", - "sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5" - ], - "markers": "python_version >= '3.7'", - "version": "==1.2.0" - }, - "packaging": { - "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" - ], - "markers": "python_version >= '3.6'", - "version": "==21.3" - }, - "psycopg2-binary": { - "hashes": [ - "sha256:01310cf4cf26db9aea5158c217caa92d291f0500051a6469ac52166e1a16f5b7", - "sha256:083a55275f09a62b8ca4902dd11f4b33075b743cf0d360419e2051a8a5d5ff76", - "sha256:090f3348c0ab2cceb6dfbe6bf721ef61262ddf518cd6cc6ecc7d334996d64efa", - "sha256:0a29729145aaaf1ad8bafe663131890e2111f13416b60e460dae0a96af5905c9", - "sha256:0c9d5450c566c80c396b7402895c4369a410cab5a82707b11aee1e624da7d004", - "sha256:10bb90fb4d523a2aa67773d4ff2b833ec00857f5912bafcfd5f5414e45280fb1", - "sha256:12b11322ea00ad8db8c46f18b7dfc47ae215e4df55b46c67a94b4effbaec7094", - "sha256:152f09f57417b831418304c7f30d727dc83a12761627bb826951692cc6491e57", - "sha256:15803fa813ea05bef089fa78835118b5434204f3a17cb9f1e5dbfd0b9deea5af", - "sha256:15c4e4cfa45f5a60599d9cec5f46cd7b1b29d86a6390ec23e8eebaae84e64554", - "sha256:183a517a3a63503f70f808b58bfbf962f23d73b6dccddae5aa56152ef2bcb232", - "sha256:1f14c8b0942714eb3c74e1e71700cbbcb415acbc311c730370e70c578a44a25c", - "sha256:1f6b813106a3abdf7b03640d36e24669234120c72e91d5cbaeb87c5f7c36c65b", - "sha256:280b0bb5cbfe8039205c7981cceb006156a675362a00fe29b16fbc264e242834", - "sha256:2d872e3c9d5d075a2e104540965a1cf898b52274a5923936e5bfddb58c59c7c2", - "sha256:2f9ffd643bc7349eeb664eba8864d9e01f057880f510e4681ba40a6532f93c71", - "sha256:3303f8807f342641851578ee7ed1f3efc9802d00a6f83c101d21c608cb864460", - "sha256:35168209c9d51b145e459e05c31a9eaeffa9a6b0fd61689b48e07464ffd1a83e", - "sha256:3a79d622f5206d695d7824cbf609a4f5b88ea6d6dab5f7c147fc6d333a8787e4", - "sha256:404224e5fef3b193f892abdbf8961ce20e0b6642886cfe1fe1923f41aaa75c9d", - "sha256:46f0e0a6b5fa5851bbd9ab1bc805eef362d3a230fbdfbc209f4a236d0a7a990d", - "sha256:47133f3f872faf28c1e87d4357220e809dfd3fa7c64295a4a148bcd1e6e34ec9", - "sha256:526ea0378246d9b080148f2d6681229f4b5964543c170dd10bf4faaab6e0d27f", - "sha256:53293533fcbb94c202b7c800a12c873cfe24599656b341f56e71dd2b557be063", - "sha256:539b28661b71da7c0e428692438efbcd048ca21ea81af618d845e06ebfd29478", - "sha256:57804fc02ca3ce0dbfbef35c4b3a4a774da66d66ea20f4bda601294ad2ea6092", - "sha256:63638d875be8c2784cfc952c9ac34e2b50e43f9f0a0660b65e2a87d656b3116c", - "sha256:6472a178e291b59e7f16ab49ec8b4f3bdada0a879c68d3817ff0963e722a82ce", - "sha256:68641a34023d306be959101b345732360fc2ea4938982309b786f7be1b43a4a1", - "sha256:6e82d38390a03da28c7985b394ec3f56873174e2c88130e6966cb1c946508e65", - "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e", - "sha256:7af0dd86ddb2f8af5da57a976d27cd2cd15510518d582b478fbb2292428710b4", - "sha256:7b1e9b80afca7b7a386ef087db614faebbf8839b7f4db5eb107d0f1a53225029", - "sha256:874a52ecab70af13e899f7847b3e074eeb16ebac5615665db33bce8a1009cf33", - "sha256:887dd9aac71765ac0d0bac1d0d4b4f2c99d5f5c1382d8b770404f0f3d0ce8a39", - "sha256:8b344adbb9a862de0c635f4f0425b7958bf5a4b927c8594e6e8d261775796d53", - "sha256:8fc53f9af09426a61db9ba357865c77f26076d48669f2e1bb24d85a22fb52307", - "sha256:91920527dea30175cc02a1099f331aa8c1ba39bf8b7762b7b56cbf54bc5cce42", - "sha256:93cd1967a18aa0edd4b95b1dfd554cf15af657cb606280996d393dadc88c3c35", - "sha256:99485cab9ba0fa9b84f1f9e1fef106f44a46ef6afdeec8885e0b88d0772b49e8", - "sha256:9d29409b625a143649d03d0fd7b57e4b92e0ecad9726ba682244b73be91d2fdb", - "sha256:a29b3ca4ec9defec6d42bf5feb36bb5817ba3c0230dd83b4edf4bf02684cd0ae", - "sha256:a9e1f75f96ea388fbcef36c70640c4efbe4650658f3d6a2967b4cc70e907352e", - "sha256:accfe7e982411da3178ec690baaceaad3c278652998b2c45828aaac66cd8285f", - "sha256:adf20d9a67e0b6393eac162eb81fb10bc9130a80540f4df7e7355c2dd4af9fba", - "sha256:af9813db73395fb1fc211bac696faea4ca9ef53f32dc0cfa27e4e7cf766dcf24", - "sha256:b1c8068513f5b158cf7e29c43a77eb34b407db29aca749d3eb9293ee0d3103ca", - "sha256:bda845b664bb6c91446ca9609fc69f7db6c334ec5e4adc87571c34e4f47b7ddb", - "sha256:c381bda330ddf2fccbafab789d83ebc6c53db126e4383e73794c74eedce855ef", - "sha256:c3ae8e75eb7160851e59adc77b3a19a976e50622e44fd4fd47b8b18208189d42", - "sha256:d1c1b569ecafe3a69380a94e6ae09a4789bbb23666f3d3a08d06bbd2451f5ef1", - "sha256:def68d7c21984b0f8218e8a15d514f714d96904265164f75f8d3a70f9c295667", - "sha256:dffc08ca91c9ac09008870c9eb77b00a46b3378719584059c034b8945e26b272", - "sha256:e3699852e22aa68c10de06524a3721ade969abf382da95884e6a10ff798f9281", - "sha256:e847774f8ffd5b398a75bc1c18fbb56564cda3d629fe68fd81971fece2d3c67e", - "sha256:ffb7a888a047696e7f8240d649b43fb3644f14f0ee229077e7f6b9f9081635bd" - ], - "index": "pypi", - "version": "==2.9.3" - }, - "pycparser": { - "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" - ], - "version": "==2.21" - }, - "pycryptodomex": { - "hashes": [ - "sha256:04cc393045a8f19dd110c975e30f38ed7ab3faf21ede415ea67afebd95a22380", - "sha256:0776bfaf2c48154ab54ea45392847c1283d2fcf64e232e85565f858baedfc1fa", - "sha256:0fadb9f7fa3150577800eef35f62a8a24b9ddf1563ff060d9bd3af22d3952c8c", - "sha256:18e2ab4813883ae63396c0ffe50b13554b32bb69ec56f0afaf052e7a7ae0d55b", - "sha256:191e73bc84a8064ad1874dba0ebadedd7cce4dedee998549518f2c74a003b2e1", - "sha256:35a8f7afe1867118330e2e0e0bf759c409e28557fb1fc2fbb1c6c937297dbe9a", - "sha256:3709f13ca3852b0b07fc04a2c03b379189232b24007c466be0f605dd4723e9d4", - "sha256:4540904c09704b6f831059c0dfb38584acb82cb97b0125cd52688c1f1e3fffa6", - "sha256:463119d7d22d0fc04a0f9122e9d3e6121c6648bcb12a052b51bd1eed1b996aa2", - "sha256:46b3f05f2f7ac7841053da4e0f69616929ca3c42f238c405f6c3df7759ad2780", - "sha256:48697790203909fab02a33226fda546604f4e2653f9d47bc5d3eb40879fa7c64", - "sha256:5676a132169a1c1a3712edf25250722ebc8c9102aa9abd814df063ca8362454f", - "sha256:65204412d0c6a8e3c41e21e93a5e6054a74fea501afa03046a388cf042e3377a", - "sha256:67e1e6a92151023ccdfcfbc0afb3314ad30080793b4c27956ea06ab1fb9bcd8a", - "sha256:6f5b6ba8aefd624834bc177a2ac292734996bb030f9d1b388e7504103b6fcddf", - "sha256:7341f1bb2dadb0d1a0047f34c3a58208a92423cdbd3244d998e4b28df5eac0ed", - "sha256:78d9621cf0ea35abf2d38fa2ca6d0634eab6c991a78373498ab149953787e5e5", - "sha256:8eecdf9cdc7343001d047f951b9cc805cd68cb6cd77b20ea46af5bffc5bd3dfb", - "sha256:94c7b60e1f52e1a87715571327baea0733708ab4723346598beca4a3b6879794", - "sha256:996e1ba717077ce1e6d4849af7a1426f38b07b3d173b879e27d5e26d2e958beb", - "sha256:a07a64709e366c2041cd5cfbca592b43998bf4df88f7b0ca73dca37071ccf1bd", - "sha256:b6306403228edde6e289f626a3908a2f7f67c344e712cf7c0a508bab3ad9e381", - "sha256:b9279adc16e4b0f590ceff581f53a80179b02cba9056010d733eb4196134a870", - "sha256:c4cb9cb492ea7dcdf222a8d19a1d09002798ea516aeae8877245206d27326d86", - "sha256:dd452a5af7014e866206d41751886c9b4bf379a339fdf2dbfc7dd16c0fb4f8e0", - "sha256:e2b12968522a0358b8917fc7b28865acac002f02f4c4c6020fcb264d76bfd06d", - "sha256:e3164a18348bd53c69b4435ebfb4ac8a4076291ffa2a70b54f0c4b80c7834b1d", - "sha256:e47bf8776a7e15576887f04314f5228c6527b99946e6638cf2f16da56d260cab", - "sha256:f8be976cec59b11f011f790b88aca67b4ea2bd286578d0bd3e31bcd19afcd3e4", - "sha256:fc9bc7a9b79fe5c750fc81a307052f8daabb709bdaabb0fb18fb136b66b653b5" - ], - "index": "pypi", - "version": "==3.15.0" - }, - "pydantic": { - "hashes": [ - "sha256:02eefd7087268b711a3ff4db528e9916ac9aa18616da7bca69c1871d0b7a091f", - "sha256:059b6c1795170809103a1538255883e1983e5b831faea6558ef873d4955b4a74", - "sha256:0bf07cab5b279859c253d26a9194a8906e6f4a210063b84b433cf90a569de0c1", - "sha256:1542636a39c4892c4f4fa6270696902acb186a9aaeac6f6cf92ce6ae2e88564b", - "sha256:177071dfc0df6248fd22b43036f936cfe2508077a72af0933d0c1fa269b18537", - "sha256:18f3e912f9ad1bdec27fb06b8198a2ccc32f201e24174cec1b3424dda605a310", - "sha256:1dd8fecbad028cd89d04a46688d2fcc14423e8a196d5b0a5c65105664901f810", - "sha256:1ed987c3ff29fff7fd8c3ea3a3ea877ad310aae2ef9889a119e22d3f2db0691a", - "sha256:447d5521575f18e18240906beadc58551e97ec98142266e521c34968c76c8761", - "sha256:494f7c8537f0c02b740c229af4cb47c0d39840b829ecdcfc93d91dcbb0779892", - "sha256:4988c0f13c42bfa9ddd2fe2f569c9d54646ce84adc5de84228cfe83396f3bd58", - "sha256:4ce9ae9e91f46c344bec3b03d6ee9612802682c1551aaf627ad24045ce090761", - "sha256:5d93d4e95eacd313d2c765ebe40d49ca9dd2ed90e5b37d0d421c597af830c195", - "sha256:61b6760b08b7c395975d893e0b814a11cf011ebb24f7d869e7118f5a339a82e1", - "sha256:72ccb318bf0c9ab97fc04c10c37683d9eea952ed526707fabf9ac5ae59b701fd", - "sha256:79b485767c13788ee314669008d01f9ef3bc05db9ea3298f6a50d3ef596a154b", - "sha256:7eb57ba90929bac0b6cc2af2373893d80ac559adda6933e562dcfb375029acee", - "sha256:8bc541a405423ce0e51c19f637050acdbdf8feca34150e0d17f675e72d119580", - "sha256:969dd06110cb780da01336b281f53e2e7eb3a482831df441fb65dd30403f4608", - "sha256:985ceb5d0a86fcaa61e45781e567a59baa0da292d5ed2e490d612d0de5796918", - "sha256:9bcf8b6e011be08fb729d110f3e22e654a50f8a826b0575c7196616780683380", - "sha256:9ce157d979f742a915b75f792dbd6aa63b8eccaf46a1005ba03aa8a986bde34a", - "sha256:9f659a5ee95c8baa2436d392267988fd0f43eb774e5eb8739252e5a7e9cf07e0", - "sha256:a4a88dcd6ff8fd47c18b3a3709a89adb39a6373f4482e04c1b765045c7e282fd", - "sha256:a955260d47f03df08acf45689bd163ed9df82c0e0124beb4251b1290fa7ae728", - "sha256:a9af62e9b5b9bc67b2a195ebc2c2662fdf498a822d62f902bf27cccb52dbbf49", - "sha256:ae72f8098acb368d877b210ebe02ba12585e77bd0db78ac04a1ee9b9f5dd2166", - "sha256:b83ba3825bc91dfa989d4eed76865e71aea3a6ca1388b59fc801ee04c4d8d0d6", - "sha256:c11951b404e08b01b151222a1cb1a9f0a860a8153ce8334149ab9199cd198131", - "sha256:c320c64dd876e45254bdd350f0179da737463eea41c43bacbee9d8c9d1021f11", - "sha256:c8098a724c2784bf03e8070993f6d46aa2eeca031f8d8a048dff277703e6e193", - "sha256:d12f96b5b64bec3f43c8e82b4aab7599d0157f11c798c9f9c528a72b9e0b339a", - "sha256:e565a785233c2d03724c4dc55464559639b1ba9ecf091288dd47ad9c629433bd", - "sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e", - "sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6" - ], - "markers": "python_full_version >= '3.6.1'", - "version": "==1.9.1" - }, - "pyngrok": { - "hashes": [ - "sha256:4d03f44a69c3cbc168b17377956a9edcf723e77dbc864eba34c272db15da443c" - ], - "index": "pypi", - "version": "==5.1.0" - }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.0.9" - }, - "pypng": { - "hashes": [ - "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c", - "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1" - ], - "index": "pypi", - "version": "==0.20220715.0" - }, - "pyqrcode": { - "hashes": [ - "sha256:1b2812775fa6ff5c527977c4cd2ccb07051ca7d0bc0aecf937a43864abe5eff6", - "sha256:fdbf7634733e56b72e27f9bce46e4550b75a3a2c420414035cae9d9d26b234d5" - ], - "index": "pypi", - "version": "==1.2.1" - }, - "pyscss": { - "hashes": [ - "sha256:8f35521ffe36afa8b34c7d6f3195088a7057c185c2b8f15ee459ab19748669ff" - ], - "index": "pypi", - "version": "==1.4.0" - }, - "python-dotenv": { - "hashes": [ - "sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f", - "sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938" - ], - "markers": "python_version >= '3.5'", - "version": "==0.20.0" - }, - "pyyaml": { - "hashes": [ - "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", - "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", - "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", - "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", - "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", - "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", - "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", - "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", - "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", - "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", - "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", - "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", - "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", - "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", - "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", - "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", - "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", - "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", - "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", - "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", - "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", - "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", - "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", - "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", - "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", - "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", - "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", - "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", - "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", - "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", - "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", - "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", - "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" - ], - "version": "==6.0" - }, - "represent": { - "hashes": [ - "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" - ], - "version": "==1.5.0" - }, - "secp256k1": { - "hashes": [ - "sha256:130f119b06142e597c10eb4470b5a38eae865362d01aaef06b113478d77f728d", - "sha256:373dc8bca735f3c2d73259aa2711a9ecea2f3c7edbb663555fe3422e3dd76102", - "sha256:3aedcfe6eb1c5fa7c6be25b7cc91c76d8eb984271920ba0f7a934ae41ed56f51", - "sha256:4b1bf09953cde181132cf5e9033065615e5c2694e803165e2db763efa47695e5", - "sha256:63eb148196b8f646922d4be6739b17fbbf50ebb3a020078c823e2445d88b7a81", - "sha256:6af07be5f8612628c3638dc7b208f6cc78d0abae3e25797eadb13890c7d5da81", - "sha256:72735da6cb28273e924431cd40aa607e7f80ef09608c8c9300be2e0e1d2417b4", - "sha256:7a27c479ab60571502516a1506a562d0a9df062de8ad645313fabfcc97252816", - "sha256:82c06712d69ef945220c8b53c1a0d424c2ff6a1f64aee609030df79ad8383397", - "sha256:87f4ad42a370f768910585989a301d1d65de17dcd86f6e8def9b021364b34d5c", - "sha256:97a30c8dae633cb18135c76b6517ae99dc59106818e8985be70dbc05dcc06c0d", - "sha256:a8dbd75a9fb6f42de307f3c5e24573fe59c3374637cbf39136edc66c200a4029", - "sha256:adc23a4c5d24c95191638eb2ca313097827f07db102e77b59faed15d50c98cae", - "sha256:bc761894b3634021686714278fc62b73395fa3eded33453eadfd8a00a6c44ef3", - "sha256:c91dd3154f6c46ac798d9a41166120e1751222587f54516cc3f378f56ce4ac82", - "sha256:c9e7c024ff17e9b9d7c392bb2a917da231d6cb40ab119389ff1f51dca10339a4", - "sha256:ce0314788d3248b275426501228969fd32f6501c9d1837902ee0e7bd8264a36f", - "sha256:f4062d8c101aa63b9ecb3709f1f075ad9c01b6672869bbaa1bd77271816936a7", - "sha256:f4b9306bff6dde020444dfee9ca9b9f5b20ca53a2c0b04898361a3f43d5daf2e", - "sha256:f666c67dcf1dc69e1448b2ede5e12aaf382b600204a61dbc65e4f82cea444405", - "sha256:fcabb3c3497a902fb61eec72d1b69bf72747d7bcc2a732d56d9319a1e8322262", - "sha256:fe3f503c9dfdf663b500d3e0688ad842e116c2907ad3f1e1d685812df3f56290", - "sha256:fec790cb6d0d37129ca0ce5b3f8e85692d5fb618d1c440f189453d18694035df" - ], - "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", - "sha256:b2bb9eb7773170e253bb7ba25971023acb473517a8b76803d9618668cb1dd46f" - ], - "index": "pypi", - "version": "==1.0.9" - }, - "six": { - "hashes": [ - "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": { - "hashes": [ - "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663", - "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de" - ], - "markers": "python_version >= '3.5'", - "version": "==1.2.0" - }, - "sqlalchemy": { - "hashes": [ - "sha256:040bdfc1d76a9074717a3f43455685f781c581f94472b010cd6c4754754e1862", - "sha256:1fe5d8d39118c2b018c215c37b73fd6893c3e1d4895be745ca8ff6eb83333ed3", - "sha256:23927c3981d1ec6b4ea71eb99d28424b874d9c696a21e5fbd9fa322718be3708", - "sha256:24f9569e82a009a09ce2d263559acb3466eba2617203170e4a0af91e75b4f075", - "sha256:2578dbdbe4dbb0e5126fb37ffcd9793a25dcad769a95f171a2161030bea850ff", - "sha256:269990b3ab53cb035d662dcde51df0943c1417bdab707dc4a7e4114a710504b4", - "sha256:29cccc9606750fe10c5d0e8bd847f17a97f3850b8682aef1f56f5d5e1a5a64b1", - "sha256:37b83bf81b4b85dda273aaaed5f35ea20ad80606f672d94d2218afc565fb0173", - "sha256:63677d0c08524af4c5893c18dbe42141de7178001360b3de0b86217502ed3601", - "sha256:639940bbe1108ac667dcffc79925db2966826c270112e9159439ab6bb14f8d80", - "sha256:6a939a868fdaa4b504e8b9d4a61f21aac11e3fecc8a8214455e144939e3d2aea", - "sha256:6b8b8c80c7f384f06825612dd078e4a31f0185e8f1f6b8c19e188ff246334205", - "sha256:6c9e6cc9237de5660bcddea63f332428bb83c8e2015c26777281f7ffbd2efb84", - "sha256:6ec1044908414013ebfe363450c22f14698803ce97fbb47e53284d55c5165848", - "sha256:6fca33672578666f657c131552c4ef8979c1606e494f78cd5199742dfb26918b", - "sha256:751934967f5336a3e26fc5993ccad1e4fee982029f9317eb6153bc0bc3d2d2da", - "sha256:8be835aac18ec85351385e17b8665bd4d63083a7160a017bef3d640e8e65cadb", - "sha256:927ce09e49bff3104459e1451ce82983b0a3062437a07d883a4c66f0b344c9b5", - "sha256:94208867f34e60f54a33a37f1c117251be91a47e3bfdb9ab8a7847f20886ad06", - "sha256:94f667d86be82dd4cb17d08de0c3622e77ca865320e0b95eae6153faa7b4ecaf", - "sha256:9e9c25522933e569e8b53ccc644dc993cab87e922fb7e142894653880fdd419d", - "sha256:a0e306e9bb76fd93b29ae3a5155298e4c1b504c7cbc620c09c20858d32d16234", - "sha256:a8bfc1e1afe523e94974132d7230b82ca7fa2511aedde1f537ec54db0399541a", - "sha256:ac2244e64485c3778f012951fdc869969a736cd61375fde6096d08850d8be729", - "sha256:b4b0e44d586cd64b65b507fa116a3814a1a53d55dce4836d7c1a6eb2823ff8d1", - "sha256:baeb451ee23e264de3f577fee5283c73d9bbaa8cb921d0305c0bbf700094b65b", - "sha256:c7dc052432cd5d060d7437e217dd33c97025287f99a69a50e2dc1478dd610d64", - "sha256:d1a85dfc5dee741bf49cb9b6b6b8d2725a268e4992507cf151cba26b17d97c37", - "sha256:d90010304abb4102123d10cbad2cdf2c25a9f2e66a50974199b24b468509bad5", - "sha256:ddfb511e76d016c3a160910642d57f4587dc542ce5ee823b0d415134790eeeb9", - "sha256:e273367f4076bd7b9a8dc2e771978ef2bfd6b82526e80775a7db52bff8ca01dd", - "sha256:e5bb3463df697279e5459a7316ad5a60b04b0107f9392e88674d0ece70e9cf70", - "sha256:e8a1750b44ad6422ace82bf3466638f1aa0862dbb9689690d5f2f48cce3476c8", - "sha256:eab063a70cca4a587c28824e18be41d8ecc4457f8f15b2933584c6c6cccd30f0", - "sha256:ecce8c021894a77d89808222b1ff9687ad84db54d18e4bd0500ca766737faaf6", - "sha256:f4d972139d5000105fcda9539a76452039434013570d6059993120dc2a65e447", - "sha256:fd3b96f8c705af8e938eaa99cbd8fd1450f632d38cad55e7367c33b263bf98ec", - "sha256:fdd2ed7395df8ac2dbb10cefc44737b66c6a5cd7755c92524733d7a443e5b7e2" - ], - "index": "pypi", - "version": "==1.3.23" - }, - "sqlalchemy-aio": { - "hashes": [ - "sha256:3f4aa392c38f032d6734826a4138a0f02ed3122d442ed142be1e5964f2a33b60", - "sha256:f531c7982662d71dfc0b117e77bb2ed544e25cd5361e76cf9f5208edcfb71f7b" - ], - "index": "pypi", - "version": "==0.17.0" - }, - "sse-starlette": { - "hashes": [ - "sha256:14608559d40e3e7c6385e8c5a7b88468f7fc40c2277673a1fe8d26568e8d7c65", - "sha256:72438ed39b1612d1ea6d89a7c0af8afee6de0389dcbe2e77539001e78b5aa89c" - ], - "index": "pypi", - "version": "==1.0.0" - }, - "starlette": { - "hashes": [ - "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf", - "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7" - ], - "markers": "python_version >= '3.6'", - "version": "==0.19.1" - }, - "typing-extensions": { - "hashes": [ - "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02", - "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6" - ], - "index": "pypi", - "version": "==4.3.0" - }, - "uvicorn": { - "extras": [ - "standard" - ], - "hashes": [ - "sha256:c19a057deb1c5bb060946e2e5c262fc01590c6529c0af2c3d9ce941e89bc30e0", - "sha256:cade07c403c397f9fe275492a48c1b869efd175d5d8a692df649e6e7e2ed8f4e" - ], - "index": "pypi", - "version": "==0.18.2" - }, - "uvloop": { - "hashes": [ - "sha256:04ff57aa137230d8cc968f03481176041ae789308b4d5079118331ab01112450", - "sha256:089b4834fd299d82d83a25e3335372f12117a7d38525217c2258e9b9f4578897", - "sha256:1e5f2e2ff51aefe6c19ee98af12b4ae61f5be456cd24396953244a30880ad861", - "sha256:30ba9dcbd0965f5c812b7c2112a1ddf60cf904c1c160f398e7eed3a6b82dcd9c", - "sha256:3a19828c4f15687675ea912cc28bbcb48e9bb907c801873bd1519b96b04fb805", - "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d", - "sha256:647e481940379eebd314c00440314c81ea547aa636056f554d491e40503c8464", - "sha256:6ccd57ae8db17d677e9e06192e9c9ec4bd2066b77790f9aa7dede2cc4008ee8f", - "sha256:772206116b9b57cd625c8a88f2413df2fcfd0b496eb188b82a43bed7af2c2ec9", - "sha256:8e0d26fa5875d43ddbb0d9d79a447d2ace4180d9e3239788208527c4784f7cab", - "sha256:98d117332cc9e5ea8dfdc2b28b0a23f60370d02e1395f88f40d1effd2cb86c4f", - "sha256:b572256409f194521a9895aef274cea88731d14732343da3ecdb175228881638", - "sha256:bd53f7f5db562f37cd64a3af5012df8cac2c464c97e732ed556800129505bd64", - "sha256:bd8f42ea1ea8f4e84d265769089964ddda95eb2bb38b5cbe26712b0616c3edee", - "sha256:e814ac2c6f9daf4c36eb8e85266859f42174a4ff0d71b99405ed559257750382", - "sha256:f74bc20c7b67d1c27c72601c78cf95be99d5c2cdd4514502b4f3eb0933ff1228" - ], - "version": "==0.16.0" - }, - "watchfiles": { - "hashes": [ - "sha256:059bd9596429f8c13604b2eb30888a5661b3c79099edc506f11b63be7afe3ca4", - "sha256:09490d258be8fdd7f5141a39b468dede0b4aa4a52f2b2dbfb0f3835ae7c23eca", - "sha256:1bb5f0117c8b93f8e1b22ac0be60cfeb00332959a72e6bbe2073fea27ed086e5", - "sha256:3d3f0397c9128971398a5cbb0fb45852ab2fa4472ac9724c031071e1e39970c0", - "sha256:43d1d517faffa8955c2da0e6f64268e38442d43b50ca73cb686df25f891e49a1", - "sha256:4f712dbe9d8c0365bf46ffe0dd9c6a62cc0acf05ba951f1a53de2b4d5bb63299", - "sha256:59498853d3214d1e4d9b1cb3a06b0011a11f24d31708b1734d9cd7f5a30fe1af", - "sha256:5e3d4c92091d16bca1d61920575dab5d6dcbceda76dccd5fb91da0b7390b4ee9", - "sha256:5fa786d102e7eabef22b2147af531aa70194aabcb35335be81c07c26382b0050", - "sha256:750e40db5efcf3f5f11602dbc6fdf8e96a0eefdbccd271093efe9fa2e9d02ed2", - "sha256:7c80e3907d21ca3f1689f42632d239fdc40ffc1d5f32f564997480f85e94c474", - "sha256:8d635dcba3aab2909bf568765547696d7465d30e2e9c6f5ab99da877b58d29bb", - "sha256:a5f64674559fac56a6bf2f5e086cb3758740140c80711fe3e016f5443b84ef15", - "sha256:bcd085980389bc64fe509188a9caffa4fe13b2616e2e3e674cde58f916b2a8ee", - "sha256:c9e3756cd2ba17e5042e8c9399a08e4bdbe1a366156a164e8373bda30ca096d0", - "sha256:cbdb7814ca43f85ab8569206ab2c3bcd51dd5d1ba582914246784414e6ada62e", - "sha256:d5fb4f3b5c884d4f22f643b0697edbb04942bcad961a8f9a9bfadb73e7a1e229" - ], - "version": "==0.16.0" - }, - "websockets": { - "hashes": [ - "sha256:07cdc0a5b2549bcfbadb585ad8471ebdc7bdf91e32e34ae3889001c1c106a6af", - "sha256:210aad7fdd381c52e58777560860c7e6110b6174488ef1d4b681c08b68bf7f8c", - "sha256:28dd20b938a57c3124028680dc1600c197294da5db4292c76a0b48efb3ed7f76", - "sha256:2f94fa3ae454a63ea3a19f73b95deeebc9f02ba2d5617ca16f0bbdae375cda47", - "sha256:31564a67c3e4005f27815634343df688b25705cccb22bc1db621c781ddc64c69", - "sha256:347974105bbd4ea068106ec65e8e8ebd86f28c19e529d115d89bd8cc5cda3079", - "sha256:379e03422178436af4f3abe0aa8f401aa77ae2487843738542a75faf44a31f0c", - "sha256:3eda1cb7e9da1b22588cefff09f0951771d6ee9fa8dbe66f5ae04cc5f26b2b55", - "sha256:51695d3b199cd03098ae5b42833006a0f43dc5418d3102972addc593a783bc02", - "sha256:54c000abeaff6d8771a4e2cef40900919908ea7b6b6a30eae72752607c6db559", - "sha256:5b936bf552e4f6357f5727579072ff1e1324717902127ffe60c92d29b67b7be3", - "sha256:6075fd24df23133c1b078e08a9b04a3bc40b31a8def4ee0b9f2c8865acce913e", - "sha256:661f641b44ed315556a2fa630239adfd77bd1b11cb0b9d96ed8ad90b0b1e4978", - "sha256:6ea6b300a6bdd782e49922d690e11c3669828fe36fc2471408c58b93b5535a98", - "sha256:6ed1d6f791eabfd9808afea1e068f5e59418e55721db8b7f3bfc39dc831c42ae", - "sha256:7934e055fd5cd9dee60f11d16c8d79c4567315824bacb1246d0208a47eca9755", - "sha256:7ab36e17af592eec5747c68ef2722a74c1a4a70f3772bc661079baf4ae30e40d", - "sha256:7f6d96fdb0975044fdd7953b35d003b03f9e2bcf85f2d2cf86285ece53e9f991", - "sha256:83e5ca0d5b743cde3d29fda74ccab37bdd0911f25bd4cdf09ff8b51b7b4f2fa1", - "sha256:85506b3328a9e083cc0a0fb3ba27e33c8db78341b3eb12eb72e8afd166c36680", - "sha256:8af75085b4bc0b5c40c4a3c0e113fa95e84c60f4ed6786cbb675aeb1ee128247", - "sha256:8b1359aba0ff810d5830d5ab8e2c4a02bebf98a60aa0124fb29aa78cfdb8031f", - "sha256:8fbd7d77f8aba46d43245e86dd91a8970eac4fb74c473f8e30e9c07581f852b2", - "sha256:907e8247480f287aa9bbc9391bd6de23c906d48af54c8c421df84655eef66af7", - "sha256:93d5ea0b5da8d66d868b32c614d2b52d14304444e39e13a59566d4acb8d6e2e4", - "sha256:97bc9d41e69a7521a358f9b8e44871f6cdeb42af31815c17aed36372d4eec667", - "sha256:994cdb1942a7a4c2e10098d9162948c9e7b235df755de91ca33f6e0481366fdb", - "sha256:a141de3d5a92188234afa61653ed0bbd2dde46ad47b15c3042ffb89548e77094", - "sha256:a1e15b230c3613e8ea82c9fc6941b2093e8eb939dd794c02754d33980ba81e36", - "sha256:aad5e300ab32036eb3fdc350ad30877210e2f51bceaca83fb7fef4d2b6c72b79", - "sha256:b529fdfa881b69fe563dbd98acce84f3e5a67df13de415e143ef053ff006d500", - "sha256:b9c77f0d1436ea4b4dc089ed8335fa141e6a251a92f75f675056dac4ab47a71e", - "sha256:bb621ec2dbbbe8df78a27dbd9dd7919f9b7d32a73fafcb4d9252fc4637343582", - "sha256:c7250848ce69559756ad0086a37b82c986cd33c2d344ab87fea596c5ac6d9442", - "sha256:c8d1d14aa0f600b5be363077b621b1b4d1eb3fbf90af83f9281cda668e6ff7fd", - "sha256:d1655a6fc7aecd333b079d00fb3c8132d18988e47f19740c69303bf02e9883c6", - "sha256:d6353ba89cfc657a3f5beabb3b69be226adbb5c6c7a66398e17809b0ce3c4731", - "sha256:da4377904a3379f0c1b75a965fff23b28315bcd516d27f99a803720dfebd94d4", - "sha256:e49ea4c1a9543d2bd8a747ff24411509c29e4bdcde05b5b0895e2120cb1a761d", - "sha256:e4e08305bfd76ba8edab08dcc6496f40674f44eb9d5e23153efa0a35750337e8", - "sha256:e6fa05a680e35d0fcc1470cb070b10e6fe247af54768f488ed93542e71339d6f", - "sha256:e7e6f2d6fd48422071cc8a6f8542016f350b79cc782752de531577d35e9bd677", - "sha256:e904c0381c014b914136c492c8fa711ca4cced4e9b3d110e5e7d436d0fc289e8", - "sha256:ec2b0ab7edc8cd4b0eb428b38ed89079bdc20c6bdb5f889d353011038caac2f9", - "sha256:ef5ce841e102278c1c2e98f043db99d6755b1c58bde475516aef3a008ed7f28e", - "sha256:f351c7d7d92f67c0609329ab2735eee0426a03022771b00102816a72715bb00b", - "sha256:fab7c640815812ed5f10fbee7abbf58788d602046b7bb3af9b1ac753a6d5e916", - "sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4" - ], - "version": "==10.3" - } - }, - "develop": { - "appdirs": { - "hashes": [ - "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", - "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128" - ], - "version": "==1.4.4" - }, - "attrs": { - "hashes": [ - "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": { - "hashes": [ - "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea" - ], - "index": "pypi", - "version": "==20.8b1" - }, - "certifi": { - "hashes": [ - "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d", - "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412" - ], - "markers": "python_version >= '3.6'", - "version": "==2022.6.15" - }, - "charset-normalizer": { - "hashes": [ - "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5", - "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413" - ], - "markers": "python_version >= '3.6'", - "version": "==2.1.0" - }, - "click": { - "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.3" - }, - "coverage": { - "extras": [ - "toml" - ], - "hashes": [ - "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32", - "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7", - "sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996", - "sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55", - "sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46", - "sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de", - "sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039", - "sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee", - "sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1", - "sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f", - "sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63", - "sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083", - "sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe", - "sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0", - "sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6", - "sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe", - "sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933", - "sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0", - "sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c", - "sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07", - "sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8", - "sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b", - "sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e", - "sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120", - "sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f", - "sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e", - "sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd", - "sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f", - "sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386", - "sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8", - "sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae", - "sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc", - "sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783", - "sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d", - "sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c", - "sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97", - "sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978", - "sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf", - "sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29", - "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39", - "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452" - ], - "markers": "python_version >= '3.7'", - "version": "==6.4.2" - }, - "idna": { - "hashes": [ - "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", - "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" - ], - "markers": "python_version >= '3.5'", - "version": "==3.3" - }, - "iniconfig": { - "hashes": [ - "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", - "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" - ], - "version": "==1.1.1" - }, - "mock": { - "hashes": [ - "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62", - "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc" - ], - "index": "pypi", - "version": "==4.0.3" - }, - "mypy": { - "hashes": [ - "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.971" - }, - "mypy-extensions": { - "hashes": [ - "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", - "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" - ], - "version": "==0.4.3" - }, - "packaging": { - "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" - ], - "markers": "python_version >= '3.6'", - "version": "==21.3" - }, - "pathspec": { - "hashes": [ - "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a", - "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1" - ], - "version": "==0.9.0" - }, - "pluggy": { - "hashes": [ - "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", - "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" - ], - "markers": "python_version >= '3.6'", - "version": "==1.0.0" - }, - "py": { - "hashes": [ - "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": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.0.9" - }, - "pytest": { - "hashes": [ - "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c", - "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45" - ], - "index": "pypi", - "version": "==7.1.2" - }, - "pytest-asyncio": { - "hashes": [ - "sha256:7a97e37cfe1ed296e2e84941384bdd37c376453912d397ed39293e0916f521fa", - "sha256:ac4ebf3b6207259750bc32f4c1d8fcd7e79739edbc67ad0c58dd150b1d072fed" - ], - "index": "pypi", - "version": "==0.19.0" - }, - "pytest-cov": { - "hashes": [ - "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6", - "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470" - ], - "index": "pypi", - "version": "==3.0.0" - }, - "regex": { - "hashes": [ - "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" - ], - "markers": "python_version >= '3.6'", - "version": "==2022.7.24" - }, - "requests": { - "hashes": [ - "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", - "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" - ], - "index": "pypi", - "version": "==2.28.1" - }, - "toml": { - "hashes": [ - "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": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.1" - }, - "typed-ast": { - "hashes": [ - "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2", - "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1", - "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6", - "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62", - "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac", - "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d", - "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc", - "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2", - "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97", - "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35", - "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6", - "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1", - "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4", - "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c", - "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e", - "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec", - "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f", - "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72", - "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47", - "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72", - "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe", - "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6", - "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", - "version": "==4.3.0" - }, - "urllib3": { - "hashes": [ - "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" - } - } -} diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 782a0829..8ebbcf13 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -8,7 +8,7 @@ nav_order: 2 # Basic installation -You can choose between four package managers, `poetry`, `pipenv`, `venv` and `nix`. +You can choose between four package managers, `poetry`, `nix` and `venv`. By default, LNbits will use SQLite as its database. You can also use PostgreSQL which is recommended for applications with a high load (see guide below). @@ -33,35 +33,25 @@ poetry run lnbits # To change port/host pass 'poetry run lnbits --port 9000 --host 0.0.0.0' ``` -## Option 2: pipenv +## Option 2: Nix ```sh git clone https://github.com/lnbits/lnbits-legend.git cd lnbits-legend/ +# Modern debian distros usually include Nix, however you can install with: +# 'sh <(curl -L https://nixos.org/nix/install) --daemon', or use setup here https://nixos.org/download.html#nix-verify-installation -sudo apt update && sudo apt install -y pipenv -pipenv install --dev -# pipenv --python 3.9 install --dev (if you wish to use a version of Python higher than 3.7) -pipenv shell -# pipenv --python 3.9 shell (if you wish to use a version of Python higher than 3.7) +nix build .#lnbits +mkdir data -# If any of the modules fails to install, try checking and upgrading your setupTool module -# pip install -U setuptools wheel - -# install libffi/libpq in case "pipenv install" fails -# sudo apt-get install -y libffi-dev libpq-dev - - mkdir data && cp .env.example .env -``` - -#### Running the server - -```sh -pipenv run python -m uvicorn lnbits.__main__:app --port 5000 --host 0.0.0.0 ``` -Add the flag `--reload` for development (includes hot-reload). +#### Running the server +```sh +# .env variables are currently passed when running +LNBITS_DATA_FOLDER=data LNBITS_BACKEND_WALLET_CLASS=LNbitsWallet LNBITS_ENDPOINT=https://legend.lnbits.com LNBITS_KEY=7b1a78d6c78f48b09a202f2dcb2d22eb ./result/bin/lnbits --port 9000 +``` ## Option 3: venv @@ -84,26 +74,6 @@ mkdir data && cp .env.example .env If you want to host LNbits on the internet, run with the option `--host 0.0.0.0`. -## Option 4: Nix - -```sh -git clone https://github.com/lnbits/lnbits-legend.git -cd lnbits-legend/ -# Modern debian distros usually include Nix, however you can install with: -# 'sh <(curl -L https://nixos.org/nix/install) --daemon', or use setup here https://nixos.org/download.html#nix-verify-installation - -nix build .#lnbits -mkdir data - -``` - -#### Running the server - -```sh -# .env variables are currently passed when running -LNBITS_DATA_FOLDER=data LNBITS_BACKEND_WALLET_CLASS=LNbitsWallet LNBITS_ENDPOINT=https://legend.lnbits.com LNBITS_KEY=7b1a78d6c78f48b09a202f2dcb2d22eb ./result/bin/lnbits --port 9000 -``` - ### Troubleshooting Problems installing? These commands have helped us install LNbits. From 19b6acf93217b065f44b3cac2746f76c68c17130 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 28 Jul 2022 11:23:03 +0100 Subject: [PATCH 77/78] removed pipenv tests --- .github/workflows/tests.yml | 59 +++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 218a557b..15a2388a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -68,11 +68,11 @@ jobs: uses: codecov/codecov-action@v3 with: file: ./coverage.xml - pipenv-sqlite: + poetry-sqlite: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.8, 3.9] + python-version: [3.9] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} @@ -80,9 +80,56 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install dependencies + env: + VIRTUAL_ENV: ./venv + PATH: ${{ env.VIRTUAL_ENV }}/bin:${{ env.PATH }} run: | - pip install pipenv - pipenv install --dev - pipenv install importlib-metadata + python -m venv ${{ env.VIRTUAL_ENV }} + ./venv/bin/python -m pip install --upgrade pip + ./venv/bin/pip install -r requirements.txt + ./venv/bin/pip install pytest pytest-asyncio pytest-cov requests mock - name: Run tests - run: make test-pipenv \ No newline at end of file + run: make test + poetry-postgres: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:latest + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres + ports: + # maps tcp port 5432 on service container to the host + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + strategy: + matrix: + python-version: [3.9] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + env: + VIRTUAL_ENV: ./venv + PATH: ${{ env.VIRTUAL_ENV }}/bin:${{ env.PATH }} + run: | + python -m venv ${{ env.VIRTUAL_ENV }} + ./venv/bin/python -m pip install --upgrade pip + ./venv/bin/pip install -r requirements.txt + ./venv/bin/pip install pytest pytest-asyncio pytest-cov requests mock + - name: Run tests + env: + LNBITS_DATABASE_URL: postgres://postgres:postgres@0.0.0.0:5432/postgres + run: make test + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml \ No newline at end of file From a7d27467f00fccfa7a21edb505a96c1e1d34180c Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 28 Jul 2022 11:43:31 +0100 Subject: [PATCH 78/78] Updated install docs --- docs/devs/installation.md | 2 +- docs/guide/installation.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/devs/installation.md b/docs/devs/installation.md index f4d6b145..b807ce34 100644 --- a/docs/devs/installation.md +++ b/docs/devs/installation.md @@ -8,7 +8,7 @@ nav_order: 1 # Installation This guide has been moved to the [installation guide](../guide/installation.md). -To install the developer packages, use `pipenv install --dev`. +To install the developer packages for running tests etc before pr'ing, use `./venv/bin/pip install pytest pytest-asyncio pytest-cov requests mock black mypy isort`. ## Notes: diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 8ebbcf13..1c2a757a 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -82,10 +82,10 @@ Problems installing? These commands have helped us install LNbits. sudo apt install pkg-config libffi-dev libpq-dev # if the secp256k1 build fails: -# if you used pipenv (option 1) -pipenv install setuptools wheel -# if you used venv (option 2) +# if you used venv ./venv/bin/pip install setuptools wheel +# if you used poetry +poetry add setuptools wheel # build essentials for debian/ubuntu sudo apt install python3-dev gcc build-essential ```