From c4924bcf24f1c6e9edcbea1ef0ac164a25417481 Mon Sep 17 00:00:00 2001
From: matthewcroughan
Date: Fri, 8 Jul 2022 09:20:38 +0100
Subject: [PATCH 01/84] gitignore: add Nix ignores
---
.gitignore | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.gitignore b/.gitignore
index 07ee15b86..b70f620d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,3 +34,6 @@ __bundle__
node_modules
lnbits/static/bundle.*
docker
+
+# Nix
+*result*
From 50a5f47308ccce2f367b5e2b350b043480a9468d Mon Sep 17 00:00:00 2001
From: matthewcroughan
Date: Fri, 8 Jul 2022 09:22:35 +0100
Subject: [PATCH 02/84] Add Poetry as a build system
---
build.py | 110 +++++
poetry.lock | 1165 ++++++++++++++++++++++++++++++++++++++++++++++++
pyproject.toml | 70 +++
3 files changed, 1345 insertions(+)
create mode 100644 build.py
create mode 100644 poetry.lock
create mode 100644 pyproject.toml
diff --git a/build.py b/build.py
new file mode 100644
index 000000000..5cc8b7de1
--- /dev/null
+++ b/build.py
@@ -0,0 +1,110 @@
+import warnings
+import subprocess
+import glob
+import os
+from os import path
+from typing import Any, List, NamedTuple, Optional
+from pathlib import Path
+
+LNBITS_PATH = path.dirname(path.realpath(__file__)) + "/lnbits"
+
+def get_js_vendored(prefer_minified: bool = False) -> List[str]:
+ paths = get_vendored(".js", prefer_minified)
+
+ def sorter(key: str):
+ if "moment@" in key:
+ return 1
+ if "vue@" in key:
+ return 2
+ if "vue-router@" in key:
+ return 3
+ if "polyfills" in key:
+ return 4
+ return 9
+
+ return sorted(paths, key=sorter)
+
+
+def get_css_vendored(prefer_minified: bool = False) -> List[str]:
+ paths = get_vendored(".css", prefer_minified)
+
+ def sorter(key: str):
+ if "quasar@" in key:
+ return 1
+ if "vue@" in key:
+ return 2
+ if "chart.js@" in key:
+ return 100
+ return 9
+
+ return sorted(paths, key=sorter)
+
+
+def get_vendored(ext: str, prefer_minified: bool = False) -> List[str]:
+ paths: List[str] = []
+ for path in glob.glob(
+ os.path.join(LNBITS_PATH, "static/vendor/**"), recursive=True
+ ):
+ if path.endswith(".min" + ext):
+ # path is minified
+ unminified = path.replace(".min" + ext, ext)
+ if prefer_minified:
+ paths.append(path)
+ if unminified in paths:
+ paths.remove(unminified)
+ elif unminified not in paths:
+ paths.append(path)
+
+ elif path.endswith(ext):
+ # path is not minified
+ minified = path.replace(ext, ".min" + ext)
+ if not prefer_minified:
+ paths.append(path)
+ if minified in paths:
+ paths.remove(minified)
+ elif minified not in paths:
+ paths.append(path)
+
+ return sorted(paths)
+
+
+def url_for_vendored(abspath: str) -> str:
+ return "/" + os.path.relpath(abspath, LNBITS_PATH)
+
+def transpile_scss():
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ from scss.compiler import compile_string # type: ignore
+
+ with open(os.path.join(LNBITS_PATH, "static/scss/base.scss")) as scss:
+ with open(os.path.join(LNBITS_PATH, "static/css/base.css"), "w") as css:
+ css.write(compile_string(scss.read()))
+
+def bundle_vendored():
+ for getfiles, outputpath in [
+ (get_js_vendored, os.path.join(LNBITS_PATH, "static/bundle.js")),
+ (get_css_vendored, os.path.join(LNBITS_PATH, "static/bundle.css")),
+ ]:
+ output = ""
+ for path in getfiles():
+ with open(path) as f:
+ output += "/* " + url_for_vendored(path) + " */\n" + f.read() + ";\n"
+ with open(outputpath, "w") as f:
+ f.write(output)
+
+
+def build():
+ transpile_scss()
+ bundle_vendored()
+# root = Path("lnbits/static/foo")
+# root.mkdir(parents=True)
+# root.joinpath("example.css").write_text("")
+
+if __name__ == "__main__":
+ build()
+
+#def build(setup_kwargs):
+# """Build """
+# transpile_scss()
+# bundle_vendored()
+# subprocess.run(["ls", "-la", "./lnbits/static"])
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 000000000..48c508ceb
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,1165 @@
+[[package]]
+name = "aiofiles"
+version = "0.7.0"
+description = "File support for asyncio."
+category = "main"
+optional = false
+python-versions = ">=3.6,<4.0"
+
+[[package]]
+name = "anyio"
+version = "3.6.1"
+description = "High level compatibility layer for multiple asynchronous event loop implementations"
+category = "main"
+optional = false
+python-versions = ">=3.6.2"
+
+[package.dependencies]
+idna = ">=2.8"
+sniffio = ">=1.1"
+
+[package.extras]
+doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
+test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"]
+trio = ["trio (>=0.16)"]
+
+[[package]]
+name = "asgiref"
+version = "3.4.1"
+description = "ASGI specs, helper code, and adapters"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
+
+[[package]]
+name = "attrs"
+version = "21.2.0"
+description = "Classes Without Boilerplate"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.extras]
+dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
+docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
+tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
+
+[[package]]
+name = "bech32"
+version = "1.2.0"
+description = "Reference implementation for Bech32 and segwit addresses."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "bitstring"
+version = "3.1.9"
+description = "Simple construction, analysis and modification of binary data."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "cerberus"
+version = "1.3.4"
+description = "Lightweight, extensible schema and data validation tool for Python dictionaries."
+category = "main"
+optional = false
+python-versions = ">=2.7"
+
+[[package]]
+name = "certifi"
+version = "2021.5.30"
+description = "Python package for providing Mozilla's CA Bundle."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "cffi"
+version = "1.15.0"
+description = "Foreign Function Interface for Python calling C code."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+pycparser = "*"
+
+[[package]]
+name = "charset-normalizer"
+version = "2.0.6"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+category = "main"
+optional = false
+python-versions = ">=3.5.0"
+
+[package.extras]
+unicode_backport = ["unicodedata2"]
+
+[[package]]
+name = "click"
+version = "8.0.1"
+description = "Composable command line interface toolkit"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "colorama"
+version = "0.4.5"
+description = "Cross-platform colored terminal text."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "ecdsa"
+version = "0.17.0"
+description = "ECDSA cryptographic signature library (pure python)"
+category = "main"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[package.dependencies]
+six = ">=1.9.0"
+
+[package.extras]
+gmpy = ["gmpy"]
+gmpy2 = ["gmpy2"]
+
+[[package]]
+name = "embit"
+version = "0.4.9"
+description = "yet another bitcoin library"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "environs"
+version = "9.3.3"
+description = "simplified environment variable parsing"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+marshmallow = ">=3.0.0"
+python-dotenv = "*"
+
+[package.extras]
+dev = ["pytest", "dj-database-url", "dj-email-url", "django-cache-url", "flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)", "tox"]
+django = ["dj-database-url", "dj-email-url", "django-cache-url"]
+lint = ["flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "mypy (==0.910)", "pre-commit (>=2.4,<3.0)"]
+tests = ["pytest", "dj-database-url", "dj-email-url", "django-cache-url"]
+
+[[package]]
+name = "fastapi"
+version = "0.78.0"
+description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0"
+starlette = "0.19.1"
+
+[package.extras]
+all = ["requests (>=2.24.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<3.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
+dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)", "pre-commit (>=2.17.0,<3.0.0)"]
+doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer (>=0.4.1,<0.5.0)", "pyyaml (>=5.3.1,<7.0.0)"]
+test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==22.3.0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "flask (>=1.1.2,<3.0.0)", "anyio[trio] (>=3.2.1,<4.0.0)", "types-ujson (==4.2.1)", "types-orjson (==3.6.2)", "types-dataclasses (==0.6.5)"]
+
+[[package]]
+name = "h11"
+version = "0.12.0"
+description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "httpcore"
+version = "0.13.7"
+description = "A minimal low-level HTTP client."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+anyio = ">=3.0.0,<4.0.0"
+h11 = ">=0.11,<0.13"
+sniffio = ">=1.0.0,<2.0.0"
+
+[package.extras]
+http2 = ["h2 (>=3,<5)"]
+
+[[package]]
+name = "httptools"
+version = "0.2.0"
+description = "A collection of framework independent HTTP protocol utils."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.extras]
+test = ["Cython (==0.29.22)"]
+
+[[package]]
+name = "httpx"
+version = "0.19.0"
+description = "The next generation HTTP client."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+certifi = "*"
+charset-normalizer = "*"
+httpcore = ">=0.13.3,<0.14.0"
+rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
+sniffio = "*"
+
+[package.extras]
+brotli = ["brotlicffi", "brotli"]
+http2 = ["h2 (>=3,<5)"]
+
+[[package]]
+name = "idna"
+version = "3.2"
+description = "Internationalized Domain Names in Applications (IDNA)"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "importlib-metadata"
+version = "4.8.1"
+description = "Read metadata from Python packages"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+zipp = ">=0.5"
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+perf = ["ipython"]
+testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
+
+[[package]]
+name = "jinja2"
+version = "3.0.1"
+description = "A very fast and expressive template engine."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+MarkupSafe = ">=2.0"
+
+[package.extras]
+i18n = ["Babel (>=2.7)"]
+
+[[package]]
+name = "lnurl"
+version = "0.3.6"
+description = "LNURL implementation for Python."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+bech32 = "*"
+pydantic = "*"
+
+[[package]]
+name = "loguru"
+version = "0.5.3"
+description = "Python logging made (stupidly) simple"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
+win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
+
+[package.extras]
+dev = ["codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "tox (>=3.9.0)", "tox-travis (>=0.12)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "Sphinx (>=2.2.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "black (>=19.10b0)", "isort (>=5.1.1)"]
+
+[[package]]
+name = "markupsafe"
+version = "2.0.1"
+description = "Safely add untrusted strings to HTML/XML markup."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "marshmallow"
+version = "3.13.0"
+description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+dev = ["pytest", "pytz", "simplejson", "mypy (==0.910)", "flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "pre-commit (>=2.4,<3.0)", "tox"]
+docs = ["sphinx (==4.1.1)", "sphinx-issues (==1.2.0)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.6)"]
+lint = ["mypy (==0.910)", "flake8 (==3.9.2)", "flake8-bugbear (==21.4.3)", "pre-commit (>=2.4,<3.0)"]
+tests = ["pytest", "pytz", "simplejson"]
+
+[[package]]
+name = "outcome"
+version = "1.1.0"
+description = "Capture the outcome of Python function calls."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+attrs = ">=19.2.0"
+
+[[package]]
+name = "psycopg2-binary"
+version = "2.9.1"
+description = "psycopg2 - Python-PostgreSQL Database Adapter"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "pycparser"
+version = "2.21"
+description = "C parser in Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "pycryptodomex"
+version = "3.14.1"
+description = "Cryptographic library for Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "pydantic"
+version = "1.8.2"
+description = "Data validation and settings management using python 3.6 type hinting"
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+typing-extensions = ">=3.7.4.3"
+
+[package.extras]
+dotenv = ["python-dotenv (>=0.10.4)"]
+email = ["email-validator (>=1.0.3)"]
+
+[[package]]
+name = "pypng"
+version = "0.0.21"
+description = "Pure Python library for saving and loading PNG images"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pyqrcode"
+version = "1.2.1"
+description = "A QR code generator written purely in Python with SVG, EPS, PNG and terminal output."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.extras]
+PNG = ["pypng (>=0.0.13)"]
+
+[[package]]
+name = "pyscss"
+version = "1.3.7"
+description = "pyScss, a Scss compiler for Python"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+six = "*"
+
+[[package]]
+name = "python-dotenv"
+version = "0.19.0"
+description = "Read key-value pairs from a .env file and set them as environment variables"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+cli = ["click (>=5.0)"]
+
+[[package]]
+name = "pyyaml"
+version = "5.4.1"
+description = "YAML parser and emitter for Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[[package]]
+name = "represent"
+version = "1.6.0.post0"
+description = "Create __repr__ automatically or declaratively."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.dependencies]
+six = ">=1.8.0"
+
+[package.extras]
+test = ["ipython", "pytest (>=3.0.5)", "mock"]
+
+[[package]]
+name = "rfc3986"
+version = "1.5.0"
+description = "Validating URI References per RFC 3986"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+idna = {version = "*", optional = true, markers = "extra == \"idna2008\""}
+
+[package.extras]
+idna2008 = ["idna"]
+
+[[package]]
+name = "secp256k1"
+version = "0.14.0"
+description = "FFI bindings to libsecp256k1"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+cffi = ">=1.3.0"
+
+[[package]]
+name = "shortuuid"
+version = "1.0.1"
+description = "A generator library for concise, unambiguous and URL-safe UUIDs."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "sniffio"
+version = "1.2.0"
+description = "Sniff out which async library your code is running under"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "sqlalchemy"
+version = "1.3.23"
+description = "Database Abstraction Library"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.extras]
+mssql = ["pyodbc"]
+mssql_pymssql = ["pymssql"]
+mssql_pyodbc = ["pyodbc"]
+mysql = ["mysqlclient"]
+oracle = ["cx-oracle"]
+postgresql = ["psycopg2"]
+postgresql_pg8000 = ["pg8000 (<1.16.6)"]
+postgresql_psycopg2binary = ["psycopg2-binary"]
+postgresql_psycopg2cffi = ["psycopg2cffi"]
+pymysql = ["pymysql (<1)", "pymysql"]
+
+[[package]]
+name = "sqlalchemy-aio"
+version = "0.16.0"
+description = "Async support for SQLAlchemy."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+outcome = "*"
+represent = ">=1.4"
+sqlalchemy = "*"
+
+[package.extras]
+test = ["pytest (>=5.4)", "pytest-asyncio (>=0.14)", "pytest-trio (>=0.6)"]
+test-noextras = ["pytest (>=5.4)", "pytest-asyncio (>=0.14)"]
+trio = ["trio (>=0.15)"]
+
+[[package]]
+name = "sse-starlette"
+version = "0.6.2"
+description = "SSE plugin for Starlette"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "starlette"
+version = "0.19.1"
+description = "The little ASGI library that shines."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+anyio = ">=3.4.0,<5"
+typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
+
+[[package]]
+name = "typing-extensions"
+version = "3.10.0.2"
+description = "Backported and Experimental Type Hints for Python 3.5+"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "uvicorn"
+version = "0.18.1"
+description = "The lightning-fast ASGI server."
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+click = ">=7.0"
+h11 = ">=0.8"
+
+[package.extras]
+standard = ["websockets (>=10.0)", "httptools (>=0.4.0)", "watchfiles (>=0.13)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"]
+
+[[package]]
+name = "uvloop"
+version = "0.16.0"
+description = "Fast implementation of asyncio event loop on top of libuv"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+dev = ["Cython (>=0.29.24,<0.30.0)", "pytest (>=3.6.0)", "Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"]
+docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)"]
+test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"]
+
+[[package]]
+name = "watchgod"
+version = "0.7"
+description = "Simple, modern file watching and code reload in python."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "websockets"
+version = "10.0"
+description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "win32-setctime"
+version = "1.1.0"
+description = "A small Python utility to set file creation time on Windows"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
+
+[[package]]
+name = "zipp"
+version = "3.5.0"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
+
+[metadata]
+lock-version = "1.1"
+python-versions = "^3.9"
+content-hash = "921a5f4fe1a4d1a4c3b490f8631ed4bdd0d8af1f1992f1a4f74eaed986c4eb0b"
+
+[metadata.files]
+aiofiles = [
+ {file = "aiofiles-0.7.0-py3-none-any.whl", hash = "sha256:c67a6823b5f23fcab0a2595a289cec7d8c863ffcb4322fb8cd6b90400aedfdbc"},
+ {file = "aiofiles-0.7.0.tar.gz", hash = "sha256:a1c4fc9b2ff81568c83e21392a82f344ea9d23da906e4f6a52662764545e19d4"},
+]
+anyio = [
+ {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"},
+ {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"},
+]
+asgiref = [
+ {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
+ {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"},
+]
+attrs = [
+ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
+ {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
+]
+bech32 = [
+ {file = "bech32-1.2.0-py3-none-any.whl", hash = "sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981"},
+ {file = "bech32-1.2.0.tar.gz", hash = "sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899"},
+]
+bitstring = [
+ {file = "bitstring-3.1.9-py2-none-any.whl", hash = "sha256:e3e340e58900a948787a05e8c08772f1ccbe133f6f41fe3f0fa19a18a22bbf4f"},
+ {file = "bitstring-3.1.9-py3-none-any.whl", hash = "sha256:0de167daa6a00c9386255a7cac931b45e6e24e0ad7ea64f1f92a64ac23ad4578"},
+ {file = "bitstring-3.1.9.tar.gz", hash = "sha256:a5848a3f63111785224dca8bb4c0a75b62ecdef56a042c8d6be74b16f7e860e7"},
+]
+cerberus = [
+ {file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"},
+]
+certifi = [
+ {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"},
+ {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"},
+]
+cffi = [
+ {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"},
+ {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"},
+ {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"},
+ {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"},
+ {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"},
+ {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"},
+ {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"},
+ {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"},
+ {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"},
+ {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"},
+ {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"},
+ {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"},
+ {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"},
+ {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"},
+ {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"},
+ {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"},
+ {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"},
+ {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"},
+ {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"},
+ {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"},
+ {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"},
+ {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"},
+ {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"},
+ {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"},
+ {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
+]
+charset-normalizer = [
+ {file = "charset-normalizer-2.0.6.tar.gz", hash = "sha256:5ec46d183433dcbd0ab716f2d7f29d8dee50505b3fdb40c6b985c7c4f5a3591f"},
+ {file = "charset_normalizer-2.0.6-py3-none-any.whl", hash = "sha256:5d209c0a931f215cee683b6445e2d77677e7e75e159f78def0db09d68fafcaa6"},
+]
+click = [
+ {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
+ {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
+]
+colorama = [
+ {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
+ {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
+]
+ecdsa = [
+ {file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"},
+ {file = "ecdsa-0.17.0.tar.gz", hash = "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"},
+]
+embit = [
+ {file = "embit-0.4.9.tar.gz", hash = "sha256:992332bd89af6e2d027e26fe437eb14aa33997db08c882c49064d49c3e6f4ab9"},
+]
+environs = [
+ {file = "environs-9.3.3-py2.py3-none-any.whl", hash = "sha256:ee5466156b50fe03aa9fec6e720feea577b5bf515d7f21b2c46608272557ba26"},
+ {file = "environs-9.3.3.tar.gz", hash = "sha256:72b867ff7b553076cdd90f3ee01ecc1cf854987639c9c459f0ed0d3d44ae490c"},
+]
+fastapi = [
+ {file = "fastapi-0.78.0-py3-none-any.whl", hash = "sha256:15fcabd5c78c266fa7ae7d8de9b384bfc2375ee0503463a6febbe3bab69d6f65"},
+ {file = "fastapi-0.78.0.tar.gz", hash = "sha256:3233d4a789ba018578658e2af1a4bb5e38bdd122ff722b313666a9b2c6786a83"},
+]
+h11 = [
+ {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
+ {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
+]
+httpcore = [
+ {file = "httpcore-0.13.7-py3-none-any.whl", hash = "sha256:369aa481b014cf046f7067fddd67d00560f2f00426e79569d99cb11245134af0"},
+ {file = "httpcore-0.13.7.tar.gz", hash = "sha256:036f960468759e633574d7c121afba48af6419615d36ab8ede979f1ad6276fa3"},
+]
+httptools = [
+ {file = "httptools-0.2.0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:79dbc21f3612a78b28384e989b21872e2e3cf3968532601544696e4ed0007ce5"},
+ {file = "httptools-0.2.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:78d03dd39b09c99ec917d50189e6743adbfd18c15d5944392d2eabda688bf149"},
+ {file = "httptools-0.2.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a23166e5ae2775709cf4f7ad4c2048755ebfb272767d244e1a96d55ac775cca7"},
+ {file = "httptools-0.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3ab1f390d8867f74b3b5ee2a7ecc9b8d7f53750bd45714bf1cb72a953d7dfa77"},
+ {file = "httptools-0.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a7594f9a010cdf1e16a58b3bf26c9da39bbf663e3b8d46d39176999d71816658"},
+ {file = "httptools-0.2.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:01b392a166adcc8bc2f526a939a8aabf89fe079243e1543fd0e7dc1b58d737cb"},
+ {file = "httptools-0.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:80ffa04fe8c8dfacf6e4cef8277347d35b0442c581f5814f3b0cf41b65c43c6e"},
+ {file = "httptools-0.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d5682eeb10cca0606c4a8286a3391d4c3c5a36f0c448e71b8bd05be4e1694bfb"},
+ {file = "httptools-0.2.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:a289c27ccae399a70eacf32df9a44059ca2ba4ac444604b00a19a6c1f0809943"},
+ {file = "httptools-0.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:813871f961edea6cb2fe312f2d9b27d12a51ba92545380126f80d0de1917ea15"},
+ {file = "httptools-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:cc9be041e428c10f8b6ab358c6b393648f9457094e1dcc11b4906026d43cd380"},
+ {file = "httptools-0.2.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b08d00d889a118f68f37f3c43e359aab24ee29eb2e3fe96d64c6a2ba8b9d6557"},
+ {file = "httptools-0.2.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fd3b8905e21431ad306eeaf56644a68fdd621bf8f3097eff54d0f6bdf7262065"},
+ {file = "httptools-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:200fc1cdf733a9ff554c0bb97a4047785cfaad9875307d6087001db3eb2b417f"},
+ {file = "httptools-0.2.0.tar.gz", hash = "sha256:94505026be56652d7a530ab03d89474dc6021019d6b8682281977163b3471ea0"},
+]
+httpx = [
+ {file = "httpx-0.19.0-py3-none-any.whl", hash = "sha256:9bd728a6c5ec0a9e243932a9983d57d3cc4a87bb4f554e1360fce407f78f9435"},
+ {file = "httpx-0.19.0.tar.gz", hash = "sha256:92ecd2c00c688b529eda11cedb15161eaf02dee9116712f621c70d9a40b2cdd0"},
+]
+idna = [
+ {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"},
+ {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"},
+]
+importlib-metadata = [
+ {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"},
+ {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"},
+]
+jinja2 = [
+ {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"},
+ {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"},
+]
+lnurl = [
+ {file = "lnurl-0.3.6-py3-none-any.whl", hash = "sha256:579982fd8c4d25bc84c61c74ec45cb7999fa1fa2426f5d5aeb0160ba333b9c92"},
+ {file = "lnurl-0.3.6.tar.gz", hash = "sha256:8af07460115a48f3122a5a9c9a6062bee3897d5f6ab4c9a60f6561a83a8234f6"},
+]
+loguru = [
+ {file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"},
+ {file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"},
+]
+markupsafe = [
+ {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
+ {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
+]
+marshmallow = [
+ {file = "marshmallow-3.13.0-py2.py3-none-any.whl", hash = "sha256:dd4724335d3c2b870b641ffe4a2f8728a1380cd2e7e2312756715ffeaa82b842"},
+ {file = "marshmallow-3.13.0.tar.gz", hash = "sha256:c67929438fd73a2be92128caa0325b1b5ed8b626d91a094d2f7f2771bf1f1c0e"},
+]
+outcome = [
+ {file = "outcome-1.1.0-py2.py3-none-any.whl", hash = "sha256:c7dd9375cfd3c12db9801d080a3b63d4b0a261aa996c4c13152380587288d958"},
+ {file = "outcome-1.1.0.tar.gz", hash = "sha256:e862f01d4e626e63e8f92c38d1f8d5546d3f9cce989263c521b2e7990d186967"},
+]
+psycopg2-binary = [
+ {file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:24b0b6688b9f31a911f2361fe818492650795c9e5d3a1bc647acbd7440142a4f"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:542875f62bc56e91c6eac05a0deadeae20e1730be4c6334d8f04c944fcd99759"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:661509f51531ec125e52357a489ea3806640d0ca37d9dada461ffc69ee1e7b6e"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:d92272c7c16e105788efe2cfa5d680f07e34e0c29b03c1908f8636f55d5f915a"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:736b8797b58febabb85494142c627bd182b50d2a7ec65322983e71065ad3034c"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-win32.whl", hash = "sha256:ebccf1123e7ef66efc615a68295bf6fdba875a75d5bba10a05073202598085fc"},
+ {file = "psycopg2_binary-2.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:1f6ca4a9068f5c5c57e744b4baa79f40e83e3746875cac3c45467b16326bab45"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:c250a7ec489b652c892e4f0a5d122cc14c3780f9f643e1a326754aedf82d9a76"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aef9aee84ec78af51107181d02fe8773b100b01c5dfde351184ad9223eab3698"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123c3fb684e9abfc47218d3784c7b4c47c8587951ea4dd5bc38b6636ac57f616"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:995fc41ebda5a7a663a254a1dcac52638c3e847f48307b5416ee373da15075d7"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:fbb42a541b1093385a2d8c7eec94d26d30437d0e77c1d25dae1dcc46741a385e"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-win32.whl", hash = "sha256:20f1ab44d8c352074e2d7ca67dc00843067788791be373e67a0911998787ce7d"},
+ {file = "psycopg2_binary-2.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f6fac64a38f6768e7bc7b035b9e10d8a538a9fadce06b983fb3e6fa55ac5f5ce"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:1e3a362790edc0a365385b1ac4cc0acc429a0c0d662d829a50b6ce743ae61b5a"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f8559617b1fcf59a9aedba2c9838b5b6aa211ffedecabca412b92a1ff75aac1a"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a36c7eb6152ba5467fb264d73844877be8b0847874d4822b7cf2d3c0cb8cdcb0"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:2f62c207d1740b0bde5c4e949f857b044818f734a3d57f1d0d0edc65050532ed"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:cfc523edecddaef56f6740d7de1ce24a2fdf94fd5e704091856a201872e37f9f"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-win32.whl", hash = "sha256:1e85b74cbbb3056e3656f1cc4781294df03383127a8114cbc6531e8b8367bf1e"},
+ {file = "psycopg2_binary-2.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1473c0215b0613dd938db54a653f68251a45a78b05f6fc21af4326f40e8360a2"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:35c4310f8febe41f442d3c65066ca93cccefd75013df3d8c736c5b93ec288140"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c13d72ed6af7fd2c8acbd95661cf9477f94e381fce0792c04981a8283b52917"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14db1752acdd2187d99cb2ca0a1a6dfe57fc65c3281e0f20e597aac8d2a5bd90"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:aed4a9a7e3221b3e252c39d0bf794c438dc5453bc2963e8befe9d4cd324dff72"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:da113b70f6ec40e7d81b43d1b139b9db6a05727ab8be1ee559f3a69854a69d34"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-win32.whl", hash = "sha256:4235f9d5ddcab0b8dbd723dca56ea2922b485ea00e1dafacf33b0c7e840b3d32"},
+ {file = "psycopg2_binary-2.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:988b47ac70d204aed01589ed342303da7c4d84b56c2f4c4b8b00deda123372bf"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:7360647ea04db2e7dff1648d1da825c8cf68dc5fbd80b8fb5b3ee9f068dcd21a"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca86db5b561b894f9e5f115d6a159fff2a2570a652e07889d8a383b5fae66eb4"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ced67f1e34e1a450cdb48eb53ca73b60aa0af21c46b9b35ac3e581cf9f00e31"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:0f2e04bd2a2ab54fa44ee67fe2d002bb90cee1c0f1cc0ebc3148af7b02034cbd"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:3242b9619de955ab44581a03a64bdd7d5e470cc4183e8fcadd85ab9d3756ce7a"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-win32.whl", hash = "sha256:0b7dae87f0b729922e06f85f667de7bf16455d411971b2043bbd9577af9d1975"},
+ {file = "psycopg2_binary-2.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:b4d7679a08fea64573c969f6994a2631908bb2c0e69a7235648642f3d2e39a68"},
+]
+pycparser = [
+ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
+ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
+]
+pycryptodomex = [
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca88f2f7020002638276439a01ffbb0355634907d1aa5ca91f3dc0c2e44e8f3b"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:8536bc08d130cae6dcba1ea689f2913dfd332d06113904d171f2f56da6228e89"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:406ec8cfe0c098fadb18d597dc2ee6de4428d640c0ccafa453f3d9b2e58d29e2"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:da8db8374295fb532b4b0c467e66800ef17d100e4d5faa2bbbd6df35502da125"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:d709572d64825d8d59ea112e11cc7faf6007f294e9951324b7574af4251e4de8"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-win32.whl", hash = "sha256:3da13c2535b7aea94cc2a6d1b1b37746814c74b6e80790daddd55ca5c120a489"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27m-win_amd64.whl", hash = "sha256:298c00ea41a81a491d5b244d295d18369e5aac4b61b77b2de5b249ca61cd6659"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:77931df40bb5ce5e13f4de2bfc982b2ddc0198971fbd947776c8bb5050896eb2"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:c5dd3ffa663c982d7f1be9eb494a8924f6d40e2e2f7d1d27384cfab1b2ac0662"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:2aa887683eee493e015545bd69d3d21ac8d5ad582674ec98f4af84511e353e45"},
+ {file = "pycryptodomex-3.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:8085bd0ad2034352eee4d4f3e2da985c2749cb7344b939f4d95ead38c2520859"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:e95a4a6c54d27a84a4624d2af8bb9ee178111604653194ca6880c98dcad92f48"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-manylinux1_i686.whl", hash = "sha256:a4d412eba5679ede84b41dbe48b1bed8f33131ab9db06c238a235334733acc5e"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:d2cce1c82a7845d7e2e8a0956c6b7ed3f1661c9acf18eb120fc71e098ab5c6fe"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:f75009715dcf4a3d680c2338ab19dac5498f8121173a929872950f4fb3a48fbf"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:1ca8e1b4c62038bb2da55451385246f51f412c5f5eabd64812c01766a5989b4a"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-win32.whl", hash = "sha256:ee835def05622e0c8b1435a906491760a43d0c462f065ec9143ec4b8d79f8bff"},
+ {file = "pycryptodomex-3.14.1-cp35-abi3-win_amd64.whl", hash = "sha256:b5a185ae79f899b01ca49f365bdf15a45d78d9856f09b0de1a41b92afce1a07f"},
+ {file = "pycryptodomex-3.14.1-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:797a36bd1f69df9e2798e33edb4bd04e5a30478efc08f9428c087f17f65a7045"},
+ {file = "pycryptodomex-3.14.1-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:aebecde2adc4a6847094d3bd6a8a9538ef3438a5ea84ac1983fcb167db614461"},
+ {file = "pycryptodomex-3.14.1-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:f8524b8bc89470cec7ac51734907818d3620fb1637f8f8b542d650ebec42a126"},
+ {file = "pycryptodomex-3.14.1-pp27-pypy_73-win32.whl", hash = "sha256:4d0db8df9ffae36f416897ad184608d9d7a8c2b46c4612c6bc759b26c073f750"},
+ {file = "pycryptodomex-3.14.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b276cc4deb4a80f9dfd47a41ebb464b1fe91efd8b1b8620cf5ccf8b824b850d6"},
+ {file = "pycryptodomex-3.14.1-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:e36c7e3b5382cd5669cf199c4a04a0279a43b2a3bdd77627e9b89778ac9ec08c"},
+ {file = "pycryptodomex-3.14.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:c4d8977ccda886d88dc3ca789de2f1adc714df912ff3934b3d0a3f3d777deafb"},
+ {file = "pycryptodomex-3.14.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:530756d2faa40af4c1f74123e1d889bd07feae45bac2fd32f259a35f7aa74151"},
+ {file = "pycryptodomex-3.14.1.tar.gz", hash = "sha256:2ce76ed0081fd6ac8c74edc75b9d14eca2064173af79843c24fa62573263c1f2"},
+]
+pydantic = [
+ {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"},
+ {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"},
+ {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"},
+ {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"},
+ {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"},
+ {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"},
+ {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"},
+ {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"},
+ {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"},
+ {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"},
+ {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"},
+ {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"},
+ {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"},
+ {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"},
+]
+pypng = [
+ {file = "pypng-0.0.21-py3-none-any.whl", hash = "sha256:76f8a1539ec56451da7ab7121f12a361969fe0f2d48d703d198ce2a99d6c5afd"},
+]
+pyqrcode = [
+ {file = "PyQRCode-1.2.1.tar.gz", hash = "sha256:fdbf7634733e56b72e27f9bce46e4550b75a3a2c420414035cae9d9d26b234d5"},
+ {file = "PyQRCode-1.2.1.zip", hash = "sha256:1b2812775fa6ff5c527977c4cd2ccb07051ca7d0bc0aecf937a43864abe5eff6"},
+]
+pyscss = [
+ {file = "pyScss-1.3.7.tar.gz", hash = "sha256:f1df571569021a23941a538eb154405dde80bed35dc1ea7c5f3e18e0144746bf"},
+]
+python-dotenv = [
+ {file = "python-dotenv-0.19.0.tar.gz", hash = "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172"},
+ {file = "python_dotenv-0.19.0-py2.py3-none-any.whl", hash = "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1"},
+]
+pyyaml = [
+ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
+ {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"},
+ {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"},
+ {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"},
+ {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"},
+ {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"},
+ {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"},
+ {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"},
+ {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"},
+ {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
+ {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
+]
+represent = [
+ {file = "Represent-1.6.0.post0-py2.py3-none-any.whl", hash = "sha256:99142650756ef1998ce0661568f54a47dac8c638fb27e3816c02536575dbba8c"},
+ {file = "Represent-1.6.0.post0.tar.gz", hash = "sha256:026c0de2ee8385d1255b9c2426cd4f03fe9177ac94c09979bc601946c8493aa0"},
+]
+rfc3986 = [
+ {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
+ {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
+]
+secp256k1 = [
+ {file = "secp256k1-0.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f666c67dcf1dc69e1448b2ede5e12aaf382b600204a61dbc65e4f82cea444405"},
+ {file = "secp256k1-0.14.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fcabb3c3497a902fb61eec72d1b69bf72747d7bcc2a732d56d9319a1e8322262"},
+ {file = "secp256k1-0.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7a27c479ab60571502516a1506a562d0a9df062de8ad645313fabfcc97252816"},
+ {file = "secp256k1-0.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4b9306bff6dde020444dfee9ca9b9f5b20ca53a2c0b04898361a3f43d5daf2e"},
+ {file = "secp256k1-0.14.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:72735da6cb28273e924431cd40aa607e7f80ef09608c8c9300be2e0e1d2417b4"},
+ {file = "secp256k1-0.14.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:87f4ad42a370f768910585989a301d1d65de17dcd86f6e8def9b021364b34d5c"},
+ {file = "secp256k1-0.14.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:130f119b06142e597c10eb4470b5a38eae865362d01aaef06b113478d77f728d"},
+ {file = "secp256k1-0.14.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:3aedcfe6eb1c5fa7c6be25b7cc91c76d8eb984271920ba0f7a934ae41ed56f51"},
+ {file = "secp256k1-0.14.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c91dd3154f6c46ac798d9a41166120e1751222587f54516cc3f378f56ce4ac82"},
+ {file = "secp256k1-0.14.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fec790cb6d0d37129ca0ce5b3f8e85692d5fb618d1c440f189453d18694035df"},
+ {file = "secp256k1-0.14.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:63eb148196b8f646922d4be6739b17fbbf50ebb3a020078c823e2445d88b7a81"},
+ {file = "secp256k1-0.14.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:adc23a4c5d24c95191638eb2ca313097827f07db102e77b59faed15d50c98cae"},
+ {file = "secp256k1-0.14.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ce0314788d3248b275426501228969fd32f6501c9d1837902ee0e7bd8264a36f"},
+ {file = "secp256k1-0.14.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bc761894b3634021686714278fc62b73395fa3eded33453eadfd8a00a6c44ef3"},
+ {file = "secp256k1-0.14.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:373dc8bca735f3c2d73259aa2711a9ecea2f3c7edbb663555fe3422e3dd76102"},
+ {file = "secp256k1-0.14.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fe3f503c9dfdf663b500d3e0688ad842e116c2907ad3f1e1d685812df3f56290"},
+ {file = "secp256k1-0.14.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4b1bf09953cde181132cf5e9033065615e5c2694e803165e2db763efa47695e5"},
+ {file = "secp256k1-0.14.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6af07be5f8612628c3638dc7b208f6cc78d0abae3e25797eadb13890c7d5da81"},
+ {file = "secp256k1-0.14.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a8dbd75a9fb6f42de307f3c5e24573fe59c3374637cbf39136edc66c200a4029"},
+ {file = "secp256k1-0.14.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97a30c8dae633cb18135c76b6517ae99dc59106818e8985be70dbc05dcc06c0d"},
+ {file = "secp256k1-0.14.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f4062d8c101aa63b9ecb3709f1f075ad9c01b6672869bbaa1bd77271816936a7"},
+ {file = "secp256k1-0.14.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c9e7c024ff17e9b9d7c392bb2a917da231d6cb40ab119389ff1f51dca10339a4"},
+ {file = "secp256k1-0.14.0.tar.gz", hash = "sha256:82c06712d69ef945220c8b53c1a0d424c2ff6a1f64aee609030df79ad8383397"},
+]
+shortuuid = [
+ {file = "shortuuid-1.0.1-py3-none-any.whl", hash = "sha256:492c7402ff91beb1342a5898bd61ea953985bf24a41cd9f247409aa2e03c8f77"},
+ {file = "shortuuid-1.0.1.tar.gz", hash = "sha256:3c11d2007b915c43bee3e10625f068d8a349e04f0d81f08f5fa08507427ebf1f"},
+]
+six = [
+ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+sniffio = [
+ {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
+ {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
+]
+sqlalchemy = [
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:fd3b96f8c705af8e938eaa99cbd8fd1450f632d38cad55e7367c33b263bf98ec"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:29cccc9606750fe10c5d0e8bd847f17a97f3850b8682aef1f56f5d5e1a5a64b1"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:927ce09e49bff3104459e1451ce82983b0a3062437a07d883a4c66f0b344c9b5"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-win32.whl", hash = "sha256:b4b0e44d586cd64b65b507fa116a3814a1a53d55dce4836d7c1a6eb2823ff8d1"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27m-win_amd64.whl", hash = "sha256:6b8b8c80c7f384f06825612dd078e4a31f0185e8f1f6b8c19e188ff246334205"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9e9c25522933e569e8b53ccc644dc993cab87e922fb7e142894653880fdd419d"},
+ {file = "SQLAlchemy-1.3.23-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:a0e306e9bb76fd93b29ae3a5155298e4c1b504c7cbc620c09c20858d32d16234"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:6c9e6cc9237de5660bcddea63f332428bb83c8e2015c26777281f7ffbd2efb84"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:94f667d86be82dd4cb17d08de0c3622e77ca865320e0b95eae6153faa7b4ecaf"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:751934967f5336a3e26fc5993ccad1e4fee982029f9317eb6153bc0bc3d2d2da"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:63677d0c08524af4c5893c18dbe42141de7178001360b3de0b86217502ed3601"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-win32.whl", hash = "sha256:ddfb511e76d016c3a160910642d57f4587dc542ce5ee823b0d415134790eeeb9"},
+ {file = "SQLAlchemy-1.3.23-cp35-cp35m-win_amd64.whl", hash = "sha256:040bdfc1d76a9074717a3f43455685f781c581f94472b010cd6c4754754e1862"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:d1a85dfc5dee741bf49cb9b6b6b8d2725a268e4992507cf151cba26b17d97c37"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:639940bbe1108ac667dcffc79925db2966826c270112e9159439ab6bb14f8d80"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e8a1750b44ad6422ace82bf3466638f1aa0862dbb9689690d5f2f48cce3476c8"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e5bb3463df697279e5459a7316ad5a60b04b0107f9392e88674d0ece70e9cf70"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-win32.whl", hash = "sha256:e273367f4076bd7b9a8dc2e771978ef2bfd6b82526e80775a7db52bff8ca01dd"},
+ {file = "SQLAlchemy-1.3.23-cp36-cp36m-win_amd64.whl", hash = "sha256:ac2244e64485c3778f012951fdc869969a736cd61375fde6096d08850d8be729"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:23927c3981d1ec6b4ea71eb99d28424b874d9c696a21e5fbd9fa322718be3708"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d90010304abb4102123d10cbad2cdf2c25a9f2e66a50974199b24b468509bad5"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a8bfc1e1afe523e94974132d7230b82ca7fa2511aedde1f537ec54db0399541a"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:269990b3ab53cb035d662dcde51df0943c1417bdab707dc4a7e4114a710504b4"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-win32.whl", hash = "sha256:fdd2ed7395df8ac2dbb10cefc44737b66c6a5cd7755c92524733d7a443e5b7e2"},
+ {file = "SQLAlchemy-1.3.23-cp37-cp37m-win_amd64.whl", hash = "sha256:6a939a868fdaa4b504e8b9d4a61f21aac11e3fecc8a8214455e144939e3d2aea"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:24f9569e82a009a09ce2d263559acb3466eba2617203170e4a0af91e75b4f075"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2578dbdbe4dbb0e5126fb37ffcd9793a25dcad769a95f171a2161030bea850ff"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1fe5d8d39118c2b018c215c37b73fd6893c3e1d4895be745ca8ff6eb83333ed3"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:c7dc052432cd5d060d7437e217dd33c97025287f99a69a50e2dc1478dd610d64"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-win32.whl", hash = "sha256:ecce8c021894a77d89808222b1ff9687ad84db54d18e4bd0500ca766737faaf6"},
+ {file = "SQLAlchemy-1.3.23-cp38-cp38-win_amd64.whl", hash = "sha256:37b83bf81b4b85dda273aaaed5f35ea20ad80606f672d94d2218afc565fb0173"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:8be835aac18ec85351385e17b8665bd4d63083a7160a017bef3d640e8e65cadb"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6ec1044908414013ebfe363450c22f14698803ce97fbb47e53284d55c5165848"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:eab063a70cca4a587c28824e18be41d8ecc4457f8f15b2933584c6c6cccd30f0"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:baeb451ee23e264de3f577fee5283c73d9bbaa8cb921d0305c0bbf700094b65b"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-win32.whl", hash = "sha256:94208867f34e60f54a33a37f1c117251be91a47e3bfdb9ab8a7847f20886ad06"},
+ {file = "SQLAlchemy-1.3.23-cp39-cp39-win_amd64.whl", hash = "sha256:f4d972139d5000105fcda9539a76452039434013570d6059993120dc2a65e447"},
+ {file = "SQLAlchemy-1.3.23.tar.gz", hash = "sha256:6fca33672578666f657c131552c4ef8979c1606e494f78cd5199742dfb26918b"},
+]
+sqlalchemy-aio = [
+ {file = "sqlalchemy_aio-0.16.0-py2.py3-none-any.whl", hash = "sha256:f767320427c22c66fa5840a1f17f3261110a8ddc8560558f4fbf12d31a66b17b"},
+ {file = "sqlalchemy_aio-0.16.0.tar.gz", hash = "sha256:7f77366f55d34891c87386dd0962a28b948b684e8ea5edb7daae4187c0b291bf"},
+]
+sse-starlette = [
+ {file = "sse-starlette-0.6.2.tar.gz", hash = "sha256:1c0cc62cc7d021a386dc06a16a9ddc3e2861d19da6bc2e654e65cc111e820456"},
+]
+starlette = [
+ {file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"},
+ {file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"},
+]
+typing-extensions = [
+ {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"},
+ {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"},
+ {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"},
+]
+uvicorn = [
+ {file = "uvicorn-0.18.1-py3-none-any.whl", hash = "sha256:013c4ea0787cc2dc456ef4368e18c01982e6be57903e4d3183218e543eb889b7"},
+ {file = "uvicorn-0.18.1.tar.gz", hash = "sha256:35703e6518105cfe53f16a5a9435db3e2e227d0784f1fd8fbc1214b1fdc108df"},
+]
+uvloop = [
+ {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"},
+ {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30ba9dcbd0965f5c812b7c2112a1ddf60cf904c1c160f398e7eed3a6b82dcd9c"},
+ {file = "uvloop-0.16.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bd53f7f5db562f37cd64a3af5012df8cac2c464c97e732ed556800129505bd64"},
+ {file = "uvloop-0.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:772206116b9b57cd625c8a88f2413df2fcfd0b496eb188b82a43bed7af2c2ec9"},
+ {file = "uvloop-0.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b572256409f194521a9895aef274cea88731d14732343da3ecdb175228881638"},
+ {file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:04ff57aa137230d8cc968f03481176041ae789308b4d5079118331ab01112450"},
+ {file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a19828c4f15687675ea912cc28bbcb48e9bb907c801873bd1519b96b04fb805"},
+ {file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e814ac2c6f9daf4c36eb8e85266859f42174a4ff0d71b99405ed559257750382"},
+ {file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bd8f42ea1ea8f4e84d265769089964ddda95eb2bb38b5cbe26712b0616c3edee"},
+ {file = "uvloop-0.16.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:647e481940379eebd314c00440314c81ea547aa636056f554d491e40503c8464"},
+ {file = "uvloop-0.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e0d26fa5875d43ddbb0d9d79a447d2ace4180d9e3239788208527c4784f7cab"},
+ {file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6ccd57ae8db17d677e9e06192e9c9ec4bd2066b77790f9aa7dede2cc4008ee8f"},
+ {file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:089b4834fd299d82d83a25e3335372f12117a7d38525217c2258e9b9f4578897"},
+ {file = "uvloop-0.16.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98d117332cc9e5ea8dfdc2b28b0a23f60370d02e1395f88f40d1effd2cb86c4f"},
+ {file = "uvloop-0.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e5f2e2ff51aefe6c19ee98af12b4ae61f5be456cd24396953244a30880ad861"},
+ {file = "uvloop-0.16.0.tar.gz", hash = "sha256:f74bc20c7b67d1c27c72601c78cf95be99d5c2cdd4514502b4f3eb0933ff1228"},
+]
+watchgod = [
+ {file = "watchgod-0.7-py3-none-any.whl", hash = "sha256:d6c1ea21df37847ac0537ca0d6c2f4cdf513562e95f77bb93abbcf05573407b7"},
+ {file = "watchgod-0.7.tar.gz", hash = "sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29"},
+]
+websockets = [
+ {file = "websockets-10.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cd8c6f2ec24aedace251017bc7a414525171d4e6578f914acab9349362def4da"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1f6b814cff6aadc4288297cb3a248614829c6e4ff5556593c44a115e9dd49939"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:01db0ecd1a0ca6702d02a5ed40413e18b7d22f94afb3bbe0d323bac86c42c1c8"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:82b17524b1ce6ae7f7dd93e4d18e9b9474071e28b65dbf1dfe9b5767778db379"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:8bbf8660c3f833ddc8b1afab90213f2e672a9ddac6eecb3cde968e6b2807c1c7"},
+ {file = "websockets-10.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b8176deb6be540a46695960a765a77c28ac8b2e3ef2ec95d50a4f5df901edb1c"},
+ {file = "websockets-10.0-cp37-cp37m-win32.whl", hash = "sha256:706e200fc7f03bed99ad0574cd1ea8b0951477dd18cc978ccb190683c69dba76"},
+ {file = "websockets-10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5b2600e01c7ca6f840c42c747ffbe0254f319594ed108db847eb3d75f4aacb80"},
+ {file = "websockets-10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:085bb8a6e780d30eaa1ba48ac7f3a6707f925edea787cfb761ce5a39e77ac09b"},
+ {file = "websockets-10.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9a4d889162bd48588e80950e07fa5e039eee9deb76a58092e8c3ece96d7ef537"},
+ {file = "websockets-10.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b4ade7569b6fd17912452f9c3757d96f8e4044016b6d22b3b8391e641ca50456"},
+ {file = "websockets-10.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:2a43072e434c041a99f2e1eb9b692df0232a38c37c61d00e9f24db79474329e4"},
+ {file = "websockets-10.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:7f79f02c7f9a8320aff7d3321cd1c7e3a7dbc15d922ac996cca827301ee75238"},
+ {file = "websockets-10.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:1ac35426fe3e7d3d0fac3d63c8965c76ed67a8fd713937be072bf0ce22808539"},
+ {file = "websockets-10.0-cp38-cp38-win32.whl", hash = "sha256:ff59c6bdb87b31f7e2d596f09353d5a38c8c8ff571b0e2238e8ee2d55ad68465"},
+ {file = "websockets-10.0-cp38-cp38-win_amd64.whl", hash = "sha256:d67646ddd17a86117ae21c27005d83c1895c0cef5d7be548b7549646372f868a"},
+ {file = "websockets-10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:82bd921885231f4a30d9bc550552495b3fc36b1235add6d374e7c65c3babd805"},
+ {file = "websockets-10.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7d2e12e4f901f1bc062dfdf91831712c4106ed18a9a4cdb65e2e5f502124ca37"},
+ {file = "websockets-10.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:71358c7816e2762f3e4af3adf0040f268e219f5a38cb3487a9d0fc2e554fef6a"},
+ {file = "websockets-10.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:fe83b3ec9ef34063d86dfe1029160a85f24a5a94271036e5714a57acfdd089a1"},
+ {file = "websockets-10.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:eb282127e9c136f860c6068a4fba5756eb25e755baffb5940b6f1eae071928b2"},
+ {file = "websockets-10.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:62160772314920397f9d219147f958b33fa27a12c662d4455c9ccbba9a07e474"},
+ {file = "websockets-10.0-cp39-cp39-win32.whl", hash = "sha256:e42a1f1e03437b017af341e9bbfdc09252cd48ef32a8c3c3ead769eab3b17368"},
+ {file = "websockets-10.0-cp39-cp39-win_amd64.whl", hash = "sha256:c5880442f5fc268f1ef6d37b2c152c114deccca73f48e3a8c48004d2f16f4567"},
+ {file = "websockets-10.0.tar.gz", hash = "sha256:c4fc9a1d242317892590abe5b61a9127f1a61740477bfb121743f290b8054002"},
+]
+win32-setctime = []
+zipp = [
+ {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"},
+ {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"},
+]
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000..5c4bc7a05
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,70 @@
+[tool.poetry]
+name = "lnbits"
+version = "0.1.0"
+description = ""
+authors = ["matthewcroughan "]
+
+[tool.poetry.build]
+generate-setup-file = false
+script = "build.py"
+
+[tool.poetry.dependencies]
+python = "^3.9"
+aiofiles = "0.7.0"
+asgiref = "3.4.1"
+attrs = "21.2.0"
+bech32 = "1.2.0"
+bitstring = "3.1.9"
+cerberus = "1.3.4"
+certifi = "2021.5.30"
+charset-normalizer = "2.0.6"
+click = "8.0.1"
+ecdsa = "0.17.0"
+embit = "0.4.9"
+environs = "9.3.3"
+fastapi = "0.78.0"
+h11 = "0.12.0"
+httpcore = "0.13.7"
+httptools = "0.2.0"
+httpx = "0.19.0"
+idna = "3.2"
+importlib-metadata = "4.8.1"
+jinja2 = "3.0.1"
+lnurl = "0.3.6"
+markupsafe = "2.0.1"
+marshmallow = "3.13.0"
+outcome = "1.1.0"
+psycopg2-binary = "2.9.1"
+pycryptodomex = "3.14.1"
+pydantic = "1.8.2"
+pypng = "0.0.21"
+pyqrcode = "1.2.1"
+pyscss = "1.3.7"
+python-dotenv = "0.19.0"
+pyyaml = "5.4.1"
+represent = "1.6.0.post0"
+rfc3986 = "1.5.0"
+secp256k1 = "0.14.0"
+shortuuid = "1.0.1"
+six = "1.16.0"
+sniffio = "1.2.0"
+sqlalchemy = "1.3.23"
+sqlalchemy-aio = "0.16.0"
+sse-starlette = "0.6.2"
+typing-extensions = "3.10.0.2"
+uvicorn = "0.18.1"
+uvloop = "0.16.0"
+watchgod = "0.7"
+websockets = "10.0"
+zipp = "3.5.0"
+loguru = "0.5.3"
+cffi = "1.15.0"
+
+[tool.poetry.dev-dependencies]
+
+[build-system]
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"
+
+[tool.poetry.scripts]
+lnbits = "lnbits.server:main"
From 49bd5ad26bffa5f4cce8ce6a9f2bbeb4e874140d Mon Sep 17 00:00:00 2001
From: matthewcroughan
Date: Fri, 8 Jul 2022 09:24:54 +0100
Subject: [PATCH 03/84] lnbits/server.py: init
This adds a wrapper around uvicorn using Python. This is so the
entrypoint lnbits.server can be called on the command line, and passed
arguments like port and host. This makes lnbits feel more like a real
CLI application, and lends well to running as a service in systemd
---
lnbits/server.py | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 lnbits/server.py
diff --git a/lnbits/server.py b/lnbits/server.py
new file mode 100644
index 000000000..c0efb8953
--- /dev/null
+++ b/lnbits/server.py
@@ -0,0 +1,17 @@
+import uvicorn
+
+import click
+
+@click.command()
+@click.option('--port', default="5000", help='Port to run LNBits on')
+@click.option('--host', default="127.0.0.1", help='Host to run LNBits on')
+def main(port, host):
+ """Launched with `poetry run lnbits` at root level"""
+ uvicorn.run("lnbits.__main__:app", port=port, host=host)
+
+if __name__ == '__main__':
+ main()
+
+#def main():
+# """Launched with `poetry run start` at root level"""
+# uvicorn.run("lnbits.__main__:app")
From 36e92765d3e76bc50543c6a96829f09cbb4c5b2e Mon Sep 17 00:00:00 2001
From: matthewcroughan
Date: Fri, 8 Jul 2022 09:49:38 +0100
Subject: [PATCH 04/84] treewide: use StaticFiles() rather than rel path
This commit is not exhaustive, and it is a trend in this codebase to not
use StaticFiles() and instead use relative paths. This means the code
cannot run anywhere other than the source code directory, as it will not
find the files it is looking for
---
lnbits/__main__.py | 4 +---
lnbits/app.py | 4 ++--
lnbits/extensions/bleskomat/__init__.py | 4 ++--
lnbits/extensions/copilot/__init__.py | 4 ++--
lnbits/extensions/discordbot/__init__.py | 4 ++--
lnbits/extensions/jukebox/__init__.py | 4 ++--
lnbits/extensions/livestream/__init__.py | 4 ++--
lnbits/extensions/lnurlp/__init__.py | 4 ++--
lnbits/extensions/offlineshop/__init__.py | 4 ++--
lnbits/extensions/splitpayments/__init__.py | 4 ++--
lnbits/extensions/withdraw/__init__.py | 4 ++--
11 files changed, 21 insertions(+), 23 deletions(-)
diff --git a/lnbits/__main__.py b/lnbits/__main__.py
index 93e2181c6..aa6528991 100644
--- a/lnbits/__main__.py
+++ b/lnbits/__main__.py
@@ -5,7 +5,7 @@ from starlette.requests import Request
from loguru import logger
-from .commands import bundle_vendored, migrate_databases, transpile_scss
+from .commands import migrate_databases
from .settings import (
DEBUG,
LNBITS_COMMIT,
@@ -21,8 +21,6 @@ from .settings import (
uvloop.install()
asyncio.create_task(migrate_databases())
-transpile_scss()
-bundle_vendored()
from .app import create_app
diff --git a/lnbits/app.py b/lnbits/app.py
index 9ab7c75d9..84df250fe 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -47,9 +47,9 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
configure_logger()
app = FastAPI()
- app.mount("/static", StaticFiles(directory="lnbits/static"), name="static")
+ app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static")
app.mount(
- "/core/static", StaticFiles(directory="lnbits/core/static"), name="core_static"
+ "/core/static", StaticFiles(packages=[("lnbits.core", "static")]), name="core_static"
)
origins = ["*"]
diff --git a/lnbits/extensions/bleskomat/__init__.py b/lnbits/extensions/bleskomat/__init__.py
index cc89760e5..06b4a441e 100644
--- a/lnbits/extensions/bleskomat/__init__.py
+++ b/lnbits/extensions/bleskomat/__init__.py
@@ -9,7 +9,7 @@ db = Database("ext_bleskomat")
bleskomat_static_files = [
{
"path": "/bleskomat/static",
- "app": StaticFiles(directory="lnbits/extensions/bleskomat/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/bleskomat/static")]),
"name": "bleskomat_static",
}
]
@@ -18,7 +18,7 @@ bleskomat_ext: APIRouter = APIRouter(prefix="/bleskomat", tags=["Bleskomat"])
def bleskomat_renderer():
- return template_renderer(["lnbits/extensions/bleskomat/templates"])
+ return template_renderer([StaticFiles(packages=[("lnbits", "extensions/bleskomat/static/templates")])])
from .lnurl_api import * # noqa
diff --git a/lnbits/extensions/copilot/__init__.py b/lnbits/extensions/copilot/__init__.py
index 8a6342678..8634505f0 100644
--- a/lnbits/extensions/copilot/__init__.py
+++ b/lnbits/extensions/copilot/__init__.py
@@ -12,7 +12,7 @@ db = Database("ext_copilot")
copilot_static_files = [
{
"path": "/copilot/static",
- "app": StaticFiles(directory="lnbits/extensions/copilot/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/copilot/static")]),
"name": "copilot_static",
}
]
@@ -20,7 +20,7 @@ copilot_ext: APIRouter = APIRouter(prefix="/copilot", tags=["copilot"])
def copilot_renderer():
- return template_renderer(["lnbits/extensions/copilot/templates"])
+ return template_renderer([StaticFiles(packages=[("lnbits", "extensions/copilot/static/templates")])])
from .lnurl import * # noqa
diff --git a/lnbits/extensions/discordbot/__init__.py b/lnbits/extensions/discordbot/__init__.py
index ff60dd628..eb755bd1b 100644
--- a/lnbits/extensions/discordbot/__init__.py
+++ b/lnbits/extensions/discordbot/__init__.py
@@ -9,7 +9,7 @@ db = Database("ext_discordbot")
discordbot_static_files = [
{
"path": "/discordbot/static",
- "app": StaticFiles(directory="lnbits/extensions/discordbot/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/discordbot/static")]),
"name": "discordbot_static",
}
]
@@ -18,7 +18,7 @@ discordbot_ext: APIRouter = APIRouter(prefix="/discordbot", tags=["discordbot"])
def discordbot_renderer():
- return template_renderer(["lnbits/extensions/discordbot/templates"])
+ return template_renderer([StaticFiles(packages=[("lnbits", "extensions/discordbot/static/templates")])])
from .views import * # noqa
diff --git a/lnbits/extensions/jukebox/__init__.py b/lnbits/extensions/jukebox/__init__.py
index 702a6c67b..ee92ec6ae 100644
--- a/lnbits/extensions/jukebox/__init__.py
+++ b/lnbits/extensions/jukebox/__init__.py
@@ -12,7 +12,7 @@ db = Database("ext_jukebox")
jukebox_static_files = [
{
"path": "/jukebox/static",
- "app": StaticFiles(directory="lnbits/extensions/jukebox/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/jukebox/static")]),
"name": "jukebox_static",
}
]
@@ -21,7 +21,7 @@ jukebox_ext: APIRouter = APIRouter(prefix="/jukebox", tags=["jukebox"])
def jukebox_renderer():
- return template_renderer(["lnbits/extensions/jukebox/templates"])
+ return template_renderer([StaticFiles(packages=[("lnbits", "extensions/jukebox/static/templates")])])
from .tasks import wait_for_paid_invoices
diff --git a/lnbits/extensions/livestream/__init__.py b/lnbits/extensions/livestream/__init__.py
index 17fc2d3b8..fbfcf22af 100644
--- a/lnbits/extensions/livestream/__init__.py
+++ b/lnbits/extensions/livestream/__init__.py
@@ -12,7 +12,7 @@ db = Database("ext_livestream")
livestream_static_files = [
{
"path": "/livestream/static",
- "app": StaticFiles(directory="lnbits/extensions/livestream/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/livestream/static")]),
"name": "livestream_static",
}
]
@@ -21,7 +21,7 @@ livestream_ext: APIRouter = APIRouter(prefix="/livestream", tags=["livestream"])
def livestream_renderer():
- return template_renderer(["lnbits/extensions/livestream/templates"])
+ return template_renderer([StaticFiles(packages=[("lnbits", "extensions/livestream/static/templates")])])
from .lnurl import * # noqa
diff --git a/lnbits/extensions/lnurlp/__init__.py b/lnbits/extensions/lnurlp/__init__.py
index 409817481..02dbf2ceb 100644
--- a/lnbits/extensions/lnurlp/__init__.py
+++ b/lnbits/extensions/lnurlp/__init__.py
@@ -12,7 +12,7 @@ db = Database("ext_lnurlp")
lnurlp_static_files = [
{
"path": "/lnurlp/static",
- "app": StaticFiles(directory="lnbits/extensions/lnurlp/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/lnurlp/static")]),
"name": "lnurlp_static",
}
]
@@ -21,7 +21,7 @@ lnurlp_ext: APIRouter = APIRouter(prefix="/lnurlp", tags=["lnurlp"])
def lnurlp_renderer():
- return template_renderer(["lnbits/extensions/lnurlp/templates"])
+ return template_renderer([StaticFiles(packages=[("lnbits", "extensions/lnurlp/static/templates")])])
from .lnurl import * # noqa
diff --git a/lnbits/extensions/offlineshop/__init__.py b/lnbits/extensions/offlineshop/__init__.py
index a601c1b85..bacbf339f 100644
--- a/lnbits/extensions/offlineshop/__init__.py
+++ b/lnbits/extensions/offlineshop/__init__.py
@@ -9,7 +9,7 @@ db = Database("ext_offlineshop")
offlineshop_static_files = [
{
"path": "/offlineshop/static",
- "app": StaticFiles(directory="lnbits/extensions/offlineshop/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/offlineshop/static")]),
"name": "offlineshop_static",
}
]
@@ -18,7 +18,7 @@ offlineshop_ext: APIRouter = APIRouter(prefix="/offlineshop", tags=["Offlineshop
def offlineshop_renderer():
- return template_renderer(["lnbits/extensions/offlineshop/templates"])
+ return template_renderer([StaticFiles(packages=[("lnbits", "extensions/offlineshop/static/templates")])])
from .lnurl import * # noqa
diff --git a/lnbits/extensions/splitpayments/__init__.py b/lnbits/extensions/splitpayments/__init__.py
index df6feb943..3edac442d 100644
--- a/lnbits/extensions/splitpayments/__init__.py
+++ b/lnbits/extensions/splitpayments/__init__.py
@@ -12,7 +12,7 @@ db = Database("ext_splitpayments")
splitpayments_static_files = [
{
"path": "/splitpayments/static",
- "app": StaticFiles(directory="lnbits/extensions/splitpayments/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/splitpayments/static")]),
"name": "splitpayments_static",
}
]
@@ -22,7 +22,7 @@ splitpayments_ext: APIRouter = APIRouter(
def splitpayments_renderer():
- return template_renderer(["lnbits/extensions/splitpayments/templates"])
+ return template_renderer([StaticFiles(packages=[("lnbits", "extensions/splitpayments/static/templates")])])
from .tasks import wait_for_paid_invoices
diff --git a/lnbits/extensions/withdraw/__init__.py b/lnbits/extensions/withdraw/__init__.py
index 58ccfe7e8..187ee7c00 100644
--- a/lnbits/extensions/withdraw/__init__.py
+++ b/lnbits/extensions/withdraw/__init__.py
@@ -9,7 +9,7 @@ db = Database("ext_withdraw")
withdraw_static_files = [
{
"path": "/withdraw/static",
- "app": StaticFiles(directory="lnbits/extensions/withdraw/static"),
+ "app": StaticFiles(packages=[("lnbits", "extensions/withdraw/static")]),
"name": "withdraw_static",
}
]
@@ -19,7 +19,7 @@ withdraw_ext: APIRouter = APIRouter(prefix="/withdraw", tags=["withdraw"])
def withdraw_renderer():
- return template_renderer(["lnbits/extensions/withdraw/templates"])
+ return template_renderer([StaticFiles(packages=[("lnbits", "extensions/withdraw/static/templates")])])
from .lnurl import * # noqa
From 6aa8330dc331c94fdfb2f8427948c0daa4abac61 Mon Sep 17 00:00:00 2001
From: matthewcroughan
Date: Fri, 8 Jul 2022 09:51:21 +0100
Subject: [PATCH 05/84] Start using Nix as a reproducible build tool
---
flake.lock | 77 +++++++++++++++++++++
flake.nix | 55 +++++++++++++++
nix/modules/lnbits-service.nix | 106 +++++++++++++++++++++++++++++
nix/tests/default.nix | 4 ++
nix/tests/nixos-module/default.nix | 25 +++++++
5 files changed, 267 insertions(+)
create mode 100644 flake.lock
create mode 100644 flake.nix
create mode 100644 nix/modules/lnbits-service.nix
create mode 100644 nix/tests/default.nix
create mode 100644 nix/tests/nixos-module/default.nix
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 000000000..0ca2db014
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,77 @@
+{
+ "nodes": {
+ "flake-utils": {
+ "locked": {
+ "lastModified": 1656928814,
+ "narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1657114324,
+ "narHash": "sha256-fWuaUNXrHcz/ciHRHlcSO92dvV3EVS0GJQUSBO5JIB4=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "a5c867d9fe9e4380452628e8f171c26b69fa9d3d",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1657261001,
+ "narHash": "sha256-sUZeuRYfhG59uD6xafM07bc7bAIkpcGq84Vj4B+cyms=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "0be91cefefde5701f8fa957904618a13e3bb51d8",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "poetry2nix": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs_2"
+ },
+ "locked": {
+ "lastModified": 1657149754,
+ "narHash": "sha256-iSnZoqwNDDVoO175whSuvl4sS9lAb/2zZ3Sa4ywo970=",
+ "owner": "nix-community",
+ "repo": "poetry2nix",
+ "rev": "fc1930e011dea149db81863aac22fe701f36f1b5",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "poetry2nix",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "nixpkgs": "nixpkgs",
+ "poetry2nix": "poetry2nix"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 000000000..775228664
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,55 @@
+{
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
+ poetry2nix.url = "github:nix-community/poetry2nix";
+ };
+ outputs = { self, nixpkgs, poetry2nix }@inputs:
+ let
+ supportedSystems = [ "x86_64-linux" "aarch64-linux" ];
+ forSystems = systems: f:
+ nixpkgs.lib.genAttrs systems
+ (system: f system (import nixpkgs { inherit system; overlays = [ poetry2nix.overlay self.overlays.default ]; }));
+ forAllSystems = forSystems supportedSystems;
+ projectName = "lnbits";
+ in
+ {
+ devShells = forAllSystems (system: pkgs: {
+ default = pkgs.mkShell {
+ buildInputs = with pkgs; [
+ nodePackages.prettier
+ ];
+ };
+ });
+ overlays = {
+ default = final: prev: {
+ ${projectName} = self.packages.${final.hostPlatform.system}.${projectName};
+ };
+ };
+ packages = forAllSystems (system: pkgs: {
+ default = self.packages.${system}.${projectName};
+ ${projectName} = pkgs.poetry2nix.mkPoetryApplication {
+ projectDir = ./.;
+ python = pkgs.python39;
+ };
+ });
+ nixosModules = forAllSystems (system: pkgs: {
+ default = { pkgs, lib, config, ... }: {
+ imports = [ "${./nix/modules/${projectName}-service.nix}" ];
+ nixpkgs.overlays = [ self.overlays.default ];
+ };
+ });
+ checks = forAllSystems (system: pkgs:
+ let
+ vmTests = import ./nix/tests {
+ makeTest = (import (nixpkgs + "/nixos/lib/testing-python.nix") { inherit system; }).makeTest;
+ inherit inputs pkgs;
+ };
+ in
+ pkgs.lib.optionalAttrs pkgs.stdenv.isLinux vmTests # vmTests can only be ran on Linux, so append them only if on Linux.
+ //
+ {
+ # Other checks here...
+ }
+ );
+ };
+}
diff --git a/nix/modules/lnbits-service.nix b/nix/modules/lnbits-service.nix
new file mode 100644
index 000000000..5d8e06403
--- /dev/null
+++ b/nix/modules/lnbits-service.nix
@@ -0,0 +1,106 @@
+{ config, pkgs, lib, ... }:
+
+let
+ defaultUser = "lnbits";
+ cfg = config.services.lnbits;
+ inherit (lib) mkOption mkIf types optionalAttrs;
+in
+
+{
+ options = {
+ services.lnbits = {
+ enable = mkOption {
+ default = false;
+ type = types.bool;
+ description = ''
+ Whether to enable the lnbits service
+ '';
+ };
+ openFirewall = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Whether to open the ports used by lnbits in the firewall for the server
+ '';
+ };
+ package = mkOption {
+ type = types.package;
+ default = pkgs.lnbits;
+ description = ''
+ The lnbits package to use.
+ '';
+ };
+ stateDir = mkOption {
+ type = types.path;
+ default = "/var/lib/lnbits";
+ description = ''
+ The lnbits state directory which LNBITS_DATA_FOLDER will be set to
+ '';
+ };
+ host = mkOption {
+ type = types.str;
+ default = "127.0.0.1";
+ description = ''
+ The host to bind to
+ '';
+ };
+ port = mkOption {
+ type = types.port;
+ default = 8231;
+ description = ''
+ The port to run on
+ '';
+ };
+ user = mkOption {
+ type = types.str;
+ default = "lnbits";
+ description = "user to run lnbits as";
+ };
+ group = mkOption {
+ type = types.str;
+ default = "lnbits";
+ description = "group to run lnbits as";
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ users.users = optionalAttrs (cfg.user == defaultUser) {
+ ${defaultUser} = {
+ isSystemUser = true;
+ group = defaultUser;
+ };
+ };
+
+ users.groups = optionalAttrs (cfg.group == defaultUser) {
+ ${defaultUser} = { };
+ };
+
+ systemd.tmpfiles.rules = [
+ "d ${cfg.stateDir} 0700 ${cfg.user} ${cfg.group} - -"
+ ];
+
+ systemd.services.lnbits = {
+ enable = true;
+ description = "lnbits";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network-online.target" ];
+ environment = {
+ LNBITS_DATA_FOLDER = "${cfg.stateDir}";
+ };
+ serviceConfig = {
+ User = cfg.user;
+ Group = cfg.group;
+ WorkingDirectory = "${cfg.package.src}";
+ StateDirectory = "${cfg.stateDir}";
+ ExecStart = "${lib.getExe cfg.package} --port ${toString cfg.port} --host ${cfg.host}";
+ Restart = "always";
+ PrivateTmp = true;
+ };
+ };
+ networking.firewall = mkIf cfg.openFirewall {
+ allowedTCPPorts = [ cfg.port ];
+ };
+ };
+}
+
diff --git a/nix/tests/default.nix b/nix/tests/default.nix
new file mode 100644
index 000000000..7b8513acf
--- /dev/null
+++ b/nix/tests/default.nix
@@ -0,0 +1,4 @@
+{ pkgs, makeTest, inputs }:
+{
+ vmTest = import ./nixos-module { inherit pkgs makeTest inputs; };
+}
diff --git a/nix/tests/nixos-module/default.nix b/nix/tests/nixos-module/default.nix
new file mode 100644
index 000000000..5649ae591
--- /dev/null
+++ b/nix/tests/nixos-module/default.nix
@@ -0,0 +1,25 @@
+{ pkgs, makeTest, inputs }:
+makeTest {
+ nodes = {
+ client = { config, pkgs, ... }: {
+ environment.systemPackages = [ pkgs.curl ];
+ };
+ lnbits = { ... }: {
+ imports = [ inputs.self.nixosModules.${pkgs.hostPlatform.system}.default ];
+ services.lnbits = {
+ enable = true;
+ openFirewall = true;
+ host = "0.0.0.0";
+ };
+ };
+ };
+ testScript = { nodes, ... }: ''
+ start_all()
+ lnbits.wait_for_open_port(${toString nodes.lnbits.config.services.lnbits.port})
+ client.wait_for_unit("multi-user.target")
+ with subtest("Check that the lnbits webserver can be reached."):
+ assert "LNbits" in client.succeed(
+ "curl -sSf http:/lnbits:8231/ | grep title"
+ )
+ '';
+}
From 984f06239f0fed969a25be903cf35be4d788dd1d Mon Sep 17 00:00:00 2001
From: matthewcroughan
Date: Thu, 14 Jul 2022 05:27:38 +0100
Subject: [PATCH 06/84] nix: remove forAllSystems from nixosModule
This causes lnbits.nixosModules.default to exist, rather than
lnbits.nixosModules.aarch64-linux.default. The latter was a mistake.
---
flake.nix | 4 ++--
nix/tests/nixos-module/default.nix | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/flake.nix b/flake.nix
index 775228664..af25ba5c3 100644
--- a/flake.nix
+++ b/flake.nix
@@ -32,12 +32,12 @@
python = pkgs.python39;
};
});
- nixosModules = forAllSystems (system: pkgs: {
+ nixosModules = {
default = { pkgs, lib, config, ... }: {
imports = [ "${./nix/modules/${projectName}-service.nix}" ];
nixpkgs.overlays = [ self.overlays.default ];
};
- });
+ };
checks = forAllSystems (system: pkgs:
let
vmTests = import ./nix/tests {
diff --git a/nix/tests/nixos-module/default.nix b/nix/tests/nixos-module/default.nix
index 5649ae591..868579121 100644
--- a/nix/tests/nixos-module/default.nix
+++ b/nix/tests/nixos-module/default.nix
@@ -5,7 +5,7 @@ makeTest {
environment.systemPackages = [ pkgs.curl ];
};
lnbits = { ... }: {
- imports = [ inputs.self.nixosModules.${pkgs.hostPlatform.system}.default ];
+ imports = [ inputs.self.nixosModules.default ];
services.lnbits = {
enable = true;
openFirewall = true;
From 83a000d7c9795b81fbe88f9d444c27bc0d20ad40 Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 19 Jul 2022 13:06:19 +0200
Subject: [PATCH 07/84] fix up mypy.ini, and properly ignore grpc and extension
---
mypy.ini | 7 ++++---
requirements.txt | 3 ++-
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/mypy.ini b/mypy.ini
index 735f89e05..e5a974b51 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -1,7 +1,8 @@
[mypy]
ignore_missing_imports = True
-exclude = lnbits/wallets/lnd_grpc_files/
-exclude = lnbits/extensions/
-
+exclude = (?x)(
+ ^lnbits/extensions.
+ | ^lnbits/wallets/lnd_grpc_files.
+ )
[mypy-lnbits.wallets.lnd_grpc_files.*]
follow_imports = skip
diff --git a/requirements.txt b/requirements.txt
index 8359456ff..322712f5a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -43,9 +43,10 @@ sqlalchemy==1.3.23
sqlalchemy-aio==0.16.0
sse-starlette==0.6.2
starlette==0.14.2
+types-protobuf==3.19.22
typing-extensions==3.10.0.2
uvicorn==0.15.0
uvloop==0.16.0
watchgod==0.7
websockets==10.0
-zipp==3.5.0
\ No newline at end of file
+zipp==3.5.0
From 45a13fd807433debcc8c83915b2100aef555bb0a Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 19 Jul 2022 13:36:44 +0200
Subject: [PATCH 08/84] remove commented out and unused code in app object, it
is now used in lnbits/__main__.py directly, fixes mypy error
---
lnbits/app.py | 8 --------
1 file changed, 8 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index a7c8fdaf3..eaf8a9db4 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -17,7 +17,6 @@ from loguru import logger
import lnbits.settings
from lnbits.core.tasks import register_task_listeners
-from .commands import db_migrate, handle_assets
from .core import core_app
from .core.views.generic import core_html_routes
from .helpers import (
@@ -84,7 +83,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
check_funding_source(app)
register_assets(app)
register_routes(app)
- # register_commands(app)
register_async_tasks(app)
register_exception_handlers(app)
@@ -137,12 +135,6 @@ def register_routes(app: FastAPI) -> None:
)
-def register_commands(app: FastAPI):
- """Register Click commands."""
- app.cli.add_command(db_migrate)
- app.cli.add_command(handle_assets)
-
-
def register_assets(app: FastAPI):
"""Serve each vendored asset separately or a bundle."""
From 8901ff8084ae3638f95c5616086ccbdcf8b175d8 Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 19 Jul 2022 18:51:35 +0200
Subject: [PATCH 09/84] some more mypy fixes
---
lnbits/core/crud.py | 2 +-
lnbits/core/models.py | 2 ++
lnbits/core/views/api.py | 15 +++++++++------
lnbits/core/views/generic.py | 6 +++++-
lnbits/helpers.py | 2 +-
lnbits/tasks.py | 2 +-
lnbits/utils/exchange_rates.py | 8 ++++----
lnbits/wallets/lnpay.py | 2 +-
lnbits/wallets/opennode.py | 2 +-
9 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py
index 770e2906a..db802d7bb 100644
--- a/lnbits/core/crud.py
+++ b/lnbits/core/crud.py
@@ -113,7 +113,7 @@ async def create_wallet(
async def update_wallet(
wallet_id: str, new_name: str, conn: Optional[Connection] = None
) -> Optional[Wallet]:
- await (conn or db).execute(
+ return await (conn or db).execute(
"""
UPDATE wallets SET
name = ?
diff --git a/lnbits/core/models.py b/lnbits/core/models.py
index ab73b7020..0f7eba737 100644
--- a/lnbits/core/models.py
+++ b/lnbits/core/models.py
@@ -106,6 +106,8 @@ class Payment(BaseModel):
@property
def tag(self) -> Optional[str]:
+ if self.extra is None:
+ return ""
return self.extra.get("tag")
@property
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 9fee6063d..bd15ee8d6 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -3,7 +3,7 @@ import hashlib
import json
from binascii import unhexlify
from http import HTTPStatus
-from typing import Dict, List, Optional, Union
+from typing import Dict, List, Optional, Union, Tuple
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
import httpx
@@ -185,7 +185,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
assert (
data.lnurl_balance_check is not None
), "lnurl_balance_check is required"
- save_balance_check(wallet.id, data.lnurl_balance_check)
+ await save_balance_check(wallet.id, data.lnurl_balance_check)
async with httpx.AsyncClient() as client:
try:
@@ -291,7 +291,7 @@ async def api_payments_pay_lnurl(
timeout=40,
)
if r.is_error:
- raise httpx.ConnectError
+ raise httpx.ConnectError("LNURL Callback Connection Error")
except (httpx.ConnectError, httpx.RequestError):
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
@@ -348,7 +348,7 @@ async def subscribe(request: Request, wallet: Wallet):
logger.debug("adding sse listener", payment_queue)
api_invoice_listeners.append(payment_queue)
- send_queue: asyncio.Queue[tuple[str, Payment]] = asyncio.Queue(0)
+ send_queue: asyncio.Queue[Tuple[str, Payment]] = asyncio.Queue(0)
async def payment_received() -> None:
while True:
@@ -389,10 +389,13 @@ async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)):
# If a valid key is given, we also return the field "details", otherwise not
wallet = None
try:
+ assert X_Api_Key is not None
+ # TODO: type above is Optional[str] how can that have .extra?
if X_Api_Key.extra:
logger.warning("No key")
except:
- wallet = await get_wallet_for_key(X_Api_Key)
+ if X_Api_Key is not None:
+ wallet = await get_wallet_for_key(X_Api_Key)
payment = await get_standalone_payment(
payment_hash, wallet_id=wallet.id if wallet else None
) # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order
@@ -606,7 +609,7 @@ class ConversionData(BaseModel):
async def api_fiat_as_sats(data: ConversionData):
output = {}
if data.from_ == "sat":
- output["sats"] = int(data.amount)
+ output["sats"] = data.amount
output["BTC"] = data.amount / 100000000
for currency in data.to.split(","):
output[currency.strip().upper()] = await satoshis_amount_as_fiat(
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 4366028d7..d75aa77e2 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -112,9 +112,11 @@ async def wallet(
if not user_id:
user = await get_user((await create_account()).id)
+ assert user is not None
logger.info(f"Created new account for user {user.id}")
else:
user = await get_user(user_id)
+ assert user is not None
if not user:
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": "User does not exist."}
@@ -209,6 +211,7 @@ async def lnurl_full_withdraw_callback(request: Request):
@core_html_routes.get("/deletewallet", response_class=RedirectResponse)
async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)):
user = await get_user(usr)
+ assert user is not None
user_wallet_ids = [u.id for u in user.wallets]
if wal not in user_wallet_ids:
@@ -233,7 +236,7 @@ async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query
async def lnurl_balance_notify(request: Request, service: str):
bc = await get_balance_check(request.query_params.get("wal"), service)
if bc:
- redeem_lnurl_withdraw(bc.wallet, bc.url)
+ await redeem_lnurl_withdraw(bc.wallet, bc.url)
@core_html_routes.get(
@@ -243,6 +246,7 @@ async def lnurlwallet(request: Request):
async with db.connect() as conn:
account = await create_account(conn=conn)
user = await get_user(account.id, conn=conn)
+ assert user is not None
wallet = await create_wallet(user_id=user.id, conn=conn)
asyncio.create_task(
diff --git a/lnbits/helpers.py b/lnbits/helpers.py
index 71b3dd691..e97fc7bbb 100644
--- a/lnbits/helpers.py
+++ b/lnbits/helpers.py
@@ -34,7 +34,7 @@ class ExtensionManager:
@property
def extensions(self) -> List[Extension]:
- output = []
+ output: List[Extension] = []
if "all" in self._disabled:
return output
diff --git a/lnbits/tasks.py b/lnbits/tasks.py
index 86863f98f..f4d0a928d 100644
--- a/lnbits/tasks.py
+++ b/lnbits/tasks.py
@@ -66,7 +66,7 @@ async def webhook_handler():
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
-internal_invoice_queue = asyncio.Queue(0)
+internal_invoice_queue: asyncio.Queue = asyncio.Queue(0)
async def internal_invoice_listener():
diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py
index 0432b364a..fbb4add22 100644
--- a/lnbits/utils/exchange_rates.py
+++ b/lnbits/utils/exchange_rates.py
@@ -1,5 +1,5 @@
import asyncio
-from typing import Callable, NamedTuple
+from typing import Callable, NamedTuple, List
import httpx
from loguru import logger
@@ -227,10 +227,10 @@ async def btc_price(currency: str) -> float:
"TO": currency.upper(),
"to": currency.lower(),
}
- rates = []
- tasks = []
+ rates: List[float] = []
+ tasks: List[asyncio.Task] = []
- send_channel = asyncio.Queue()
+ send_channel: asyncio.Queue = asyncio.Queue()
async def controller():
failures = 0
diff --git a/lnbits/wallets/lnpay.py b/lnbits/wallets/lnpay.py
index 807d72538..2ff1afa93 100644
--- a/lnbits/wallets/lnpay.py
+++ b/lnbits/wallets/lnpay.py
@@ -119,7 +119,7 @@ class LNPayWallet(Wallet):
return PaymentStatus(statuses[r.json()["settled"]])
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
- self.queue = asyncio.Queue(0)
+ self.queue: asyncio.Queue = asyncio.Queue(0)
while True:
value = await self.queue.get()
yield value
diff --git a/lnbits/wallets/opennode.py b/lnbits/wallets/opennode.py
index 6d3fb02c9..9cd05ebdd 100644
--- a/lnbits/wallets/opennode.py
+++ b/lnbits/wallets/opennode.py
@@ -127,7 +127,7 @@ class OpenNodeWallet(Wallet):
return PaymentStatus(statuses[r.json()["data"]["status"]])
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
- self.queue = asyncio.Queue(0)
+ self.queue: asyncio.Queue = asyncio.Queue(0)
while True:
value = await self.queue.get()
yield value
From 91c5a68cccb254665318adbf128f36e39dda1292 Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 19 Jul 2022 19:20:20 +0200
Subject: [PATCH 10/84] mypy fakewallet, macaroon, bolt11 invoice
---
lnbits/bolt11.py | 1 +
lnbits/wallets/fake.py | 10 +++++++---
lnbits/wallets/macaroon/macaroon.py | 2 +-
3 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/lnbits/bolt11.py b/lnbits/bolt11.py
index cc8415852..cc8c60401 100644
--- a/lnbits/bolt11.py
+++ b/lnbits/bolt11.py
@@ -23,6 +23,7 @@ class Route(NamedTuple):
class Invoice(object):
payment_hash: str
+ checking_id: Optional[str] = None
amount_msat: int = 0
description: Optional[str] = None
description_hash: Optional[str] = None
diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py
index 3859d33c5..c2b2307ab 100644
--- a/lnbits/wallets/fake.py
+++ b/lnbits/wallets/fake.py
@@ -28,7 +28,7 @@ class FakeWallet(Wallet):
logger.info(
"FakeWallet funding source is for using LNbits as a centralised, stand-alone payment system with brrrrrr."
)
- return StatusResponse(None, float("inf"))
+ return StatusResponse(None, 1000000000)
async def create_invoice(
self,
@@ -80,8 +80,12 @@ class FakeWallet(Wallet):
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
invoice = decode(bolt11)
+ # TODO: no data here?
+ data: Dict = {
+ "privkey": "missing"
+ }
if (
- hasattr(invoice, "checking_id")
+ invoice.checking_id is not None
and invoice.checking_id[6:] == data["privkey"][:6]
):
return PaymentResponse(True, invoice.payment_hash, 0)
@@ -97,7 +101,7 @@ class FakeWallet(Wallet):
return PaymentStatus(None)
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
- self.queue = asyncio.Queue(0)
+ self.queue: asyncio.Queue = asyncio.Queue(0)
while True:
value = await self.queue.get()
yield value
diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py
index 2183dacb8..aa00cd3e6 100644
--- a/lnbits/wallets/macaroon/macaroon.py
+++ b/lnbits/wallets/macaroon/macaroon.py
@@ -73,7 +73,7 @@ class AESCipher(object):
final_key += key
return final_key[:output]
- def decrypt(self, encrypted: str) -> str:
+ def decrypt(self, encrypted) -> str:
"""Decrypts a string using AES-256-CBC."""
passphrase = self.passphrase
encrypted = base64.b64decode(encrypted)
From 886c74f205ebb725ec98729a956f5a5bab65c331 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 08:43:10 +0200
Subject: [PATCH 11/84] assert Optional[str]
---
lnbits/core/tasks.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py
index 5fea769d4..8234b5fc4 100644
--- a/lnbits/core/tasks.py
+++ b/lnbits/core/tasks.py
@@ -55,6 +55,7 @@ async def dispatch_webhook(payment: Payment):
data = payment.dict()
try:
logger.debug("sending webhook", payment.webhook)
+ assert payment.webhook is not None
r = await client.post(payment.webhook, json=data, timeout=40)
await mark_webhook_sent(payment, r.status_code)
except (httpx.ConnectError, httpx.RequestError):
From 045212a0701157c252f6425d8b50c48f2191b5d9 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 09:36:13 +0200
Subject: [PATCH 12/84] mypy fixes for generic.py, decurators.py, eclair.py
---
lnbits/core/views/generic.py | 16 +++++++--------
lnbits/decorators.py | 40 ++++++++++++++++++++----------------
lnbits/wallets/eclair.py | 4 +++-
3 files changed, 33 insertions(+), 27 deletions(-)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index d75aa77e2..7ecedcf82 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -54,9 +54,9 @@ async def home(request: Request, lightning: str = None):
)
async def extensions(
request: Request,
- user: User = Depends(check_user_exists),
- enable: str = Query(None),
- disable: str = Query(None),
+ user = Depends(check_user_exists),
+ enable = Query(None),
+ disable = Query(None),
):
extension_to_enable = enable
extension_to_disable = disable
@@ -100,10 +100,10 @@ nothing: create everything
""",
)
async def wallet(
- request: Request = Query(None),
- nme: Optional[str] = Query(None),
- usr: Optional[UUID4] = Query(None),
- wal: Optional[UUID4] = Query(None),
+ request = Query(None),
+ nme = Query(None),
+ usr = Query(None),
+ wal = Query(None),
):
user_id = usr.hex if usr else None
wallet_id = wal.hex if wal else None
@@ -209,7 +209,7 @@ async def lnurl_full_withdraw_callback(request: Request):
@core_html_routes.get("/deletewallet", response_class=RedirectResponse)
-async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)):
+async def deletewallet(request: Request, wal = Query(...), usr = Query(...)):
user = await get_user(usr)
assert user is not None
user_wallet_ids = [u.id for u in user.wallets]
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index e65b9041a..77fe32276 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -1,5 +1,7 @@
from http import HTTPStatus
+from typing import Optional
+
from cerberus import Validator # type: ignore
from fastapi import status
from fastapi.exceptions import HTTPException
@@ -29,20 +31,20 @@ class KeyChecker(SecurityBase):
self._key_type = "invoice"
self._api_key = api_key
if api_key:
- self.model: APIKey = APIKey(
+ key = APIKey(
**{"in": APIKeyIn.query},
name="X-API-KEY",
description="Wallet API Key - QUERY",
)
else:
- self.model: APIKey = APIKey(
+ key = APIKey(
**{"in": APIKeyIn.header},
name="X-API-KEY",
description="Wallet API Key - HEADER",
)
- self.wallet = None
+ self.model: APIKey = key
- async def __call__(self, request: Request) -> Wallet:
+ async def __call__(self, request: Request):
try:
key_value = (
self._api_key
@@ -52,12 +54,13 @@ class KeyChecker(SecurityBase):
# FIXME: Find another way to validate the key. A fetch from DB should be avoided here.
# Also, we should not return the wallet here - thats silly.
# Possibly store it in a Redis DB
- self.wallet = await get_wallet_for_key(key_value, self._key_type)
- if not self.wallet:
+ wallet = await get_wallet_for_key(key_value, self._key_type)
+ if not wallet:
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED,
detail="Invalid key or expired key.",
)
+ self.wallet = wallet
except KeyError:
raise HTTPException(
@@ -120,8 +123,8 @@ api_key_query = APIKeyQuery(
async def get_key_type(
r: Request,
- api_key_header: str = Security(api_key_header),
- api_key_query: str = Security(api_key_query),
+ api_key_header = Security(api_key_header),
+ api_key_query = Security(api_key_query),
) -> WalletTypeInfo:
# 0: admin
# 1: invoice
@@ -134,9 +137,10 @@ async def get_key_type(
token = api_key_header if api_key_header else api_key_query
try:
- checker = WalletAdminKeyChecker(api_key=token)
- await checker.__call__(r)
- wallet = WalletTypeInfo(0, checker.wallet)
+ admin_checker = WalletAdminKeyChecker(api_key=token)
+ await admin_checker.__call__(r)
+ wallet = WalletTypeInfo(0, admin_checker.wallet)
+ assert wallet.wallet is not None
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
):
@@ -153,9 +157,9 @@ async def get_key_type(
raise
try:
- checker = WalletInvoiceKeyChecker(api_key=token)
- await checker.__call__(r)
- wallet = WalletTypeInfo(1, checker.wallet)
+ invoice_checker = WalletInvoiceKeyChecker(api_key=token)
+ await invoice_checker.__call__(r)
+ wallet = WalletTypeInfo(1, invoice_checker.wallet)
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
):
@@ -174,8 +178,8 @@ async def get_key_type(
async def require_admin_key(
r: Request,
- api_key_header: str = Security(api_key_header),
- api_key_query: str = Security(api_key_query),
+ api_key_header = Security(api_key_header),
+ api_key_query = Security(api_key_query),
):
token = api_key_header if api_key_header else api_key_query
@@ -193,8 +197,8 @@ async def require_admin_key(
async def require_invoice_key(
r: Request,
- api_key_header: str = Security(api_key_header),
- api_key_query: str = Security(api_key_query),
+ api_key_header = Security(api_key_header),
+ api_key_query = Security(api_key_query),
):
token = api_key_header if api_key_header else api_key_query
diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py
index 0ac3fd2a0..bad707ff1 100644
--- a/lnbits/wallets/eclair.py
+++ b/lnbits/wallets/eclair.py
@@ -7,7 +7,9 @@ from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
-from websockets import connect
+
+# mypy https://github.com/aaugustin/websockets/issues/940
+from websockets.client import connect
from websockets.exceptions import (
ConnectionClosed,
ConnectionClosedError,
From e1d1ce7edca9a26a4c956f82031f81f54f98ccbe Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 09:45:08 +0200
Subject: [PATCH 13/84] fix PaymentKwargs class for mypy
---
lnbits/core/services.py | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/lnbits/core/services.py b/lnbits/core/services.py
index 0b565ebb9..f4f962504 100644
--- a/lnbits/core/services.py
+++ b/lnbits/core/services.py
@@ -109,18 +109,15 @@ async def pay_invoice(
raise ValueError("Amount in invoice is too high.")
# put all parameters that don't change here
- PaymentKwargs = TypedDict(
- "PaymentKwargs",
- {
- "wallet_id": str,
- "payment_request": str,
- "payment_hash": str,
- "amount": int,
- "memo": str,
- "extra": Optional[Dict],
- },
- )
- payment_kwargs: PaymentKwargs = dict(
+ class PaymentKwargs(TypedDict):
+ wallet_id: str
+ payment_request: str
+ payment_hash: str
+ amount: int
+ memo: str
+ extra: Optional[Dict]
+
+ payment_kwargs: PaymentKwargs = PaymentKwargs(
wallet_id=wallet_id,
payment_request=payment_request,
payment_hash=invoice.payment_hash,
From 68a195952ada90895c11b5f146e643605f2366bf Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 10:05:30 +0200
Subject: [PATCH 14/84] mypy fixes, api, eclair
---
lnbits/core/views/api.py | 4 ++--
lnbits/wallets/eclair.py | 3 ++-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index bd15ee8d6..09eb4f906 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -181,7 +181,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
lnurl_response: Union[None, bool, str] = None
if data.lnurl_callback:
- if "lnurl_balance_check" in data:
+ if hasattr(data, "lnurl_balance_check"):
assert (
data.lnurl_balance_check is not None
), "lnurl_balance_check is required"
@@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet):
)
async def api_payments_create(
wallet: WalletTypeInfo = Depends(require_invoice_key),
- invoiceData: CreateInvoiceData = Body(...),
+ invoiceData = Body(...),
):
if invoiceData.out is True and wallet.wallet_type == 0:
if not invoiceData.bolt11:
diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py
index bad707ff1..b669f8b74 100644
--- a/lnbits/wallets/eclair.py
+++ b/lnbits/wallets/eclair.py
@@ -9,7 +9,8 @@ import httpx
from loguru import logger
# mypy https://github.com/aaugustin/websockets/issues/940
-from websockets.client import connect
+# from websockets.client import connect
+from websockets import connect
from websockets.exceptions import (
ConnectionClosed,
ConnectionClosedError,
From e96944a4fae70dbb8213cb36a63d6eebe1257b3f Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 10:24:40 +0200
Subject: [PATCH 15/84] fully fix decorators, thanks calle
---
lnbits/decorators.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index 77fe32276..b74230ef8 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -171,9 +171,10 @@ async def get_key_type(
if e.status_code == HTTPStatus.BAD_REQUEST:
raise
if e.status_code == HTTPStatus.UNAUTHORIZED:
- return WalletTypeInfo(2, None)
+ return WalletTypeInfo(2, Wallet())
except:
raise
+ return wallet
async def require_admin_key(
From 8dbf64a0d074657eed7517ca085181a3bf867657 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 10:34:01 +0200
Subject: [PATCH 16/84] ignore eclair websockets type
---
lnbits/wallets/eclair.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py
index b669f8b74..b43d90c10 100644
--- a/lnbits/wallets/eclair.py
+++ b/lnbits/wallets/eclair.py
@@ -8,9 +8,10 @@ from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
+from websockets import connect # type: ignore
+# TODO: https://github.com/lnbits/lnbits-legend/issues/764
# mypy https://github.com/aaugustin/websockets/issues/940
-# from websockets.client import connect
-from websockets import connect
+
from websockets.exceptions import (
ConnectionClosed,
ConnectionClosedError,
From 56b4b0cf435ae796c641b867162328b83f55b65b Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 11:21:38 +0200
Subject: [PATCH 17/84] fix last views api bug, thanks calle
---
lnbits/core/views/api.py | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 09eb4f906..6766ad8d1 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -387,19 +387,13 @@ async def api_payments_sse(
async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)):
# We use X_Api_Key here because we want this call to work with and without keys
# If a valid key is given, we also return the field "details", otherwise not
- wallet = None
- try:
- assert X_Api_Key is not None
- # TODO: type above is Optional[str] how can that have .extra?
- if X_Api_Key.extra:
- logger.warning("No key")
- except:
- if X_Api_Key is not None:
- wallet = await get_wallet_for_key(X_Api_Key)
+ wallet = await get_wallet_for_key(X_Api_Key) if type(X_Api_Key) == str else None
+
+ # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order
+ # and get_standalone_payment otherwise just fetches the first one, causing unpredictable results
payment = await get_standalone_payment(
payment_hash, wallet_id=wallet.id if wallet else None
- ) # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order
- # and get_standalone_payment otherwise just fetches the first one, causing unpredictable results
+ )
if payment is None:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Payment does not exist."
From 64502c355eff38dd6ba43da4c7a09b055397bba0 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 11:33:37 +0200
Subject: [PATCH 18/84] black formating
---
lnbits/core/views/api.py | 2 +-
lnbits/core/views/generic.py | 16 ++++++++--------
lnbits/decorators.py | 12 ++++++------
lnbits/wallets/eclair.py | 3 ++-
lnbits/wallets/fake.py | 4 +---
5 files changed, 18 insertions(+), 19 deletions(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 6766ad8d1..95e9dde3a 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet):
)
async def api_payments_create(
wallet: WalletTypeInfo = Depends(require_invoice_key),
- invoiceData = Body(...),
+ invoiceData=Body(...),
):
if invoiceData.out is True and wallet.wallet_type == 0:
if not invoiceData.bolt11:
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 7ecedcf82..00d321af2 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -54,9 +54,9 @@ async def home(request: Request, lightning: str = None):
)
async def extensions(
request: Request,
- user = Depends(check_user_exists),
- enable = Query(None),
- disable = Query(None),
+ user=Depends(check_user_exists),
+ enable=Query(None),
+ disable=Query(None),
):
extension_to_enable = enable
extension_to_disable = disable
@@ -100,10 +100,10 @@ nothing: create everything
""",
)
async def wallet(
- request = Query(None),
- nme = Query(None),
- usr = Query(None),
- wal = Query(None),
+ request=Query(None),
+ nme=Query(None),
+ usr=Query(None),
+ wal=Query(None),
):
user_id = usr.hex if usr else None
wallet_id = wal.hex if wal else None
@@ -209,7 +209,7 @@ async def lnurl_full_withdraw_callback(request: Request):
@core_html_routes.get("/deletewallet", response_class=RedirectResponse)
-async def deletewallet(request: Request, wal = Query(...), usr = Query(...)):
+async def deletewallet(request: Request, wal=Query(...), usr=Query(...)):
user = await get_user(usr)
assert user is not None
user_wallet_ids = [u.id for u in user.wallets]
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index b74230ef8..ef689f8d0 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -123,8 +123,8 @@ api_key_query = APIKeyQuery(
async def get_key_type(
r: Request,
- api_key_header = Security(api_key_header),
- api_key_query = Security(api_key_query),
+ api_key_header=Security(api_key_header),
+ api_key_query=Security(api_key_query),
) -> WalletTypeInfo:
# 0: admin
# 1: invoice
@@ -179,8 +179,8 @@ async def get_key_type(
async def require_admin_key(
r: Request,
- api_key_header = Security(api_key_header),
- api_key_query = Security(api_key_query),
+ api_key_header=Security(api_key_header),
+ api_key_query=Security(api_key_query),
):
token = api_key_header if api_key_header else api_key_query
@@ -198,8 +198,8 @@ async def require_admin_key(
async def require_invoice_key(
r: Request,
- api_key_header = Security(api_key_header),
- api_key_query = Security(api_key_query),
+ api_key_header=Security(api_key_header),
+ api_key_query=Security(api_key_query),
):
token = api_key_header if api_key_header else api_key_query
diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py
index b43d90c10..122895178 100644
--- a/lnbits/wallets/eclair.py
+++ b/lnbits/wallets/eclair.py
@@ -8,7 +8,8 @@ from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
-from websockets import connect # type: ignore
+from websockets import connect # type: ignore
+
# TODO: https://github.com/lnbits/lnbits-legend/issues/764
# mypy https://github.com/aaugustin/websockets/issues/940
diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py
index c2b2307ab..ba2956c58 100644
--- a/lnbits/wallets/fake.py
+++ b/lnbits/wallets/fake.py
@@ -81,9 +81,7 @@ class FakeWallet(Wallet):
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
invoice = decode(bolt11)
# TODO: no data here?
- data: Dict = {
- "privkey": "missing"
- }
+ data: Dict = {"privkey": "missing"}
if (
invoice.checking_id is not None
and invoice.checking_id[6:] == data["privkey"][:6]
From 627959e30ba83342834f2f02b25c5398d7cde63f Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 11:52:49 +0200
Subject: [PATCH 19/84] add tests for api_payment
---
tests/core/views/test_api.py | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py
index 10e659aa4..1f858870b 100644
--- a/tests/core/views/test_api.py
+++ b/tests/core/views/test_api.py
@@ -140,3 +140,27 @@ async def test_decode_invoice(client, invoice):
)
assert response.status_code < 300
assert response.json()["payment_hash"] == invoice["payment_hash"]
+
+
+# check api_payment() internal function call (NOT API): payment status
+@pytest.mark.asyncio
+async def test_api_payment_without_key(invoice):
+ # check the payment status
+ response = await api_payment(invoice["payment_hash"])
+ assert type(response) == dict
+ assert response["paid"] == True
+ # not key, that's why no "details"
+ assert "details" not in response
+
+
+# check api_payment() internal function call (NOT API): payment status
+@pytest.mark.asyncio
+async def test_api_payment_with_key(invoice, inkey_headers_from):
+ # check the payment status
+ response = await api_payment(
+ invoice["payment_hash"], inkey_headers_from["X-Api-Key"]
+ )
+ assert type(response) == dict
+ assert response["paid"] == True
+ # not key, that's why no "details"
+ assert "details" in response
From 5f6dc757dcd08f2b248cbd5296616cc6c90502c0 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 12:00:53 +0200
Subject: [PATCH 20/84] types-mock==4.0.15 to requirements, needed for mypy
tests
---
requirements.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/requirements.txt b/requirements.txt
index 322712f5a..8ee1fc6c3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -43,6 +43,7 @@ sqlalchemy==1.3.23
sqlalchemy-aio==0.16.0
sse-starlette==0.6.2
starlette==0.14.2
+types-mock==4.0.15
types-protobuf==3.19.22
typing-extensions==3.10.0.2
uvicorn==0.15.0
From c186e53cef1a5331be2981bce6092f0b5bf5adb9 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 12:05:35 +0200
Subject: [PATCH 21/84] fixup requirements.txt and ad to Pipfile
---
Pipfile | 12 +++++++-----
requirements.txt | 2 --
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/Pipfile b/Pipfile
index 8ef241f12..60c79292f 100644
--- a/Pipfile
+++ b/Pipfile
@@ -35,9 +35,11 @@ pycryptodomex = "*"
[dev-packages]
black = "==20.8b1"
-pytest = "*"
-pytest-cov = "*"
-mypy = "*"
-pytest-asyncio = "*"
-requests = "*"
mock = "*"
+mypy = "*"
+pytest = "*"
+pytest-asyncio = "*"
+pytest-cov = "*"
+requests = "*"
+types-mock = "*"
+types-protobuf = "*"
diff --git a/requirements.txt b/requirements.txt
index 8ee1fc6c3..3603ab036 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -43,8 +43,6 @@ sqlalchemy==1.3.23
sqlalchemy-aio==0.16.0
sse-starlette==0.6.2
starlette==0.14.2
-types-mock==4.0.15
-types-protobuf==3.19.22
typing-extensions==3.10.0.2
uvicorn==0.15.0
uvloop==0.16.0
From 2e0276bc605a1b704cbfeee39e3d5fd03881376e Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 12:17:34 +0200
Subject: [PATCH 22/84] make test run again, introduce mypy again
---
lnbits/core/views/api.py | 2 +-
tests/core/views/test_api.py | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 95e9dde3a..c81f623a7 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet):
)
async def api_payments_create(
wallet: WalletTypeInfo = Depends(require_invoice_key),
- invoiceData=Body(...),
+ invoiceData: CreateInvoiceData = Body(...),
):
if invoiceData.out is True and wallet.wallet_type == 0:
if not invoiceData.bolt11:
diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py
index 1f858870b..01427cbac 100644
--- a/tests/core/views/test_api.py
+++ b/tests/core/views/test_api.py
@@ -1,6 +1,7 @@
import pytest
import pytest_asyncio
from lnbits.core.crud import get_wallet
+from lnbits.core.views.api import api_payment
from ...helpers import get_random_invoice_data
From 06b1f71d3954d0dcb9de193a91845041a8de2ca2 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 12:21:39 +0200
Subject: [PATCH 23/84] fix up decorators for tests
---
lnbits/decorators.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index ef689f8d0..adfbd6649 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -1,6 +1,6 @@
from http import HTTPStatus
-from typing import Optional
+from typing import Union
from cerberus import Validator # type: ignore
from fastapi import status
@@ -102,9 +102,9 @@ class WalletAdminKeyChecker(KeyChecker):
class WalletTypeInfo:
wallet_type: int
- wallet: Wallet
+ wallet: Union[Wallet, None]
- def __init__(self, wallet_type: int, wallet: Wallet) -> None:
+ def __init__(self, wallet_type: int, wallet: Union[Wallet, None]) -> None:
self.wallet_type = wallet_type
self.wallet = wallet
@@ -171,7 +171,7 @@ async def get_key_type(
if e.status_code == HTTPStatus.BAD_REQUEST:
raise
if e.status_code == HTTPStatus.UNAUTHORIZED:
- return WalletTypeInfo(2, Wallet())
+ return WalletTypeInfo(2, None)
except:
raise
return wallet
From f4e3a3980e33ccca01de4502e8882993d106555c Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 13:12:55 +0200
Subject: [PATCH 24/84] fix isort check
---
lnbits/core/views/api.py | 2 +-
lnbits/decorators.py | 1 -
lnbits/utils/exchange_rates.py | 2 +-
lnbits/wallets/eclair.py | 4 +---
4 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index c81f623a7..d7be3e554 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -3,7 +3,7 @@ import hashlib
import json
from binascii import unhexlify
from http import HTTPStatus
-from typing import Dict, List, Optional, Union, Tuple
+from typing import Dict, List, Optional, Tuple, Union
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
import httpx
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index adfbd6649..b62e456af 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -1,5 +1,4 @@
from http import HTTPStatus
-
from typing import Union
from cerberus import Validator # type: ignore
diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py
index fbb4add22..2801146b5 100644
--- a/lnbits/utils/exchange_rates.py
+++ b/lnbits/utils/exchange_rates.py
@@ -1,5 +1,5 @@
import asyncio
-from typing import Callable, NamedTuple, List
+from typing import Callable, List, NamedTuple
import httpx
from loguru import logger
diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py
index 122895178..1ba81385e 100644
--- a/lnbits/wallets/eclair.py
+++ b/lnbits/wallets/eclair.py
@@ -8,11 +8,9 @@ from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
-from websockets import connect # type: ignore
-
# TODO: https://github.com/lnbits/lnbits-legend/issues/764
# mypy https://github.com/aaugustin/websockets/issues/940
-
+from websockets import connect # type: ignore
from websockets.exceptions import (
ConnectionClosed,
ConnectionClosedError,
From 0784ae02b1fa940000ecab917869c16bf28055de Mon Sep 17 00:00:00 2001
From: Daniel Krahofer
Date: Wed, 20 Jul 2022 13:15:06 +0200
Subject: [PATCH 25/84] Update tests/core/views/test_api.py
Co-authored-by: calle <93376500+callebtc@users.noreply.github.com>
---
tests/core/views/test_api.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py
index 01427cbac..d5eeeeae0 100644
--- a/tests/core/views/test_api.py
+++ b/tests/core/views/test_api.py
@@ -163,5 +163,4 @@ async def test_api_payment_with_key(invoice, inkey_headers_from):
)
assert type(response) == dict
assert response["paid"] == True
- # not key, that's why no "details"
assert "details" in response
From 613c5b466727ce77b1f81c8102f674dfe6f39536 Mon Sep 17 00:00:00 2001
From: Daniel Krahofer
Date: Wed, 20 Jul 2022 13:15:18 +0200
Subject: [PATCH 26/84] Update tests/core/views/test_api.py
Co-authored-by: calle <93376500+callebtc@users.noreply.github.com>
---
tests/core/views/test_api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py
index d5eeeeae0..61a71d31f 100644
--- a/tests/core/views/test_api.py
+++ b/tests/core/views/test_api.py
@@ -150,7 +150,7 @@ async def test_api_payment_without_key(invoice):
response = await api_payment(invoice["payment_hash"])
assert type(response) == dict
assert response["paid"] == True
- # not key, that's why no "details"
+ # no key, that's why no "details"
assert "details" not in response
From 04df2129b0fd26ec2bb3e81ce5e6bb82985cee50 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 13:21:42 +0200
Subject: [PATCH 27/84] add missing Pipfile.lock
---
Pipfile.lock | 168 ++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 132 insertions(+), 36 deletions(-)
diff --git a/Pipfile.lock b/Pipfile.lock
index 6a89abb32..2e6d3a141 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "81bd288eea338c3bf1b70b8d30c1185b84c13a25a595bcddd77f74f7bc090032"
+ "sha256": "35eac2d1e375057cb654888c4414ec0bd479a0df1dc6b51bf5b72ba78d52ea36"
},
"pipfile-spec": 6,
"requires": {
@@ -29,6 +29,7 @@
"sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b",
"sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"
],
+ "markers": "python_full_version >= '3.6.2'",
"version": "==3.6.1"
},
"asyncio": {
@@ -46,6 +47,7 @@
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
"sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.4.0"
},
"bech32": {
@@ -53,6 +55,7 @@
"sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899",
"sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981"
],
+ "markers": "python_version >= '3.5'",
"version": "==1.2.0"
},
"bitstring": {
@@ -76,6 +79,7 @@
"sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d",
"sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"
],
+ "markers": "python_version >= '3.6'",
"version": "==2022.6.15"
},
"cffi": {
@@ -152,6 +156,7 @@
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
],
+ "markers": "python_version >= '3.7'",
"version": "==8.1.3"
},
"ecdsa": {
@@ -179,17 +184,18 @@
},
"fastapi": {
"hashes": [
- "sha256:15fcabd5c78c266fa7ae7d8de9b384bfc2375ee0503463a6febbe3bab69d6f65",
- "sha256:3233d4a789ba018578658e2af1a4bb5e38bdd122ff722b313666a9b2c6786a83"
+ "sha256:cf0ff6db25b91d321050c4112baab0908c90f19b40bf257f9591d2f9780d1f22",
+ "sha256:d337563424ceada23857f73d5abe8dae0c28e4cccb53b2af06e78b7bb4a1c7d7"
],
"index": "pypi",
- "version": "==0.78.0"
+ "version": "==0.79.0"
},
"h11": {
"hashes": [
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
],
+ "markers": "python_version >= '3.6'",
"version": "==0.12.0"
},
"httpcore": {
@@ -197,6 +203,7 @@
"sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6",
"sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b"
],
+ "markers": "python_version >= '3.7'",
"version": "==0.15.0"
},
"httptools": {
@@ -251,8 +258,17 @@
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
],
+ "markers": "python_version >= '3.5'",
"version": "==3.3"
},
+ "importlib-metadata": {
+ "hashes": [
+ "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670",
+ "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"
+ ],
+ "markers": "python_version < '3.8'",
+ "version": "==4.12.0"
+ },
"jinja2": {
"hashes": [
"sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4",
@@ -320,6 +336,7 @@
"sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
"sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
],
+ "markers": "python_version >= '3.7'",
"version": "==2.1.1"
},
"marshmallow": {
@@ -327,6 +344,7 @@
"sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb",
"sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7"
],
+ "markers": "python_version >= '3.7'",
"version": "==3.17.0"
},
"outcome": {
@@ -334,6 +352,7 @@
"sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672",
"sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5"
],
+ "markers": "python_version >= '3.7'",
"version": "==1.2.0"
},
"packaging": {
@@ -341,6 +360,7 @@
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
"sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
],
+ "markers": "python_version >= '3.6'",
"version": "==21.3"
},
"psycopg2-binary": {
@@ -486,6 +506,7 @@
"sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e",
"sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6"
],
+ "markers": "python_full_version >= '3.6.1'",
"version": "==1.9.1"
},
"pyngrok": {
@@ -500,14 +521,16 @@
"sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
"sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"
],
+ "markers": "python_full_version >= '3.6.8'",
"version": "==3.0.9"
},
"pypng": {
"hashes": [
- "sha256:76f8a1539ec56451da7ab7121f12a361969fe0f2d48d703d198ce2a99d6c5afd"
+ "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c",
+ "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1"
],
"index": "pypi",
- "version": "==0.0.21"
+ "version": "==0.20220715.0"
},
"pyqrcode": {
"hashes": [
@@ -574,9 +597,13 @@
"sha256:026c0de2ee8385d1255b9c2426cd4f03fe9177ac94c09979bc601946c8493aa0",
"sha256:99142650756ef1998ce0661568f54a47dac8c638fb27e3816c02536575dbba8c"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.6.0.post0"
},
"rfc3986": {
+ "extras": [
+ "idna2008"
+ ],
"hashes": [
"sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835",
"sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"
@@ -612,6 +639,14 @@
"index": "pypi",
"version": "==0.14.0"
},
+ "setuptools": {
+ "hashes": [
+ "sha256:0d33c374d41c7863419fc8f6c10bfe25b7b498aa34164d135c622e52580c6b16",
+ "sha256:c04b44a57a6265fe34a4a444e965884716d34bae963119a76353434d6f18e450"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==63.2.0"
+ },
"shortuuid": {
"hashes": [
"sha256:459f12fa1acc34ff213b1371467c0325169645a31ed989e268872339af7563d5",
@@ -625,6 +660,7 @@
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"sniffio": {
@@ -632,6 +668,7 @@
"sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663",
"sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"
],
+ "markers": "python_version >= '3.5'",
"version": "==1.2.0"
},
"sqlalchemy": {
@@ -699,6 +736,7 @@
"sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf",
"sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"
],
+ "markers": "python_version >= '3.6'",
"version": "==0.19.1"
},
"typing-extensions": {
@@ -707,10 +745,12 @@
"sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"
],
"index": "pypi",
- "markers": "python_version < '3.10'",
"version": "==4.3.0"
},
"uvicorn": {
+ "extras": [
+ "standard"
+ ],
"hashes": [
"sha256:c19a057deb1c5bb060946e2e5c262fc01590c6529c0af2c3d9ce941e89bc30e0",
"sha256:cade07c403c397f9fe275492a48c1b869efd175d5d8a692df649e6e7e2ed8f4e"
@@ -808,6 +848,14 @@
"sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4"
],
"version": "==10.3"
+ },
+ "zipp": {
+ "hashes": [
+ "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2",
+ "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==3.8.1"
}
},
"develop": {
@@ -823,6 +871,7 @@
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
"sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.4.0"
},
"black": {
@@ -837,6 +886,7 @@
"sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d",
"sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"
],
+ "markers": "python_version >= '3.6'",
"version": "==2022.6.15"
},
"charset-normalizer": {
@@ -844,6 +894,7 @@
"sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5",
"sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"
],
+ "markers": "python_version >= '3.6'",
"version": "==2.1.0"
},
"click": {
@@ -851,9 +902,13 @@
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
],
+ "markers": "python_version >= '3.7'",
"version": "==8.1.3"
},
"coverage": {
+ "extras": [
+ "toml"
+ ],
"hashes": [
"sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32",
"sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7",
@@ -897,6 +952,7 @@
"sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39",
"sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"
],
+ "markers": "python_version >= '3.7'",
"version": "==6.4.2"
},
"idna": {
@@ -904,8 +960,17 @@
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
],
+ "markers": "python_version >= '3.5'",
"version": "==3.3"
},
+ "importlib-metadata": {
+ "hashes": [
+ "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670",
+ "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"
+ ],
+ "markers": "python_version < '3.8'",
+ "version": "==4.12.0"
+ },
"iniconfig": {
"hashes": [
"sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
@@ -923,32 +988,32 @@
},
"mypy": {
"hashes": [
- "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5",
- "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66",
- "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e",
- "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56",
- "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e",
- "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d",
- "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813",
- "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932",
- "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569",
- "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b",
- "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0",
- "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648",
- "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6",
- "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950",
- "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15",
- "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723",
- "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a",
- "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3",
- "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6",
- "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24",
- "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b",
- "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d",
- "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492"
+ "sha256:02ef476f6dcb86e6f502ae39a16b93285fef97e7f1ff22932b657d1ef1f28655",
+ "sha256:0d054ef16b071149917085f51f89555a576e2618d5d9dd70bd6eea6410af3ac9",
+ "sha256:19830b7dba7d5356d3e26e2427a2ec91c994cd92d983142cbd025ebe81d69cf3",
+ "sha256:1f7656b69974a6933e987ee8ffb951d836272d6c0f81d727f1d0e2696074d9e6",
+ "sha256:23488a14a83bca6e54402c2e6435467a4138785df93ec85aeff64c6170077fb0",
+ "sha256:23c7ff43fff4b0df93a186581885c8512bc50fc4d4910e0f838e35d6bb6b5e58",
+ "sha256:25c5750ba5609a0c7550b73a33deb314ecfb559c350bb050b655505e8aed4103",
+ "sha256:2ad53cf9c3adc43cf3bea0a7d01a2f2e86db9fe7596dfecb4496a5dda63cbb09",
+ "sha256:3fa7a477b9900be9b7dd4bab30a12759e5abe9586574ceb944bc29cddf8f0417",
+ "sha256:40b0f21484238269ae6a57200c807d80debc6459d444c0489a102d7c6a75fa56",
+ "sha256:4b21e5b1a70dfb972490035128f305c39bc4bc253f34e96a4adf9127cf943eb2",
+ "sha256:5a361d92635ad4ada1b1b2d3630fc2f53f2127d51cf2def9db83cba32e47c856",
+ "sha256:77a514ea15d3007d33a9e2157b0ba9c267496acf12a7f2b9b9f8446337aac5b0",
+ "sha256:855048b6feb6dfe09d3353466004490b1872887150c5bb5caad7838b57328cc8",
+ "sha256:9796a2ba7b4b538649caa5cecd398d873f4022ed2333ffde58eaf604c4d2cb27",
+ "sha256:98e02d56ebe93981c41211c05adb630d1d26c14195d04d95e49cd97dbc046dc5",
+ "sha256:b793b899f7cf563b1e7044a5c97361196b938e92f0a4343a5d27966a53d2ec71",
+ "sha256:d1ea5d12c8e2d266b5fb8c7a5d2e9c0219fedfeb493b7ed60cd350322384ac27",
+ "sha256:d2022bfadb7a5c2ef410d6a7c9763188afdb7f3533f22a0a32be10d571ee4bbe",
+ "sha256:d3348e7eb2eea2472db611486846742d5d52d1290576de99d59edeb7cd4a42ca",
+ "sha256:d744f72eb39f69312bc6c2abf8ff6656973120e2eb3f3ec4f758ed47e414a4bf",
+ "sha256:ef943c72a786b0f8d90fd76e9b39ce81fb7171172daf84bf43eaf937e9f220a9",
+ "sha256:f2899a3cbd394da157194f913a931edfd4be5f274a88041c9dc2d9cdcb1c315c"
],
"index": "pypi",
- "version": "==0.961"
+ "version": "==0.971"
},
"mypy-extensions": {
"hashes": [
@@ -962,6 +1027,7 @@
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
"sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
],
+ "markers": "python_version >= '3.6'",
"version": "==21.3"
},
"pathspec": {
@@ -976,6 +1042,7 @@
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
],
+ "markers": "python_version >= '3.6'",
"version": "==1.0.0"
},
"py": {
@@ -983,6 +1050,7 @@
"sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
"sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.11.0"
},
"pyparsing": {
@@ -990,6 +1058,7 @@
"sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
"sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"
],
+ "markers": "python_full_version >= '3.6.8'",
"version": "==3.0.9"
},
"pytest": {
@@ -1002,12 +1071,11 @@
},
"pytest-asyncio": {
"hashes": [
- "sha256:16cf40bdf2b4fb7fc8e4b82bd05ce3fbcd454cbf7b92afc445fe299dabb88213",
- "sha256:7659bdb0a9eb9c6e3ef992eef11a2b3e69697800ad02fb06374a210d85b29f91",
- "sha256:8fafa6c52161addfd41ee7ab35f11836c5a16ec208f93ee388f752bea3493a84"
+ "sha256:7a97e37cfe1ed296e2e84941384bdd37c376453912d397ed39293e0916f521fa",
+ "sha256:ac4ebf3b6207259750bc32f4c1d8fcd7e79739edbc67ad0c58dd150b1d072fed"
],
"index": "pypi",
- "version": "==0.18.3"
+ "version": "==0.19.0"
},
"pytest-cov": {
"hashes": [
@@ -1094,6 +1162,7 @@
"sha256:f8a2fd2f62a77536e4e3193303bec380df40d99e253b1c8f9b6eafa07eaeff67",
"sha256:fbdf4fc6adf38fab1091c579ece3fe9f493bd0f1cfc3d2c76d2e52461ca4f8a9"
],
+ "markers": "python_version >= '3.6'",
"version": "==2022.7.9"
},
"requests": {
@@ -1109,6 +1178,7 @@
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.2"
},
"tomli": {
@@ -1116,6 +1186,7 @@
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
],
+ "markers": "python_version < '3.11'",
"version": "==2.0.1"
},
"typed-ast": {
@@ -1145,15 +1216,31 @@
"sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3",
"sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"
],
+ "markers": "python_version >= '3.6'",
"version": "==1.5.4"
},
+ "types-mock": {
+ "hashes": [
+ "sha256:4535fbb3912b88a247d43cdb41db0c8b2e187138986f6f01a989717e56105848",
+ "sha256:a849bc2d966063f4946013bf404822ee2b96f77a8dccda4174b70ab61c5293fe"
+ ],
+ "index": "pypi",
+ "version": "==4.0.15"
+ },
+ "types-protobuf": {
+ "hashes": [
+ "sha256:d291388678af91bb045fafa864f142dc4ac22f5d4cdca097c7d8d8a32fa9b3ab",
+ "sha256:d2b26861b0cb46a3c8669b0df507b7ef72e487da66d61f9f3576aa76ce028a83"
+ ],
+ "index": "pypi",
+ "version": "==3.19.22"
+ },
"typing-extensions": {
"hashes": [
"sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02",
"sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"
],
"index": "pypi",
- "markers": "python_version < '3.10'",
"version": "==4.3.0"
},
"urllib3": {
@@ -1161,7 +1248,16 @@
"sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec",
"sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
"version": "==1.26.10"
+ },
+ "zipp": {
+ "hashes": [
+ "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2",
+ "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==3.8.1"
}
}
}
From 76fdce4328a946fa7aa78be27a870adbf5846f4d Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 13:23:50 +0200
Subject: [PATCH 28/84] enable mypy checks
---
.github/workflows/mypy.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml
index 4d6c6d4da..bf90a8e31 100644
--- a/.github/workflows/mypy.yml
+++ b/.github/workflows/mypy.yml
@@ -5,7 +5,6 @@ on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
- if: ${{ 'false' == 'true' }} # skip mypy for now
steps:
- uses: actions/checkout@v1
- uses: jpetrucciani/mypy-check@master
From 5aa30c843c6604a9fb013f99094b8c454d650b1a Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 13:41:13 +0200
Subject: [PATCH 29/84] added FIXME tags and fix WalletTypeInfo
---
lnbits/core/services.py | 4 ++++
lnbits/core/views/api.py | 23 +++++++++++++++++++++++
lnbits/decorators.py | 2 ++
3 files changed, 29 insertions(+)
diff --git a/lnbits/core/services.py b/lnbits/core/services.py
index f4f962504..2416ed315 100644
--- a/lnbits/core/services.py
+++ b/lnbits/core/services.py
@@ -269,6 +269,10 @@ async def perform_lnurlauth(
cb = urlparse(callback)
k1 = unhexlify(parse_qs(cb.query)["k1"][0])
+
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
+
key = wallet.wallet.lnurlauth_key(cb.netloc)
def int_to_bytes_suitable_der(x: int) -> bytes:
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index d7be3e554..a3a59a5f0 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -56,12 +56,16 @@ from ..tasks import api_invoice_listeners
@core_app.get("/api/v1/wallet")
async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
if wallet.wallet_type == 0:
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
return {
"id": wallet.wallet.id,
"name": wallet.wallet.name,
"balance": wallet.wallet.balance_msat,
}
else:
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
return {"name": wallet.wallet.name, "balance": wallet.wallet.balance_msat}
@@ -69,6 +73,9 @@ async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_update_balance(
amount: int, wallet: WalletTypeInfo = Depends(get_key_type)
):
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
+
if wallet.wallet.user not in LNBITS_ADMIN_USERS:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user"
@@ -98,6 +105,9 @@ async def api_update_balance(
async def api_update_wallet(
new_name: str, wallet: WalletTypeInfo = Depends(require_admin_key)
):
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
+
await update_wallet(wallet.wallet.id, new_name)
return {
"id": wallet.wallet.id,
@@ -112,6 +122,9 @@ async def api_payments(
offset: Optional[int] = None,
wallet: WalletTypeInfo = Depends(get_key_type),
):
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
+
pendingPayments = await get_payments(
wallet_id=wallet.wallet.id,
pending=True,
@@ -256,11 +269,15 @@ async def api_payments_create(
status_code=HTTPStatus.BAD_REQUEST,
detail="BOLT11 string is invalid or not given",
)
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
return await api_payments_pay_invoice(
invoiceData.bolt11, wallet.wallet
) # admin key
elif not invoiceData.out:
# invoice key
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
return await api_payments_create_invoice(invoiceData, wallet.wallet)
else:
raise HTTPException(
@@ -325,6 +342,8 @@ async def api_payments_pay_lnurl(
if data.comment:
extra["comment"] = data.comment
assert data.description is not None, "description is required"
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
payment_hash = await pay_invoice(
wallet_id=wallet.wallet.id,
payment_request=params["pr"],
@@ -378,6 +397,8 @@ async def subscribe(request: Request, wallet: Wallet):
async def api_payments_sse(
request: Request, wallet: WalletTypeInfo = Depends(get_key_type)
):
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
return EventSourceResponse(
subscribe(request, wallet.wallet), ping=20, media_type="text/event-stream"
)
@@ -456,6 +477,8 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
params.update(kind="auth")
params.update(callback=url) # with k1 already in it
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
lnurlauth_key = wallet.wallet.lnurlauth_key(domain)
params.update(pubkey=lnurlauth_key.verifying_key.to_string("compressed").hex())
else:
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index b62e456af..9f51ce646 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -159,6 +159,8 @@ async def get_key_type(
invoice_checker = WalletInvoiceKeyChecker(api_key=token)
await invoice_checker.__call__(r)
wallet = WalletTypeInfo(1, invoice_checker.wallet)
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
):
From 0e8da3c368389c369a0723bec9edf520d2fdd130 Mon Sep 17 00:00:00 2001
From: ben
Date: Sat, 23 Jul 2022 20:08:59 +0100
Subject: [PATCH 30/84] Fixed path for extension templates
---
lnbits/extensions/bleskomat/__init__.py | 2 +-
lnbits/extensions/copilot/__init__.py | 2 +-
lnbits/extensions/discordbot/__init__.py | 2 +-
lnbits/extensions/jukebox/__init__.py | 2 +-
lnbits/extensions/livestream/__init__.py | 2 +-
lnbits/extensions/lnurlp/__init__.py | 2 +-
lnbits/extensions/offlineshop/__init__.py | 3 ++-
lnbits/extensions/splitpayments/__init__.py | 2 +-
lnbits/extensions/withdraw/__init__.py | 2 +-
9 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/lnbits/extensions/bleskomat/__init__.py b/lnbits/extensions/bleskomat/__init__.py
index 06b4a441e..bef362dc7 100644
--- a/lnbits/extensions/bleskomat/__init__.py
+++ b/lnbits/extensions/bleskomat/__init__.py
@@ -18,7 +18,7 @@ bleskomat_ext: APIRouter = APIRouter(prefix="/bleskomat", tags=["Bleskomat"])
def bleskomat_renderer():
- return template_renderer([StaticFiles(packages=[("lnbits", "extensions/bleskomat/static/templates")])])
+ return template_renderer(["lnbits/extensions/bleskomat/templates"])
from .lnurl_api import * # noqa
diff --git a/lnbits/extensions/copilot/__init__.py b/lnbits/extensions/copilot/__init__.py
index 8634505f0..806801ce1 100644
--- a/lnbits/extensions/copilot/__init__.py
+++ b/lnbits/extensions/copilot/__init__.py
@@ -20,7 +20,7 @@ copilot_ext: APIRouter = APIRouter(prefix="/copilot", tags=["copilot"])
def copilot_renderer():
- return template_renderer([StaticFiles(packages=[("lnbits", "extensions/copilot/static/templates")])])
+ return template_renderer(["lnbits/extensions/copilot/templates"])
from .lnurl import * # noqa
diff --git a/lnbits/extensions/discordbot/__init__.py b/lnbits/extensions/discordbot/__init__.py
index eb755bd1b..21989b24a 100644
--- a/lnbits/extensions/discordbot/__init__.py
+++ b/lnbits/extensions/discordbot/__init__.py
@@ -18,7 +18,7 @@ discordbot_ext: APIRouter = APIRouter(prefix="/discordbot", tags=["discordbot"])
def discordbot_renderer():
- return template_renderer([StaticFiles(packages=[("lnbits", "extensions/discordbot/static/templates")])])
+ return template_renderer(["lnbits/extensions/discordbot/templates"])
from .views import * # noqa
diff --git a/lnbits/extensions/jukebox/__init__.py b/lnbits/extensions/jukebox/__init__.py
index ee92ec6ae..4559dccfd 100644
--- a/lnbits/extensions/jukebox/__init__.py
+++ b/lnbits/extensions/jukebox/__init__.py
@@ -21,7 +21,7 @@ jukebox_ext: APIRouter = APIRouter(prefix="/jukebox", tags=["jukebox"])
def jukebox_renderer():
- return template_renderer([StaticFiles(packages=[("lnbits", "extensions/jukebox/static/templates")])])
+ return template_renderer(["lnbits/extensions/jukebox/templates"])
from .tasks import wait_for_paid_invoices
diff --git a/lnbits/extensions/livestream/__init__.py b/lnbits/extensions/livestream/__init__.py
index fbfcf22af..e5f2987c8 100644
--- a/lnbits/extensions/livestream/__init__.py
+++ b/lnbits/extensions/livestream/__init__.py
@@ -21,7 +21,7 @@ livestream_ext: APIRouter = APIRouter(prefix="/livestream", tags=["livestream"])
def livestream_renderer():
- return template_renderer([StaticFiles(packages=[("lnbits", "extensions/livestream/static/templates")])])
+ return template_renderer(["lnbits/extensions/livestream/templates"])
from .lnurl import * # noqa
diff --git a/lnbits/extensions/lnurlp/__init__.py b/lnbits/extensions/lnurlp/__init__.py
index 02dbf2ceb..e2486df05 100644
--- a/lnbits/extensions/lnurlp/__init__.py
+++ b/lnbits/extensions/lnurlp/__init__.py
@@ -21,7 +21,7 @@ lnurlp_ext: APIRouter = APIRouter(prefix="/lnurlp", tags=["lnurlp"])
def lnurlp_renderer():
- return template_renderer([StaticFiles(packages=[("lnbits", "extensions/lnurlp/static/templates")])])
+ return template_renderer(["lnbits/extensions/lnurlp/templates"])
from .lnurl import * # noqa
diff --git a/lnbits/extensions/offlineshop/__init__.py b/lnbits/extensions/offlineshop/__init__.py
index bacbf339f..2dbaa7d0f 100644
--- a/lnbits/extensions/offlineshop/__init__.py
+++ b/lnbits/extensions/offlineshop/__init__.py
@@ -18,7 +18,8 @@ offlineshop_ext: APIRouter = APIRouter(prefix="/offlineshop", tags=["Offlineshop
def offlineshop_renderer():
- return template_renderer([StaticFiles(packages=[("lnbits", "extensions/offlineshop/static/templates")])])
+ return template_renderer(["lnbits/extensions/offlineshop/templates"])
+
from .lnurl import * # noqa
diff --git a/lnbits/extensions/splitpayments/__init__.py b/lnbits/extensions/splitpayments/__init__.py
index 3edac442d..9989728ea 100644
--- a/lnbits/extensions/splitpayments/__init__.py
+++ b/lnbits/extensions/splitpayments/__init__.py
@@ -22,7 +22,7 @@ splitpayments_ext: APIRouter = APIRouter(
def splitpayments_renderer():
- return template_renderer([StaticFiles(packages=[("lnbits", "extensions/splitpayments/static/templates")])])
+ return template_renderer(["lnbits/extensions/splitpayments/templates"])
from .tasks import wait_for_paid_invoices
diff --git a/lnbits/extensions/withdraw/__init__.py b/lnbits/extensions/withdraw/__init__.py
index 187ee7c00..a0f4b6068 100644
--- a/lnbits/extensions/withdraw/__init__.py
+++ b/lnbits/extensions/withdraw/__init__.py
@@ -19,7 +19,7 @@ withdraw_ext: APIRouter = APIRouter(prefix="/withdraw", tags=["withdraw"])
def withdraw_renderer():
- return template_renderer([StaticFiles(packages=[("lnbits", "extensions/withdraw/static/templates")])])
+ return template_renderer(["lnbits/extensions/withdraw/templates"])
from .lnurl import * # noqa
From 2891a421f196e7cf4db41c70e2cae460ac02157f Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 19 Jul 2022 13:06:19 +0200
Subject: [PATCH 31/84] fix up mypy.ini, and properly ignore grpc and extension
---
mypy.ini | 7 ++++---
requirements.txt | 1 +
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/mypy.ini b/mypy.ini
index 735f89e05..e5a974b51 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -1,7 +1,8 @@
[mypy]
ignore_missing_imports = True
-exclude = lnbits/wallets/lnd_grpc_files/
-exclude = lnbits/extensions/
-
+exclude = (?x)(
+ ^lnbits/extensions.
+ | ^lnbits/wallets/lnd_grpc_files.
+ )
[mypy-lnbits.wallets.lnd_grpc_files.*]
follow_imports = skip
diff --git a/requirements.txt b/requirements.txt
index f8ccf47cb..512011ec2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -44,6 +44,7 @@ sqlalchemy==1.3.23
sqlalchemy-aio==0.16.0
sse-starlette==0.6.2
starlette==0.14.2
+types-protobuf==3.19.22
typing-extensions==3.10.0.2
uvicorn==0.15.0
uvloop==0.16.0
From 895770e26ace770a2822ed8dddba29557dea7c5f Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 19 Jul 2022 13:36:44 +0200
Subject: [PATCH 32/84] remove commented out and unused code in app object, it
is now used in lnbits/__main__.py directly, fixes mypy error
---
lnbits/app.py | 8 --------
1 file changed, 8 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index a7c8fdaf3..eaf8a9db4 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -17,7 +17,6 @@ from loguru import logger
import lnbits.settings
from lnbits.core.tasks import register_task_listeners
-from .commands import db_migrate, handle_assets
from .core import core_app
from .core.views.generic import core_html_routes
from .helpers import (
@@ -84,7 +83,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
check_funding_source(app)
register_assets(app)
register_routes(app)
- # register_commands(app)
register_async_tasks(app)
register_exception_handlers(app)
@@ -137,12 +135,6 @@ def register_routes(app: FastAPI) -> None:
)
-def register_commands(app: FastAPI):
- """Register Click commands."""
- app.cli.add_command(db_migrate)
- app.cli.add_command(handle_assets)
-
-
def register_assets(app: FastAPI):
"""Serve each vendored asset separately or a bundle."""
From 808bd36e23765ca502b37c720349bdfe1da039bc Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 19 Jul 2022 18:51:35 +0200
Subject: [PATCH 33/84] some more mypy fixes
---
lnbits/core/crud.py | 2 +-
lnbits/core/models.py | 2 ++
lnbits/core/views/api.py | 15 +++++++++------
lnbits/core/views/generic.py | 6 +++++-
lnbits/helpers.py | 2 +-
lnbits/tasks.py | 2 +-
lnbits/utils/exchange_rates.py | 8 ++++----
lnbits/wallets/lnpay.py | 2 +-
lnbits/wallets/opennode.py | 2 +-
9 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py
index 770e2906a..db802d7bb 100644
--- a/lnbits/core/crud.py
+++ b/lnbits/core/crud.py
@@ -113,7 +113,7 @@ async def create_wallet(
async def update_wallet(
wallet_id: str, new_name: str, conn: Optional[Connection] = None
) -> Optional[Wallet]:
- await (conn or db).execute(
+ return await (conn or db).execute(
"""
UPDATE wallets SET
name = ?
diff --git a/lnbits/core/models.py b/lnbits/core/models.py
index ab73b7020..0f7eba737 100644
--- a/lnbits/core/models.py
+++ b/lnbits/core/models.py
@@ -106,6 +106,8 @@ class Payment(BaseModel):
@property
def tag(self) -> Optional[str]:
+ if self.extra is None:
+ return ""
return self.extra.get("tag")
@property
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 9fee6063d..bd15ee8d6 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -3,7 +3,7 @@ import hashlib
import json
from binascii import unhexlify
from http import HTTPStatus
-from typing import Dict, List, Optional, Union
+from typing import Dict, List, Optional, Union, Tuple
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
import httpx
@@ -185,7 +185,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
assert (
data.lnurl_balance_check is not None
), "lnurl_balance_check is required"
- save_balance_check(wallet.id, data.lnurl_balance_check)
+ await save_balance_check(wallet.id, data.lnurl_balance_check)
async with httpx.AsyncClient() as client:
try:
@@ -291,7 +291,7 @@ async def api_payments_pay_lnurl(
timeout=40,
)
if r.is_error:
- raise httpx.ConnectError
+ raise httpx.ConnectError("LNURL Callback Connection Error")
except (httpx.ConnectError, httpx.RequestError):
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
@@ -348,7 +348,7 @@ async def subscribe(request: Request, wallet: Wallet):
logger.debug("adding sse listener", payment_queue)
api_invoice_listeners.append(payment_queue)
- send_queue: asyncio.Queue[tuple[str, Payment]] = asyncio.Queue(0)
+ send_queue: asyncio.Queue[Tuple[str, Payment]] = asyncio.Queue(0)
async def payment_received() -> None:
while True:
@@ -389,10 +389,13 @@ async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)):
# If a valid key is given, we also return the field "details", otherwise not
wallet = None
try:
+ assert X_Api_Key is not None
+ # TODO: type above is Optional[str] how can that have .extra?
if X_Api_Key.extra:
logger.warning("No key")
except:
- wallet = await get_wallet_for_key(X_Api_Key)
+ if X_Api_Key is not None:
+ wallet = await get_wallet_for_key(X_Api_Key)
payment = await get_standalone_payment(
payment_hash, wallet_id=wallet.id if wallet else None
) # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order
@@ -606,7 +609,7 @@ class ConversionData(BaseModel):
async def api_fiat_as_sats(data: ConversionData):
output = {}
if data.from_ == "sat":
- output["sats"] = int(data.amount)
+ output["sats"] = data.amount
output["BTC"] = data.amount / 100000000
for currency in data.to.split(","):
output[currency.strip().upper()] = await satoshis_amount_as_fiat(
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 44666ce16..7ef306dc8 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -121,9 +121,11 @@ async def wallet(
if not user_id:
user = await get_user((await create_account()).id)
+ assert user is not None
logger.info(f"Create user {user.id}")
else:
user = await get_user(user_id)
+ assert user is not None
if not user:
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": "User does not exist."}
@@ -218,6 +220,7 @@ async def lnurl_full_withdraw_callback(request: Request):
@core_html_routes.get("/deletewallet", response_class=RedirectResponse)
async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)):
user = await get_user(usr)
+ assert user is not None
user_wallet_ids = [u.id for u in user.wallets]
if wal not in user_wallet_ids:
@@ -242,7 +245,7 @@ async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query
async def lnurl_balance_notify(request: Request, service: str):
bc = await get_balance_check(request.query_params.get("wal"), service)
if bc:
- redeem_lnurl_withdraw(bc.wallet, bc.url)
+ await redeem_lnurl_withdraw(bc.wallet, bc.url)
@core_html_routes.get(
@@ -252,6 +255,7 @@ async def lnurlwallet(request: Request):
async with db.connect() as conn:
account = await create_account(conn=conn)
user = await get_user(account.id, conn=conn)
+ assert user is not None
wallet = await create_wallet(user_id=user.id, conn=conn)
asyncio.create_task(
diff --git a/lnbits/helpers.py b/lnbits/helpers.py
index 71b3dd691..e97fc7bbb 100644
--- a/lnbits/helpers.py
+++ b/lnbits/helpers.py
@@ -34,7 +34,7 @@ class ExtensionManager:
@property
def extensions(self) -> List[Extension]:
- output = []
+ output: List[Extension] = []
if "all" in self._disabled:
return output
diff --git a/lnbits/tasks.py b/lnbits/tasks.py
index 86863f98f..f4d0a928d 100644
--- a/lnbits/tasks.py
+++ b/lnbits/tasks.py
@@ -66,7 +66,7 @@ async def webhook_handler():
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
-internal_invoice_queue = asyncio.Queue(0)
+internal_invoice_queue: asyncio.Queue = asyncio.Queue(0)
async def internal_invoice_listener():
diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py
index 0432b364a..fbb4add22 100644
--- a/lnbits/utils/exchange_rates.py
+++ b/lnbits/utils/exchange_rates.py
@@ -1,5 +1,5 @@
import asyncio
-from typing import Callable, NamedTuple
+from typing import Callable, NamedTuple, List
import httpx
from loguru import logger
@@ -227,10 +227,10 @@ async def btc_price(currency: str) -> float:
"TO": currency.upper(),
"to": currency.lower(),
}
- rates = []
- tasks = []
+ rates: List[float] = []
+ tasks: List[asyncio.Task] = []
- send_channel = asyncio.Queue()
+ send_channel: asyncio.Queue = asyncio.Queue()
async def controller():
failures = 0
diff --git a/lnbits/wallets/lnpay.py b/lnbits/wallets/lnpay.py
index 807d72538..2ff1afa93 100644
--- a/lnbits/wallets/lnpay.py
+++ b/lnbits/wallets/lnpay.py
@@ -119,7 +119,7 @@ class LNPayWallet(Wallet):
return PaymentStatus(statuses[r.json()["settled"]])
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
- self.queue = asyncio.Queue(0)
+ self.queue: asyncio.Queue = asyncio.Queue(0)
while True:
value = await self.queue.get()
yield value
diff --git a/lnbits/wallets/opennode.py b/lnbits/wallets/opennode.py
index 6d3fb02c9..9cd05ebdd 100644
--- a/lnbits/wallets/opennode.py
+++ b/lnbits/wallets/opennode.py
@@ -127,7 +127,7 @@ class OpenNodeWallet(Wallet):
return PaymentStatus(statuses[r.json()["data"]["status"]])
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
- self.queue = asyncio.Queue(0)
+ self.queue: asyncio.Queue = asyncio.Queue(0)
while True:
value = await self.queue.get()
yield value
From 8e9b3190e4d1ff49131e0f07b4d6ffe811c99221 Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 19 Jul 2022 19:20:20 +0200
Subject: [PATCH 34/84] mypy fakewallet, macaroon, bolt11 invoice
---
lnbits/bolt11.py | 1 +
lnbits/wallets/fake.py | 10 +++++++---
lnbits/wallets/macaroon/macaroon.py | 2 +-
3 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/lnbits/bolt11.py b/lnbits/bolt11.py
index cc8415852..cc8c60401 100644
--- a/lnbits/bolt11.py
+++ b/lnbits/bolt11.py
@@ -23,6 +23,7 @@ class Route(NamedTuple):
class Invoice(object):
payment_hash: str
+ checking_id: Optional[str] = None
amount_msat: int = 0
description: Optional[str] = None
description_hash: Optional[str] = None
diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py
index 3859d33c5..c2b2307ab 100644
--- a/lnbits/wallets/fake.py
+++ b/lnbits/wallets/fake.py
@@ -28,7 +28,7 @@ class FakeWallet(Wallet):
logger.info(
"FakeWallet funding source is for using LNbits as a centralised, stand-alone payment system with brrrrrr."
)
- return StatusResponse(None, float("inf"))
+ return StatusResponse(None, 1000000000)
async def create_invoice(
self,
@@ -80,8 +80,12 @@ class FakeWallet(Wallet):
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
invoice = decode(bolt11)
+ # TODO: no data here?
+ data: Dict = {
+ "privkey": "missing"
+ }
if (
- hasattr(invoice, "checking_id")
+ invoice.checking_id is not None
and invoice.checking_id[6:] == data["privkey"][:6]
):
return PaymentResponse(True, invoice.payment_hash, 0)
@@ -97,7 +101,7 @@ class FakeWallet(Wallet):
return PaymentStatus(None)
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
- self.queue = asyncio.Queue(0)
+ self.queue: asyncio.Queue = asyncio.Queue(0)
while True:
value = await self.queue.get()
yield value
diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py
index 2183dacb8..aa00cd3e6 100644
--- a/lnbits/wallets/macaroon/macaroon.py
+++ b/lnbits/wallets/macaroon/macaroon.py
@@ -73,7 +73,7 @@ class AESCipher(object):
final_key += key
return final_key[:output]
- def decrypt(self, encrypted: str) -> str:
+ def decrypt(self, encrypted) -> str:
"""Decrypts a string using AES-256-CBC."""
passphrase = self.passphrase
encrypted = base64.b64decode(encrypted)
From 7ad9ad799e782acb7f60695ea4767e095c36ebb8 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 08:43:10 +0200
Subject: [PATCH 35/84] assert Optional[str]
---
lnbits/core/tasks.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py
index 5fea769d4..8234b5fc4 100644
--- a/lnbits/core/tasks.py
+++ b/lnbits/core/tasks.py
@@ -55,6 +55,7 @@ async def dispatch_webhook(payment: Payment):
data = payment.dict()
try:
logger.debug("sending webhook", payment.webhook)
+ assert payment.webhook is not None
r = await client.post(payment.webhook, json=data, timeout=40)
await mark_webhook_sent(payment, r.status_code)
except (httpx.ConnectError, httpx.RequestError):
From 56560fca02c335534d0974b133814c4811ae88d8 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 09:36:13 +0200
Subject: [PATCH 36/84] mypy fixes for generic.py, decurators.py, eclair.py
---
lnbits/core/views/generic.py | 16 +++++++--------
lnbits/decorators.py | 40 ++++++++++++++++++++----------------
lnbits/wallets/eclair.py | 4 +++-
3 files changed, 33 insertions(+), 27 deletions(-)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 7ef306dc8..5f44131fc 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -55,9 +55,9 @@ async def home(request: Request, lightning: str = None):
)
async def extensions(
request: Request,
- user: User = Depends(check_user_exists),
- enable: str = Query(None),
- disable: str = Query(None),
+ user = Depends(check_user_exists),
+ enable = Query(None),
+ disable = Query(None),
):
extension_to_enable = enable
extension_to_disable = disable
@@ -109,10 +109,10 @@ nothing: create everything
""",
)
async def wallet(
- request: Request = Query(None),
- nme: Optional[str] = Query(None),
- usr: Optional[UUID4] = Query(None),
- wal: Optional[UUID4] = Query(None),
+ request = Query(None),
+ nme = Query(None),
+ usr = Query(None),
+ wal = Query(None),
):
user_id = usr.hex if usr else None
wallet_id = wal.hex if wal else None
@@ -218,7 +218,7 @@ async def lnurl_full_withdraw_callback(request: Request):
@core_html_routes.get("/deletewallet", response_class=RedirectResponse)
-async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)):
+async def deletewallet(request: Request, wal = Query(...), usr = Query(...)):
user = await get_user(usr)
assert user is not None
user_wallet_ids = [u.id for u in user.wallets]
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index e65b9041a..77fe32276 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -1,5 +1,7 @@
from http import HTTPStatus
+from typing import Optional
+
from cerberus import Validator # type: ignore
from fastapi import status
from fastapi.exceptions import HTTPException
@@ -29,20 +31,20 @@ class KeyChecker(SecurityBase):
self._key_type = "invoice"
self._api_key = api_key
if api_key:
- self.model: APIKey = APIKey(
+ key = APIKey(
**{"in": APIKeyIn.query},
name="X-API-KEY",
description="Wallet API Key - QUERY",
)
else:
- self.model: APIKey = APIKey(
+ key = APIKey(
**{"in": APIKeyIn.header},
name="X-API-KEY",
description="Wallet API Key - HEADER",
)
- self.wallet = None
+ self.model: APIKey = key
- async def __call__(self, request: Request) -> Wallet:
+ async def __call__(self, request: Request):
try:
key_value = (
self._api_key
@@ -52,12 +54,13 @@ class KeyChecker(SecurityBase):
# FIXME: Find another way to validate the key. A fetch from DB should be avoided here.
# Also, we should not return the wallet here - thats silly.
# Possibly store it in a Redis DB
- self.wallet = await get_wallet_for_key(key_value, self._key_type)
- if not self.wallet:
+ wallet = await get_wallet_for_key(key_value, self._key_type)
+ if not wallet:
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED,
detail="Invalid key or expired key.",
)
+ self.wallet = wallet
except KeyError:
raise HTTPException(
@@ -120,8 +123,8 @@ api_key_query = APIKeyQuery(
async def get_key_type(
r: Request,
- api_key_header: str = Security(api_key_header),
- api_key_query: str = Security(api_key_query),
+ api_key_header = Security(api_key_header),
+ api_key_query = Security(api_key_query),
) -> WalletTypeInfo:
# 0: admin
# 1: invoice
@@ -134,9 +137,10 @@ async def get_key_type(
token = api_key_header if api_key_header else api_key_query
try:
- checker = WalletAdminKeyChecker(api_key=token)
- await checker.__call__(r)
- wallet = WalletTypeInfo(0, checker.wallet)
+ admin_checker = WalletAdminKeyChecker(api_key=token)
+ await admin_checker.__call__(r)
+ wallet = WalletTypeInfo(0, admin_checker.wallet)
+ assert wallet.wallet is not None
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
):
@@ -153,9 +157,9 @@ async def get_key_type(
raise
try:
- checker = WalletInvoiceKeyChecker(api_key=token)
- await checker.__call__(r)
- wallet = WalletTypeInfo(1, checker.wallet)
+ invoice_checker = WalletInvoiceKeyChecker(api_key=token)
+ await invoice_checker.__call__(r)
+ wallet = WalletTypeInfo(1, invoice_checker.wallet)
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
):
@@ -174,8 +178,8 @@ async def get_key_type(
async def require_admin_key(
r: Request,
- api_key_header: str = Security(api_key_header),
- api_key_query: str = Security(api_key_query),
+ api_key_header = Security(api_key_header),
+ api_key_query = Security(api_key_query),
):
token = api_key_header if api_key_header else api_key_query
@@ -193,8 +197,8 @@ async def require_admin_key(
async def require_invoice_key(
r: Request,
- api_key_header: str = Security(api_key_header),
- api_key_query: str = Security(api_key_query),
+ api_key_header = Security(api_key_header),
+ api_key_query = Security(api_key_query),
):
token = api_key_header if api_key_header else api_key_query
diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py
index 0ac3fd2a0..bad707ff1 100644
--- a/lnbits/wallets/eclair.py
+++ b/lnbits/wallets/eclair.py
@@ -7,7 +7,9 @@ from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
-from websockets import connect
+
+# mypy https://github.com/aaugustin/websockets/issues/940
+from websockets.client import connect
from websockets.exceptions import (
ConnectionClosed,
ConnectionClosedError,
From 704b572349bf08c2fa07de7fc2daae753ee33c4d Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 09:45:08 +0200
Subject: [PATCH 37/84] fix PaymentKwargs class for mypy
---
lnbits/core/services.py | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/lnbits/core/services.py b/lnbits/core/services.py
index 0b565ebb9..f4f962504 100644
--- a/lnbits/core/services.py
+++ b/lnbits/core/services.py
@@ -109,18 +109,15 @@ async def pay_invoice(
raise ValueError("Amount in invoice is too high.")
# put all parameters that don't change here
- PaymentKwargs = TypedDict(
- "PaymentKwargs",
- {
- "wallet_id": str,
- "payment_request": str,
- "payment_hash": str,
- "amount": int,
- "memo": str,
- "extra": Optional[Dict],
- },
- )
- payment_kwargs: PaymentKwargs = dict(
+ class PaymentKwargs(TypedDict):
+ wallet_id: str
+ payment_request: str
+ payment_hash: str
+ amount: int
+ memo: str
+ extra: Optional[Dict]
+
+ payment_kwargs: PaymentKwargs = PaymentKwargs(
wallet_id=wallet_id,
payment_request=payment_request,
payment_hash=invoice.payment_hash,
From 25d19244be6a0eb05f25ada658b63a15a34663df Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 10:05:30 +0200
Subject: [PATCH 38/84] mypy fixes, api, eclair
---
lnbits/core/views/api.py | 4 ++--
lnbits/wallets/eclair.py | 3 ++-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index bd15ee8d6..09eb4f906 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -181,7 +181,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
lnurl_response: Union[None, bool, str] = None
if data.lnurl_callback:
- if "lnurl_balance_check" in data:
+ if hasattr(data, "lnurl_balance_check"):
assert (
data.lnurl_balance_check is not None
), "lnurl_balance_check is required"
@@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet):
)
async def api_payments_create(
wallet: WalletTypeInfo = Depends(require_invoice_key),
- invoiceData: CreateInvoiceData = Body(...),
+ invoiceData = Body(...),
):
if invoiceData.out is True and wallet.wallet_type == 0:
if not invoiceData.bolt11:
diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py
index bad707ff1..b669f8b74 100644
--- a/lnbits/wallets/eclair.py
+++ b/lnbits/wallets/eclair.py
@@ -9,7 +9,8 @@ import httpx
from loguru import logger
# mypy https://github.com/aaugustin/websockets/issues/940
-from websockets.client import connect
+# from websockets.client import connect
+from websockets import connect
from websockets.exceptions import (
ConnectionClosed,
ConnectionClosedError,
From f677777f07b2439605230bbb7ac0952f8546c4e9 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 10:24:40 +0200
Subject: [PATCH 39/84] fully fix decorators, thanks calle
---
lnbits/decorators.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index 77fe32276..b74230ef8 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -171,9 +171,10 @@ async def get_key_type(
if e.status_code == HTTPStatus.BAD_REQUEST:
raise
if e.status_code == HTTPStatus.UNAUTHORIZED:
- return WalletTypeInfo(2, None)
+ return WalletTypeInfo(2, Wallet())
except:
raise
+ return wallet
async def require_admin_key(
From 7f2c25336ca65c0835c387d2aefaf9153be4dc55 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 10:34:01 +0200
Subject: [PATCH 40/84] ignore eclair websockets type
---
lnbits/wallets/eclair.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py
index b669f8b74..b43d90c10 100644
--- a/lnbits/wallets/eclair.py
+++ b/lnbits/wallets/eclair.py
@@ -8,9 +8,10 @@ from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
+from websockets import connect # type: ignore
+# TODO: https://github.com/lnbits/lnbits-legend/issues/764
# mypy https://github.com/aaugustin/websockets/issues/940
-# from websockets.client import connect
-from websockets import connect
+
from websockets.exceptions import (
ConnectionClosed,
ConnectionClosedError,
From 9a17e4896be598a6f0294e6c54b37ba0fd7290b5 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 11:21:38 +0200
Subject: [PATCH 41/84] fix last views api bug, thanks calle
---
lnbits/core/views/api.py | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 09eb4f906..6766ad8d1 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -387,19 +387,13 @@ async def api_payments_sse(
async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)):
# We use X_Api_Key here because we want this call to work with and without keys
# If a valid key is given, we also return the field "details", otherwise not
- wallet = None
- try:
- assert X_Api_Key is not None
- # TODO: type above is Optional[str] how can that have .extra?
- if X_Api_Key.extra:
- logger.warning("No key")
- except:
- if X_Api_Key is not None:
- wallet = await get_wallet_for_key(X_Api_Key)
+ wallet = await get_wallet_for_key(X_Api_Key) if type(X_Api_Key) == str else None
+
+ # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order
+ # and get_standalone_payment otherwise just fetches the first one, causing unpredictable results
payment = await get_standalone_payment(
payment_hash, wallet_id=wallet.id if wallet else None
- ) # we have to specify the wallet id here, because postgres and sqlite return internal payments in different order
- # and get_standalone_payment otherwise just fetches the first one, causing unpredictable results
+ )
if payment is None:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Payment does not exist."
From a80fd5de7aff9f8387021bf3b414569cb510b874 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 11:33:37 +0200
Subject: [PATCH 42/84] black formating
---
lnbits/core/views/api.py | 2 +-
lnbits/core/views/generic.py | 16 ++++++++--------
lnbits/decorators.py | 12 ++++++------
lnbits/wallets/eclair.py | 3 ++-
lnbits/wallets/fake.py | 4 +---
5 files changed, 18 insertions(+), 19 deletions(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 6766ad8d1..95e9dde3a 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet):
)
async def api_payments_create(
wallet: WalletTypeInfo = Depends(require_invoice_key),
- invoiceData = Body(...),
+ invoiceData=Body(...),
):
if invoiceData.out is True and wallet.wallet_type == 0:
if not invoiceData.bolt11:
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 5f44131fc..01bc47b66 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -55,9 +55,9 @@ async def home(request: Request, lightning: str = None):
)
async def extensions(
request: Request,
- user = Depends(check_user_exists),
- enable = Query(None),
- disable = Query(None),
+ user=Depends(check_user_exists),
+ enable=Query(None),
+ disable=Query(None),
):
extension_to_enable = enable
extension_to_disable = disable
@@ -109,10 +109,10 @@ nothing: create everything
""",
)
async def wallet(
- request = Query(None),
- nme = Query(None),
- usr = Query(None),
- wal = Query(None),
+ request=Query(None),
+ nme=Query(None),
+ usr=Query(None),
+ wal=Query(None),
):
user_id = usr.hex if usr else None
wallet_id = wal.hex if wal else None
@@ -218,7 +218,7 @@ async def lnurl_full_withdraw_callback(request: Request):
@core_html_routes.get("/deletewallet", response_class=RedirectResponse)
-async def deletewallet(request: Request, wal = Query(...), usr = Query(...)):
+async def deletewallet(request: Request, wal=Query(...), usr=Query(...)):
user = await get_user(usr)
assert user is not None
user_wallet_ids = [u.id for u in user.wallets]
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index b74230ef8..ef689f8d0 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -123,8 +123,8 @@ api_key_query = APIKeyQuery(
async def get_key_type(
r: Request,
- api_key_header = Security(api_key_header),
- api_key_query = Security(api_key_query),
+ api_key_header=Security(api_key_header),
+ api_key_query=Security(api_key_query),
) -> WalletTypeInfo:
# 0: admin
# 1: invoice
@@ -179,8 +179,8 @@ async def get_key_type(
async def require_admin_key(
r: Request,
- api_key_header = Security(api_key_header),
- api_key_query = Security(api_key_query),
+ api_key_header=Security(api_key_header),
+ api_key_query=Security(api_key_query),
):
token = api_key_header if api_key_header else api_key_query
@@ -198,8 +198,8 @@ async def require_admin_key(
async def require_invoice_key(
r: Request,
- api_key_header = Security(api_key_header),
- api_key_query = Security(api_key_query),
+ api_key_header=Security(api_key_header),
+ api_key_query=Security(api_key_query),
):
token = api_key_header if api_key_header else api_key_query
diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py
index b43d90c10..122895178 100644
--- a/lnbits/wallets/eclair.py
+++ b/lnbits/wallets/eclair.py
@@ -8,7 +8,8 @@ from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
-from websockets import connect # type: ignore
+from websockets import connect # type: ignore
+
# TODO: https://github.com/lnbits/lnbits-legend/issues/764
# mypy https://github.com/aaugustin/websockets/issues/940
diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py
index c2b2307ab..ba2956c58 100644
--- a/lnbits/wallets/fake.py
+++ b/lnbits/wallets/fake.py
@@ -81,9 +81,7 @@ class FakeWallet(Wallet):
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
invoice = decode(bolt11)
# TODO: no data here?
- data: Dict = {
- "privkey": "missing"
- }
+ data: Dict = {"privkey": "missing"}
if (
invoice.checking_id is not None
and invoice.checking_id[6:] == data["privkey"][:6]
From b9373f72834065068fc41544037572b2002b547a Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 11:52:49 +0200
Subject: [PATCH 43/84] add tests for api_payment
---
tests/core/views/test_api.py | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py
index 6a5f82ecb..949d20527 100644
--- a/tests/core/views/test_api.py
+++ b/tests/core/views/test_api.py
@@ -155,3 +155,27 @@ async def test_decode_invoice(client, invoice):
)
assert response.status_code < 300
assert response.json()["payment_hash"] == invoice["payment_hash"]
+
+
+# check api_payment() internal function call (NOT API): payment status
+@pytest.mark.asyncio
+async def test_api_payment_without_key(invoice):
+ # check the payment status
+ response = await api_payment(invoice["payment_hash"])
+ assert type(response) == dict
+ assert response["paid"] == True
+ # not key, that's why no "details"
+ assert "details" not in response
+
+
+# check api_payment() internal function call (NOT API): payment status
+@pytest.mark.asyncio
+async def test_api_payment_with_key(invoice, inkey_headers_from):
+ # check the payment status
+ response = await api_payment(
+ invoice["payment_hash"], inkey_headers_from["X-Api-Key"]
+ )
+ assert type(response) == dict
+ assert response["paid"] == True
+ # not key, that's why no "details"
+ assert "details" in response
From a0707f8a32df9146d38fad8f97ce37fa3abd673e Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 12:00:53 +0200
Subject: [PATCH 44/84] types-mock==4.0.15 to requirements, needed for mypy
tests
---
requirements.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/requirements.txt b/requirements.txt
index 512011ec2..edcd737d6 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -44,6 +44,7 @@ sqlalchemy==1.3.23
sqlalchemy-aio==0.16.0
sse-starlette==0.6.2
starlette==0.14.2
+types-mock==4.0.15
types-protobuf==3.19.22
typing-extensions==3.10.0.2
uvicorn==0.15.0
From 88ee5fd6cc8c281526b65a640f5370fd9a0b98d6 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 12:05:35 +0200
Subject: [PATCH 45/84] fixup requirements.txt and ad to Pipfile
---
Pipfile | 12 +++++++-----
requirements.txt | 2 --
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/Pipfile b/Pipfile
index f8c42a9dd..a0cef805a 100644
--- a/Pipfile
+++ b/Pipfile
@@ -36,9 +36,11 @@ pycryptodomex = "*"
[dev-packages]
black = "==20.8b1"
-pytest = "*"
-pytest-cov = "*"
-mypy = "*"
-pytest-asyncio = "*"
-requests = "*"
mock = "*"
+mypy = "*"
+pytest = "*"
+pytest-asyncio = "*"
+pytest-cov = "*"
+requests = "*"
+types-mock = "*"
+types-protobuf = "*"
diff --git a/requirements.txt b/requirements.txt
index edcd737d6..f8ccf47cb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -44,8 +44,6 @@ sqlalchemy==1.3.23
sqlalchemy-aio==0.16.0
sse-starlette==0.6.2
starlette==0.14.2
-types-mock==4.0.15
-types-protobuf==3.19.22
typing-extensions==3.10.0.2
uvicorn==0.15.0
uvloop==0.16.0
From 463fbb39bd070d54615bbe43b392255383261a89 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 12:17:34 +0200
Subject: [PATCH 46/84] make test run again, introduce mypy again
---
lnbits/core/views/api.py | 2 +-
tests/core/views/test_api.py | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 95e9dde3a..c81f623a7 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -248,7 +248,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet):
)
async def api_payments_create(
wallet: WalletTypeInfo = Depends(require_invoice_key),
- invoiceData=Body(...),
+ invoiceData: CreateInvoiceData = Body(...),
):
if invoiceData.out is True and wallet.wallet_type == 0:
if not invoiceData.bolt11:
diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py
index 949d20527..7c1b134d5 100644
--- a/tests/core/views/test_api.py
+++ b/tests/core/views/test_api.py
@@ -1,6 +1,7 @@
import pytest
import pytest_asyncio
from lnbits.core.crud import get_wallet
+from lnbits.core.views.api import api_payment
from ...helpers import get_random_invoice_data
From 0e393111deb6f12c0ca8fb0d04122813cf4b915a Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 12:21:39 +0200
Subject: [PATCH 47/84] fix up decorators for tests
---
lnbits/decorators.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index ef689f8d0..adfbd6649 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -1,6 +1,6 @@
from http import HTTPStatus
-from typing import Optional
+from typing import Union
from cerberus import Validator # type: ignore
from fastapi import status
@@ -102,9 +102,9 @@ class WalletAdminKeyChecker(KeyChecker):
class WalletTypeInfo:
wallet_type: int
- wallet: Wallet
+ wallet: Union[Wallet, None]
- def __init__(self, wallet_type: int, wallet: Wallet) -> None:
+ def __init__(self, wallet_type: int, wallet: Union[Wallet, None]) -> None:
self.wallet_type = wallet_type
self.wallet = wallet
@@ -171,7 +171,7 @@ async def get_key_type(
if e.status_code == HTTPStatus.BAD_REQUEST:
raise
if e.status_code == HTTPStatus.UNAUTHORIZED:
- return WalletTypeInfo(2, Wallet())
+ return WalletTypeInfo(2, None)
except:
raise
return wallet
From 4dfa0873e5c36fd61019820efe1c138b1923396f Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 13:12:55 +0200
Subject: [PATCH 48/84] fix isort check
---
lnbits/core/views/api.py | 2 +-
lnbits/decorators.py | 1 -
lnbits/utils/exchange_rates.py | 2 +-
lnbits/wallets/eclair.py | 4 +---
4 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index c81f623a7..d7be3e554 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -3,7 +3,7 @@ import hashlib
import json
from binascii import unhexlify
from http import HTTPStatus
-from typing import Dict, List, Optional, Union, Tuple
+from typing import Dict, List, Optional, Tuple, Union
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
import httpx
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index adfbd6649..b62e456af 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -1,5 +1,4 @@
from http import HTTPStatus
-
from typing import Union
from cerberus import Validator # type: ignore
diff --git a/lnbits/utils/exchange_rates.py b/lnbits/utils/exchange_rates.py
index fbb4add22..2801146b5 100644
--- a/lnbits/utils/exchange_rates.py
+++ b/lnbits/utils/exchange_rates.py
@@ -1,5 +1,5 @@
import asyncio
-from typing import Callable, NamedTuple, List
+from typing import Callable, List, NamedTuple
import httpx
from loguru import logger
diff --git a/lnbits/wallets/eclair.py b/lnbits/wallets/eclair.py
index 122895178..1ba81385e 100644
--- a/lnbits/wallets/eclair.py
+++ b/lnbits/wallets/eclair.py
@@ -8,11 +8,9 @@ from typing import AsyncGenerator, Dict, Optional
import httpx
from loguru import logger
-from websockets import connect # type: ignore
-
# TODO: https://github.com/lnbits/lnbits-legend/issues/764
# mypy https://github.com/aaugustin/websockets/issues/940
-
+from websockets import connect # type: ignore
from websockets.exceptions import (
ConnectionClosed,
ConnectionClosedError,
From a3e4f175dd61e6ee22f7f05aaff828a93dc9572d Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 13:21:42 +0200
Subject: [PATCH 49/84] add missing Pipfile.lock
---
Pipfile.lock | 224 +++++++++++++++++++++++++++++++++------------------
1 file changed, 145 insertions(+), 79 deletions(-)
diff --git a/Pipfile.lock b/Pipfile.lock
index 42d471c6f..2704d4156 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "503e9942306106e40621c59f37a3ab866b483f8c5f27b879c1c6783dca30949f"
+ "sha256": "08e53fc6f1fcc021b33f9b2c5ae3bd6dbde815d4b317e9341ab02cf5b625acbc"
},
"pipfile-spec": 6,
"requires": {
@@ -29,6 +29,7 @@
"sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b",
"sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"
],
+ "markers": "python_full_version >= '3.6.2'",
"version": "==3.6.1"
},
"asyncio": {
@@ -46,6 +47,7 @@
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
"sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.4.0"
},
"bech32": {
@@ -53,6 +55,7 @@
"sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899",
"sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981"
],
+ "markers": "python_version >= '3.5'",
"version": "==1.2.0"
},
"bitstring": {
@@ -76,6 +79,7 @@
"sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d",
"sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"
],
+ "markers": "python_version >= '3.6'",
"version": "==2022.6.15"
},
"cffi": {
@@ -139,6 +143,7 @@
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
],
+ "markers": "python_version >= '3.7'",
"version": "==8.1.3"
},
"ecdsa": {
@@ -177,6 +182,7 @@
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
],
+ "markers": "python_version >= '3.6'",
"version": "==0.12.0"
},
"httpcore": {
@@ -184,6 +190,7 @@
"sha256:1105b8b73c025f23ff7c36468e4432226cbb959176eab66864b8e31c4ee27fa6",
"sha256:18b68ab86a3ccf3e7dc0f43598eaddcf472b602aba29f9aa6ab85fe2ada3980b"
],
+ "markers": "python_version >= '3.7'",
"version": "==0.15.0"
},
"httptools": {
@@ -238,6 +245,7 @@
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
],
+ "markers": "python_version >= '3.5'",
"version": "==3.3"
},
"jinja2": {
@@ -307,6 +315,7 @@
"sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
"sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
],
+ "markers": "python_version >= '3.7'",
"version": "==2.1.1"
},
"marshmallow": {
@@ -314,6 +323,7 @@
"sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb",
"sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7"
],
+ "markers": "python_version >= '3.7'",
"version": "==3.17.0"
},
"outcome": {
@@ -321,6 +331,7 @@
"sha256:6f82bd3de45da303cf1f771ecafa1633750a358436a8bb60e06a1ceb745d2672",
"sha256:c4ab89a56575d6d38a05aa16daeaa333109c1f96167aba8901ab18b6b5e0f7f5"
],
+ "markers": "python_version >= '3.7'",
"version": "==1.2.0"
},
"packaging": {
@@ -328,6 +339,7 @@
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
"sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
],
+ "markers": "python_version >= '3.6'",
"version": "==21.3"
},
"psycopg2-binary": {
@@ -473,6 +485,7 @@
"sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e",
"sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6"
],
+ "markers": "python_full_version >= '3.6.1'",
"version": "==1.9.1"
},
"pyngrok": {
@@ -487,6 +500,7 @@
"sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
"sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"
],
+ "markers": "python_full_version >= '3.6.8'",
"version": "==3.0.9"
},
"pypng": {
@@ -562,9 +576,13 @@
"sha256:026c0de2ee8385d1255b9c2426cd4f03fe9177ac94c09979bc601946c8493aa0",
"sha256:99142650756ef1998ce0661568f54a47dac8c638fb27e3816c02536575dbba8c"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.6.0.post0"
},
"rfc3986": {
+ "extras": [
+ "idna2008"
+ ],
"hashes": [
"sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835",
"sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"
@@ -600,6 +618,14 @@
"index": "pypi",
"version": "==0.14.0"
},
+ "setuptools": {
+ "hashes": [
+ "sha256:0d33c374d41c7863419fc8f6c10bfe25b7b498aa34164d135c622e52580c6b16",
+ "sha256:c04b44a57a6265fe34a4a444e965884716d34bae963119a76353434d6f18e450"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==63.2.0"
+ },
"shortuuid": {
"hashes": [
"sha256:459f12fa1acc34ff213b1371467c0325169645a31ed989e268872339af7563d5",
@@ -613,6 +639,7 @@
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'",
"version": "==1.16.0"
},
"sniffio": {
@@ -620,6 +647,7 @@
"sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663",
"sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"
],
+ "markers": "python_version >= '3.5'",
"version": "==1.2.0"
},
"sqlalchemy": {
@@ -676,17 +704,18 @@
},
"sse-starlette": {
"hashes": [
- "sha256:840607fed361360cc76f8408a25f0eca887e7cab3c3ee44f9762f179428e2bd4",
- "sha256:ca2de945af80b83f1efda6144df9e13db83880b3b87c660044b64f199395e8b7"
+ "sha256:14608559d40e3e7c6385e8c5a7b88468f7fc40c2277673a1fe8d26568e8d7c65",
+ "sha256:72438ed39b1612d1ea6d89a7c0af8afee6de0389dcbe2e77539001e78b5aa89c"
],
"index": "pypi",
- "version": "==0.10.3"
+ "version": "==1.0.0"
},
"starlette": {
"hashes": [
"sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf",
"sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"
],
+ "markers": "python_version >= '3.6'",
"version": "==0.19.1"
},
"typing-extensions": {
@@ -698,6 +727,9 @@
"version": "==4.3.0"
},
"uvicorn": {
+ "extras": [
+ "standard"
+ ],
"hashes": [
"sha256:c19a057deb1c5bb060946e2e5c262fc01590c6529c0af2c3d9ce941e89bc30e0",
"sha256:cade07c403c397f9fe275492a48c1b869efd175d5d8a692df649e6e7e2ed8f4e"
@@ -815,6 +847,7 @@
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
"sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.4.0"
},
"black": {
@@ -829,6 +862,7 @@
"sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d",
"sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"
],
+ "markers": "python_version >= '3.6'",
"version": "==2022.6.15"
},
"charset-normalizer": {
@@ -836,6 +870,7 @@
"sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5",
"sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"
],
+ "markers": "python_full_version >= '3.6.0'",
"version": "==2.1.0"
},
"click": {
@@ -843,9 +878,13 @@
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
],
+ "markers": "python_version >= '3.7'",
"version": "==8.1.3"
},
"coverage": {
+ "extras": [
+ "toml"
+ ],
"hashes": [
"sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32",
"sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7",
@@ -889,6 +928,7 @@
"sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39",
"sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"
],
+ "markers": "python_version >= '3.7'",
"version": "==6.4.2"
},
"idna": {
@@ -896,6 +936,7 @@
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
],
+ "markers": "python_version >= '3.5'",
"version": "==3.3"
},
"iniconfig": {
@@ -954,6 +995,7 @@
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
"sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
],
+ "markers": "python_version >= '3.6'",
"version": "==21.3"
},
"pathspec": {
@@ -968,6 +1010,7 @@
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
],
+ "markers": "python_full_version >= '3.6.0'",
"version": "==1.0.0"
},
"py": {
@@ -975,6 +1018,7 @@
"sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
"sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.11.0"
},
"pyparsing": {
@@ -982,6 +1026,7 @@
"sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
"sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"
],
+ "markers": "python_full_version >= '3.6.8'",
"version": "==3.0.9"
},
"pytest": {
@@ -1010,82 +1055,83 @@
},
"regex": {
"hashes": [
- "sha256:00d2e907d3c5e4f85197c8d2263a9cc2d34bf234a9c6236ae42a3fb0bc09b759",
- "sha256:0186edcda692c38381db8ac257c2d023fd2e08818d45dc5bee4ed84212045f51",
- "sha256:06c509bd7dcb7966bdb03974457d548e54d8327bad5b0c917e87248edc43e2eb",
- "sha256:0a3f3f45c5902eb4d90266002ccb035531ae9b9278f6d5e8028247c34d192099",
- "sha256:0c1821146b429e6fdbd13ea10f26765e48d5284bc79749468cfbfe3ceb929f0d",
- "sha256:0d93167b7d7731fa9ff9fdc1bae84ec9c7133b01a35f8cc04e926d48da6ce1f7",
- "sha256:0fd8c3635fa03ef79d07c7b3ed693b3f3930ccb52c0c51761c3296a7525b135c",
- "sha256:119091c675e6ad19da8770f89aa1d52f4ad2a2018d631956f3e90c45882df880",
- "sha256:121981ba84309dabefd5e1debd49be6d51624e54b4d44bfc184cd8d555ff1df1",
- "sha256:1244e9b9b4b81c9c34e8a84273ffaeebdc78abc98a5b02dcdd49845eb3c63bd7",
- "sha256:12e1404dfb4e928d3273a10e3468877fe84bdcd3c50b655a2c9613cfc5d9fe63",
- "sha256:13d74951c14708f00700bb29475129ecbc40e01b4029c62ee7bfe9d1f59f31ce",
- "sha256:162a5939a6fdf48658d3565eeff35acdd207e07367bf5caaff3d9ea7cb77d7a9",
- "sha256:1703490c5b850fa9cef1af00c58966756042e6ca22f4fb5bb857345cd535834f",
- "sha256:18e6203cfd81df42a987175aaeed7ba46bcb42130cd81763e2d5edcff0006d5d",
- "sha256:192c2784833aea6fc7b004730bf1b91b8b8c6b998b30271aaf3bd8adfef20a96",
- "sha256:1948d3ceac5b2d55bc93159c1e0679a256a87a54c735be5cef4543a9e692dbb9",
- "sha256:206a327e628bc529d64b21ff79a5e2564f5aec7dc7abcd4b2e8a4b271ec10550",
- "sha256:2e5db20412f0db8798ff72473d16da5f13ec808e975b49188badb2462f529fa9",
- "sha256:2f94b0befc811fe74a972b1739fffbf74c0dc1a91102aca8e324aa4f2c6991bd",
- "sha256:303676797c4c7978726e74eb8255d68f7125a3a29da71ff453448f2117290e9a",
- "sha256:34ae4f35db30caa4caf85c55069fcb7a05966a3a5ba6e9e1dab5477d84fbb08a",
- "sha256:3c6df8be7d1dd35a0d9a200fbc29f888c4452c8882d284f87608046152e049e6",
- "sha256:402fa998c5988d11ed34585eb65740dcebd0fd11844d12eb0a6b4be178eb9c64",
- "sha256:40a28759d345c0bb1f5b0ac74ac04f5d48136019522c95c0ec4b07786f67ce20",
- "sha256:414ae507ba88264444baf771fec43ce0adcd4c5dbb304d3e0716f3f4d4499d2e",
- "sha256:42da079e31ae9818ffa7a35cdd16ab7104e3f7eca9c0958040aede827b2e55c6",
- "sha256:473a7d21932ce7c314953b33c32e63df690181860edcdf14bba1278cdf71b07f",
- "sha256:49fcb45931a693b0e901972c5e077ea2cf30ec39da699645c43cb8b1542c6e14",
- "sha256:4c5913cb9769038bd03e42318955c2f15a688384a6a0b807bcfc8271603d9277",
- "sha256:4cfeb71095c8d8380a5df5a38ff94d27a3f483717e509130a822b4d6400b7991",
- "sha256:4dc74f0171eede67d79a79c06eca0fe5b7b280dbb8c27ad1fae4ced2ad66268f",
- "sha256:5b1cffff2d9f832288fe516021cb81c95c57c0067b13a82f1d2daabdbc2f4270",
- "sha256:601c99ac775b6c89699a48976f3dbb000b47d3ca59362c8abc9582e6d0780d91",
- "sha256:667a06bb8d72b6da3d9cf38dac4ba969688868ed2279a692e993d2c0e1c30aba",
- "sha256:673549a0136c7893f567ed71ab5225ed3701c79b17c0a7faee846c645fc24010",
- "sha256:67bd3bdd27db7a6460384869dd4b9c54267d805b67d70b20495bb5767f8e051c",
- "sha256:727edff0a4eaff3b6d26cbb50216feac9055aba7e6290eec23c061c2fe2fab55",
- "sha256:782627a1cb8fbb1c78d8e841f5b71c2c683086c038f975bebdac7cce7678a96f",
- "sha256:7d462ba84655abeddae4dfc517fe1afefb5430b3b5acb0a954de12a47aea7183",
- "sha256:8ab39aa445d00902c43a1e951871bedc7f18d095a21eccba153d594faac34aea",
- "sha256:8e2075ed4ea2e231e2e98b16cfa5dae87e9a6045a71104525e1efc29aa8faa8e",
- "sha256:9daeccb2764bf4cc280c40c6411ae176bb0876948e536590a052b3d647254c95",
- "sha256:9e4006942334fa954ebd32fa0728718ec870f95f4ba7cda9edc46dd49c294f22",
- "sha256:9f1c8fffd4def0b76c0947b8cb261b266e31041785dc2dc2db7569407a2f54fe",
- "sha256:a00cd58a30a1041c193777cb1bc090200b05ff4b073d5935738afd1023e63069",
- "sha256:a0220a7a16fd4bfc700661f920510defd31ef7830ce992d5cc51777aa8ccd724",
- "sha256:a048f91823862270905cb22ef88038b08aac852ce48e0ecc4b4bf1b895ec37d9",
- "sha256:a3c47c71fde0c5d584402e67546c81af9951540f1f622d821e9c20761556473a",
- "sha256:a6d9ea727fd1233ee746bf44dd37e7d4320b3ed8ff09e73d7638c969b28d280f",
- "sha256:ab0709daedc1099bbd4371ae17eeedd4efc1cf70fcdcfe5de1374a0944b61f80",
- "sha256:ab1cb36b411f16da6e057ef8e6657dd0af36f59a667f07e0b4b617e44e53d7b2",
- "sha256:ae1c5b435d44aa91d48cc710f20c3485e0584a3ad3565d5ae031d61a35f674f4",
- "sha256:b279b9bb401af41130fd2a09427105100bc8c624ed45da1c81c1c0d0aa639734",
- "sha256:b72a4ec79a15f6066d14ae1c472b743af4b4ecee14420e8d6e4a336b49b8f21c",
- "sha256:c2cd93725911c0159d597b90c96151070ef7e0e67604637e2f2abe06c34bf079",
- "sha256:c7c5f914b0eb5242c09f91058b80295525897e873b522575ab235b48db125597",
- "sha256:d07d849c9e2eca80adb85d3567302a47195a603ad7b1f0a07508e253c041f954",
- "sha256:d2672d68cf6c8452b6758fc3cd2d8feac966d511eed79a68182a5297b473af9c",
- "sha256:d35bbcbf70d14f724e7489746cf68efe122796578addd98f91428e144d0ad266",
- "sha256:d40b4447784dbe0896a6d10a178f6724598161f942c56f5a60dc0ef7fe63f7a1",
- "sha256:d561dcb0fb0ab858291837d51330696a45fd3ba6912a332a4ee130e5484b9e47",
- "sha256:d7f5ccfff648093152cadf6d886c7bd922047532f72024c953a79c7553aac2fe",
- "sha256:dce6b2ad817e3eb107f8704782b091b0631dd3adf47f14bdc086165d05b528b0",
- "sha256:e1fdda3ec7e9785065b67941693995cab95b54023a21db9bf39e54cc7b2c3526",
- "sha256:e2a262ec85c595fc8e1f3162cafc654d2219125c00ea3a190c173cea70d2cc7a",
- "sha256:e2fc1e3928c1189c0382c547c17717c6d9f425fffe619ef94270fe4c6c8be0a6",
- "sha256:ea27acd97a752cfefa9907da935e583efecb302e6e9866f37565968c8407ad58",
- "sha256:ee769a438827e443ed428e66d0aa7131c653ecd86ddc5d4644a81ed1d93af0e7",
- "sha256:f32e0d1c7e7b0b9c3cac76f3d278e7ee6b99c95672d2c1c6ea625033431837c0",
- "sha256:f355caec5bbce20421dc26e53787b10e32fd0df68db2b795435217210c08d69c",
- "sha256:f87e9108bb532f8a1fc6bf7e69b930a35c7b0267b8fef0a3ede0bcb4c5aaa531",
- "sha256:f8a2fd2f62a77536e4e3193303bec380df40d99e253b1c8f9b6eafa07eaeff67",
- "sha256:fbdf4fc6adf38fab1091c579ece3fe9f493bd0f1cfc3d2c76d2e52461ca4f8a9"
+ "sha256:045682b6457f0224deb10c9720b8008dc798b1ad4331de9302fd4b615211e5a5",
+ "sha256:04e3869fc6fc24b75d38e7547797bb0a82d6cccd49e8ce6ae21a0b87aeb9fac7",
+ "sha256:073bd76a1f03e05a6ca0df705b6117f75b10c340af068b55becb1334fc6426c2",
+ "sha256:0aa48740a1385cb668ffd828a7e2c8078ce29c72d64651c8226bd2b7cb5dba0c",
+ "sha256:0e380ebd841201f980ab022d07033be12c9438a9b2e0f60324c9e3ba31790918",
+ "sha256:0f9b8ef2e46d627b2a85d3f4fe433ead283c420adcf9461906c3db10766dd3b1",
+ "sha256:10d3a5dda21a125cfdc31e45ae6ce6bdd1e45cb194199801248d6580be8dc337",
+ "sha256:11965ae779a0ccfb6d17996d531e2f522124e04d98cc65742b96bf8f50758ab9",
+ "sha256:1c61175730596266015c4e005c65cbfeeb1ef019ccd024870169c6593a26bdb0",
+ "sha256:1d1d79a87a33fbd6573e30eb53969a4190035894c4390a0787fb823e9e86b72f",
+ "sha256:1f0bf228c948f543876f4fb310322a4ff7e398667dd58aeb4815dc9e30bf867e",
+ "sha256:2026a1c108752e48577f9720076bf6e31a60aaf0c3000fffad4e2527fdffbe95",
+ "sha256:204705b7ec16267d39870a19e72e832b12739dc48a26d923a9cb94043660d50f",
+ "sha256:248b16534d1ef8f10a72cda0f97b3dfa25b3d9123a7e726d1594cb07a541bba0",
+ "sha256:27b5011449213dfd880e592ea6d311d00739e87d9512bad507ee18c9c92a20ac",
+ "sha256:2880d21e9507869ab1636e50461afb9ffb08797f1cb76f70d3ad52e7dd13a335",
+ "sha256:2d3f9fc885ecd8b0eb248d0e190aa7264b977cc23b6da7c08444065170c57e2e",
+ "sha256:2d8b50ae3cbaaa2e5ed89ed81fc025ec64b1a54c4f34e6bdaad9dc63fc2afa6b",
+ "sha256:3ff5b2b6a136307a5551e7821d83ab12c46f57c32bf23a27877c9c6bdb55aa61",
+ "sha256:40b4436466d47271fe3b4df63e55307c91a40cda0875a9ff3b7231a08394b283",
+ "sha256:435c94d5939a7cf4b0af1cce30d196451bae441ebe64f63d08517ab490ceb385",
+ "sha256:4c0b7c413d4a8a55d72df18acbeb50276eee19cec7e2f54ed3bddc46bc3b3aeb",
+ "sha256:4cfe87490d0a801749b42491ef7e968342e5787decbf57d5402fc2c17f7302e3",
+ "sha256:4f480661cd0809a1177b09581c12c9ceff9ca989e4a0c8c0f10379dffa3b4c4c",
+ "sha256:53370620db6058dbb464324b053fee8608518d76ff6352b2835d71e2ae8ef293",
+ "sha256:53eca0cece2ddb592b8dd9746f0b258d0c8a45f5a3ac8eb96833058f64778fca",
+ "sha256:5751bfe0d939d7110396510a39e48ee928b36b55177207c47766b093886a3945",
+ "sha256:5d750f99c40a7e994df1cf1295bbb3e873417ca69508664fe9f65db92e46ca40",
+ "sha256:6019737db5c46a24f307eae5069fced0752e3a22380398bbefbef77b068b9537",
+ "sha256:605ca47681c7405723a4970d66d13fa3a3a66efa6b8499d7ab7bce1ddb44a36f",
+ "sha256:69120a8fb1eb932b6e3ededb16448be6444eb952f9350c21dddbef947fea5690",
+ "sha256:72713de336b8d895f91aad34f5591f33d1d8727bd739af3ae2657411ef6e0739",
+ "sha256:74077b462a9b255c5fca247484f46b0f25c32676fe4645cb6b5304b2d997357a",
+ "sha256:74f0067495a842f7cc198b14031a2893d377bed38e19d785f35095082ab5a556",
+ "sha256:817a0618c149d77e493963cd98851ff49d6ab8bcab247fdbf85bb89a14dca5c3",
+ "sha256:869a0f6405ec569863e09909617138af575b5e2bc5181184e60f339a4c8a6d7a",
+ "sha256:86b0cf786efd587c27abd1d07020c555a82275bba3506d916d42aed7a3744967",
+ "sha256:8cbc407c44003a1cb4aaa2d48cc19b45dde07ba0ae2f541c6750ad18f8c118f5",
+ "sha256:90e082f262cf858cbcde330999ea5612e12918982033b716d2c5d8b1bc7a01db",
+ "sha256:95d8f1083ff4546ec14fb46dc41b042d372258f8c319df1e2316d8fe1bd3f085",
+ "sha256:97211bae1bc51f153764485a54d8d1130196cae33d02285c33732b26c5328b8d",
+ "sha256:98dbaf6a86991e2b09f4d8a7669b4304755bda519565971dc3b87ee00fd6eab2",
+ "sha256:9e297ff33172853e9a9e46dbb0c2ebf44fb38ebefce659698df4eb9dfce0a748",
+ "sha256:9e64492c8105312f080e25e457db70f9b0d02e6ba3c1ea14468087b0e3aa876f",
+ "sha256:a11241808e59deec8314792bbd8a6f0a8a7a95b742709e134c73a3216dbb26ae",
+ "sha256:a33c36dcc1760d66f1969d7d3dc8956f45a3d502178053074b8489f67718138f",
+ "sha256:a4e1e7ba8c58c1f0b828418f9a2635c0f6344bf107308b8fe65f234a13c8462a",
+ "sha256:a55c4d5e5076cc5ece625dd1f7015c9a0818ba1f9ad9db421b495d7ece088e56",
+ "sha256:a6f5cc82e1fa380eb6b8040d626df6ba9f492b6886527f53d59838b11e9caaef",
+ "sha256:a96826b5c9dc68417ddb29843998473f9c2c047911e6fca36a9f81a898087b01",
+ "sha256:acf6cbcc19d86f44e8a9d3cf1f6946a71dc55c2ec8ae374c547b1eefd83b954f",
+ "sha256:b4be25d3c640a35671431d8ef8cd522319254074b150147fcacad90c91ea42c2",
+ "sha256:b5a31abc27f9bda7a455bfa1e1bd623da50e3a343a040084c879d07394a93481",
+ "sha256:bb812e590e3881a93d4d291270440b3795fa4c0bc1b03ba15fe1cc88d2ea4347",
+ "sha256:bbd542b4afd2ed5491a480e9b15f4bca13e0170ef1895064fd15741382fdebe0",
+ "sha256:bf27111f56b762238bd3ce4c5e8ad34167d85dd3c077721a0c093517526a94af",
+ "sha256:c7a4dc9436b7a55c36daa3959e92d70337c547651ebeda685dd6bb083f0b77ab",
+ "sha256:c88a1068ac8e5dc579d5104903fa2c488448c1137e580a77d1438d98070c4243",
+ "sha256:c8cffb5040bf432355cfe51378072f20087609694066ace80710bdba04cf9ce2",
+ "sha256:c944921b0e77f1923dd89cda65534223ac107e24d71b1dfe174237faa5efd32a",
+ "sha256:ca2a7233275acf0087ecd15e5fb0eeb722a1f4de453b49bb1443edf2c2f5a997",
+ "sha256:cf0a3b9744f94693f3ebcca1c259354f0043c19a4ce938f80ad6d1816b8fd8f0",
+ "sha256:dbe2f16a66f64a00dc9ccc0db7f8b5ff014f409840e03675eb431f03b50ddffd",
+ "sha256:deb7b067b3b9751c60dc7f6de68476138d550c074a5016ba944cc55863fa86d1",
+ "sha256:df669bacbda209e9b00928f1d00432b27a16c3e051f9f7e5ea306f9b78bf3e7c",
+ "sha256:e2dd4ca82c2241be9582d2ae060070f2bccb0c98295b608009d5cc6e6041eaed",
+ "sha256:e432cf909c53506da4c8308753b2671ee37d2d8d1de8b4b54ab76e91ca7ba0b5",
+ "sha256:e4e7f1aba3aaf08e11d33fd5c2d8dd8cbf573049474e11256c91e3ba3d5e1642",
+ "sha256:e51ab7fbfe5ac3002b9aee527bcb164b17fd92f5663ebf2a4e5917dd9d577864",
+ "sha256:ea00c7f86405d88995e7bab5609e343fdedfe1ffc8191d3b5ed0f8c7f5eb17ec",
+ "sha256:f7006d7c74e25f8bc592604a5a72ba624f10ebd5c0683ab4d3e940a88ac0098c",
+ "sha256:f93e3e5acf82812ea92a1ccdcce690aab18c4044dd824f6b959d2b6069d84312",
+ "sha256:fa8a4bc81b15f49c57ede3fd636786c6619179661acf2430fcc387d75bf28d33",
+ "sha256:fc44c49f33dd75e58b5ff2a5ac50c96c84b6b209d36b4790c85bca08a3b9017d"
],
- "version": "==2022.7.9"
+ "markers": "python_full_version >= '3.6.0'",
+ "version": "==2022.7.24"
},
"requests": {
"hashes": [
@@ -1100,6 +1146,7 @@
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'",
"version": "==0.10.2"
},
"tomli": {
@@ -1107,6 +1154,7 @@
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
],
+ "markers": "python_version >= '3.7'",
"version": "==2.0.1"
},
"typed-ast": {
@@ -1136,8 +1184,25 @@
"sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3",
"sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"
],
+ "markers": "python_full_version >= '3.6.0'",
"version": "==1.5.4"
},
+ "types-mock": {
+ "hashes": [
+ "sha256:4535fbb3912b88a247d43cdb41db0c8b2e187138986f6f01a989717e56105848",
+ "sha256:a849bc2d966063f4946013bf404822ee2b96f77a8dccda4174b70ab61c5293fe"
+ ],
+ "index": "pypi",
+ "version": "==4.0.15"
+ },
+ "types-protobuf": {
+ "hashes": [
+ "sha256:d291388678af91bb045fafa864f142dc4ac22f5d4cdca097c7d8d8a32fa9b3ab",
+ "sha256:d2b26861b0cb46a3c8669b0df507b7ef72e487da66d61f9f3576aa76ce028a83"
+ ],
+ "index": "pypi",
+ "version": "==3.19.22"
+ },
"typing-extensions": {
"hashes": [
"sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02",
@@ -1151,6 +1216,7 @@
"sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec",
"sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
"version": "==1.26.10"
}
}
From e939b9a5aab55bbae8cd9f5c85d919bdf075da6a Mon Sep 17 00:00:00 2001
From: Daniel Krahofer
Date: Wed, 20 Jul 2022 13:15:06 +0200
Subject: [PATCH 50/84] Update tests/core/views/test_api.py
Co-authored-by: calle <93376500+callebtc@users.noreply.github.com>
---
tests/core/views/test_api.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py
index 7c1b134d5..cfbd77104 100644
--- a/tests/core/views/test_api.py
+++ b/tests/core/views/test_api.py
@@ -178,5 +178,4 @@ async def test_api_payment_with_key(invoice, inkey_headers_from):
)
assert type(response) == dict
assert response["paid"] == True
- # not key, that's why no "details"
assert "details" in response
From b865fad95e597c027deceec409568f60fb3e0582 Mon Sep 17 00:00:00 2001
From: Daniel Krahofer
Date: Wed, 20 Jul 2022 13:15:18 +0200
Subject: [PATCH 51/84] Update tests/core/views/test_api.py
Co-authored-by: calle <93376500+callebtc@users.noreply.github.com>
---
tests/core/views/test_api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/core/views/test_api.py b/tests/core/views/test_api.py
index cfbd77104..dfd2b32ae 100644
--- a/tests/core/views/test_api.py
+++ b/tests/core/views/test_api.py
@@ -165,7 +165,7 @@ async def test_api_payment_without_key(invoice):
response = await api_payment(invoice["payment_hash"])
assert type(response) == dict
assert response["paid"] == True
- # not key, that's why no "details"
+ # no key, that's why no "details"
assert "details" not in response
From 66477bc392a5e8594122fdb526e9fe160c6b5488 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 13:23:50 +0200
Subject: [PATCH 52/84] enable mypy checks
---
.github/workflows/mypy.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml
index 4d6c6d4da..bf90a8e31 100644
--- a/.github/workflows/mypy.yml
+++ b/.github/workflows/mypy.yml
@@ -5,7 +5,6 @@ on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
- if: ${{ 'false' == 'true' }} # skip mypy for now
steps:
- uses: actions/checkout@v1
- uses: jpetrucciani/mypy-check@master
From df00a018f0b8f78edfa478a9f21d91cff95204aa Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 20 Jul 2022 13:41:13 +0200
Subject: [PATCH 53/84] added FIXME tags and fix WalletTypeInfo
---
lnbits/core/services.py | 4 ++++
lnbits/core/views/api.py | 23 +++++++++++++++++++++++
lnbits/decorators.py | 2 ++
3 files changed, 29 insertions(+)
diff --git a/lnbits/core/services.py b/lnbits/core/services.py
index f4f962504..2416ed315 100644
--- a/lnbits/core/services.py
+++ b/lnbits/core/services.py
@@ -269,6 +269,10 @@ async def perform_lnurlauth(
cb = urlparse(callback)
k1 = unhexlify(parse_qs(cb.query)["k1"][0])
+
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
+
key = wallet.wallet.lnurlauth_key(cb.netloc)
def int_to_bytes_suitable_der(x: int) -> bytes:
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index d7be3e554..a3a59a5f0 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -56,12 +56,16 @@ from ..tasks import api_invoice_listeners
@core_app.get("/api/v1/wallet")
async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
if wallet.wallet_type == 0:
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
return {
"id": wallet.wallet.id,
"name": wallet.wallet.name,
"balance": wallet.wallet.balance_msat,
}
else:
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
return {"name": wallet.wallet.name, "balance": wallet.wallet.balance_msat}
@@ -69,6 +73,9 @@ async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_update_balance(
amount: int, wallet: WalletTypeInfo = Depends(get_key_type)
):
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
+
if wallet.wallet.user not in LNBITS_ADMIN_USERS:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user"
@@ -98,6 +105,9 @@ async def api_update_balance(
async def api_update_wallet(
new_name: str, wallet: WalletTypeInfo = Depends(require_admin_key)
):
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
+
await update_wallet(wallet.wallet.id, new_name)
return {
"id": wallet.wallet.id,
@@ -112,6 +122,9 @@ async def api_payments(
offset: Optional[int] = None,
wallet: WalletTypeInfo = Depends(get_key_type),
):
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
+
pendingPayments = await get_payments(
wallet_id=wallet.wallet.id,
pending=True,
@@ -256,11 +269,15 @@ async def api_payments_create(
status_code=HTTPStatus.BAD_REQUEST,
detail="BOLT11 string is invalid or not given",
)
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
return await api_payments_pay_invoice(
invoiceData.bolt11, wallet.wallet
) # admin key
elif not invoiceData.out:
# invoice key
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
return await api_payments_create_invoice(invoiceData, wallet.wallet)
else:
raise HTTPException(
@@ -325,6 +342,8 @@ async def api_payments_pay_lnurl(
if data.comment:
extra["comment"] = data.comment
assert data.description is not None, "description is required"
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
payment_hash = await pay_invoice(
wallet_id=wallet.wallet.id,
payment_request=params["pr"],
@@ -378,6 +397,8 @@ async def subscribe(request: Request, wallet: Wallet):
async def api_payments_sse(
request: Request, wallet: WalletTypeInfo = Depends(get_key_type)
):
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
return EventSourceResponse(
subscribe(request, wallet.wallet), ping=20, media_type="text/event-stream"
)
@@ -456,6 +477,8 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
params.update(kind="auth")
params.update(callback=url) # with k1 already in it
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
lnurlauth_key = wallet.wallet.lnurlauth_key(domain)
params.update(pubkey=lnurlauth_key.verifying_key.to_string("compressed").hex())
else:
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index b62e456af..9f51ce646 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -159,6 +159,8 @@ async def get_key_type(
invoice_checker = WalletInvoiceKeyChecker(api_key=token)
await invoice_checker.__call__(r)
wallet = WalletTypeInfo(1, invoice_checker.wallet)
+ # FIXME: wallet.wallet can be None here
+ assert wallet.wallet is not None
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
):
From 9cecd42467dc5bac66b841611b97b574f54749bb Mon Sep 17 00:00:00 2001
From: dni
Date: Mon, 25 Jul 2022 12:09:29 +0200
Subject: [PATCH 54/84] ignore Body type
---
lnbits/core/views/api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index a3a59a5f0..59ce61a9d 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -261,7 +261,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet):
)
async def api_payments_create(
wallet: WalletTypeInfo = Depends(require_invoice_key),
- invoiceData: CreateInvoiceData = Body(...),
+ invoiceData: CreateInvoiceData = Body(...), #type: ignore
):
if invoiceData.out is True and wallet.wallet_type == 0:
if not invoiceData.bolt11:
From a7c10dde4ad7bc88a63f2f5805049fc29d5cf8b1 Mon Sep 17 00:00:00 2001
From: dni
Date: Mon, 25 Jul 2022 13:13:50 +0200
Subject: [PATCH 55/84] fixup types
---
.github/workflows/mypy.yml | 2 +-
lnbits/core/views/generic.py | 13 +++++++------
lnbits/decorators.py | 12 ++++++------
3 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml
index bf90a8e31..71713d88f 100644
--- a/.github/workflows/mypy.yml
+++ b/.github/workflows/mypy.yml
@@ -10,4 +10,4 @@ jobs:
- uses: jpetrucciani/mypy-check@master
with:
mypy_flags: '--install-types --non-interactive'
- path: lnbits
+ path: 'lnbits'
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 01bc47b66..433c6f62e 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -55,9 +55,9 @@ async def home(request: Request, lightning: str = None):
)
async def extensions(
request: Request,
- user=Depends(check_user_exists),
- enable=Query(None),
- disable=Query(None),
+ user: User = Depends(check_user_exists), #type: ignore
+ enable: str = Query(None), #type: ignore
+ disable: str = Query(None), #type: ignore
):
extension_to_enable = enable
extension_to_disable = disable
@@ -108,11 +108,12 @@ just **user_id**: return the first user wallet or create one if none found (with
nothing: create everything
""",
)
+
async def wallet(
request=Query(None),
- nme=Query(None),
- usr=Query(None),
- wal=Query(None),
+ nme: Optional[str] = Query(None), #type: ignore
+ usr: Optional[UUID4] = Query(None), #type: ignore
+ wal: Optional[UUID4] = Query(None), #type: ignore
):
user_id = usr.hex if usr else None
wallet_id = wal.hex if wal else None
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index 9f51ce646..b65c43a1c 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -122,8 +122,8 @@ api_key_query = APIKeyQuery(
async def get_key_type(
r: Request,
- api_key_header=Security(api_key_header),
- api_key_query=Security(api_key_query),
+ api_key_header: str = Security(api_key_header), #type: ignore
+ api_key_query: str = Security(api_key_query) #type: ignore
) -> WalletTypeInfo:
# 0: admin
# 1: invoice
@@ -180,8 +180,8 @@ async def get_key_type(
async def require_admin_key(
r: Request,
- api_key_header=Security(api_key_header),
- api_key_query=Security(api_key_query),
+ api_key_header: str = Security(api_key_header), #type: ignore
+ api_key_query: str = Security(api_key_query) #type: ignore
):
token = api_key_header if api_key_header else api_key_query
@@ -199,8 +199,8 @@ async def require_admin_key(
async def require_invoice_key(
r: Request,
- api_key_header=Security(api_key_header),
- api_key_query=Security(api_key_query),
+ api_key_header: str = Security(api_key_header), #type: ignore
+ api_key_query: str = Security(api_key_query) #type: ignore
):
token = api_key_header if api_key_header else api_key_query
From 0bf4457c2f1289784fd60066233f12c7835e807d Mon Sep 17 00:00:00 2001
From: dni
Date: Mon, 25 Jul 2022 13:19:35 +0200
Subject: [PATCH 56/84] fixup types
---
lnbits/core/views/generic.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 433c6f62e..3058d115f 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -110,7 +110,7 @@ nothing: create everything
)
async def wallet(
- request=Query(None),
+ request: Request = Query(None), #type: ignore
nme: Optional[str] = Query(None), #type: ignore
usr: Optional[UUID4] = Query(None), #type: ignore
wal: Optional[UUID4] = Query(None), #type: ignore
From 3d1abfa35a5f5672f0aa736d0dab742f8cec5b8a Mon Sep 17 00:00:00 2001
From: dni
Date: Mon, 25 Jul 2022 13:27:31 +0200
Subject: [PATCH 57/84] last mypy fails
---
lnbits/core/views/generic.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 3058d115f..0d8a7b0e4 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -88,7 +88,7 @@ async def extensions(
# Update user as his extensions have been updated
if extension_to_enable or extension_to_disable:
- user = await get_user(user.id)
+ user = await get_user(user.id) #type: ignore
return template_renderer().TemplateResponse(
"core/extensions.html", {"request": request, "user": user.dict()}
@@ -152,8 +152,8 @@ async def wallet(
)
logger.debug(f"Access wallet {wallet_name}{'of user '+ user.id if user else ''}")
- wallet = user.get_wallet(wallet_id)
- if not wallet:
+ userwallet = user.get_wallet(wallet_id)
+ if not userwallet:
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": "Wallet not found"}
)
@@ -163,7 +163,7 @@ async def wallet(
{
"request": request,
"user": user.dict(),
- "wallet": wallet.dict(),
+ "wallet": userwallet.dict(),
"service_fee": service_fee,
"web_manifest": f"/manifest/{user.id}.webmanifest",
},
From 5779243d3efcde9b9b4c1d3eb61343474922d073 Mon Sep 17 00:00:00 2001
From: dni
Date: Mon, 25 Jul 2022 13:30:45 +0200
Subject: [PATCH 58/84] blacked
---
lnbits/core/views/api.py | 2 +-
lnbits/core/views/generic.py | 17 ++++++++---------
lnbits/decorators.py | 12 ++++++------
3 files changed, 15 insertions(+), 16 deletions(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 59ce61a9d..97dff480d 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -261,7 +261,7 @@ async def api_payments_pay_invoice(bolt11: str, wallet: Wallet):
)
async def api_payments_create(
wallet: WalletTypeInfo = Depends(require_invoice_key),
- invoiceData: CreateInvoiceData = Body(...), #type: ignore
+ invoiceData: CreateInvoiceData = Body(...), # type: ignore
):
if invoiceData.out is True and wallet.wallet_type == 0:
if not invoiceData.bolt11:
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 0d8a7b0e4..8b9437166 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -55,9 +55,9 @@ async def home(request: Request, lightning: str = None):
)
async def extensions(
request: Request,
- user: User = Depends(check_user_exists), #type: ignore
- enable: str = Query(None), #type: ignore
- disable: str = Query(None), #type: ignore
+ user: User = Depends(check_user_exists), # type: ignore
+ enable: str = Query(None), # type: ignore
+ disable: str = Query(None), # type: ignore
):
extension_to_enable = enable
extension_to_disable = disable
@@ -88,7 +88,7 @@ async def extensions(
# Update user as his extensions have been updated
if extension_to_enable or extension_to_disable:
- user = await get_user(user.id) #type: ignore
+ user = await get_user(user.id) # type: ignore
return template_renderer().TemplateResponse(
"core/extensions.html", {"request": request, "user": user.dict()}
@@ -108,12 +108,11 @@ just **user_id**: return the first user wallet or create one if none found (with
nothing: create everything
""",
)
-
async def wallet(
- request: Request = Query(None), #type: ignore
- nme: Optional[str] = Query(None), #type: ignore
- usr: Optional[UUID4] = Query(None), #type: ignore
- wal: Optional[UUID4] = Query(None), #type: ignore
+ request: Request = Query(None), # type: ignore
+ nme: Optional[str] = Query(None), # type: ignore
+ usr: Optional[UUID4] = Query(None), # type: ignore
+ wal: Optional[UUID4] = Query(None), # type: ignore
):
user_id = usr.hex if usr else None
wallet_id = wal.hex if wal else None
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index b65c43a1c..e95e53f16 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -122,8 +122,8 @@ api_key_query = APIKeyQuery(
async def get_key_type(
r: Request,
- api_key_header: str = Security(api_key_header), #type: ignore
- api_key_query: str = Security(api_key_query) #type: ignore
+ api_key_header: str = Security(api_key_header), # type: ignore
+ api_key_query: str = Security(api_key_query), # type: ignore
) -> WalletTypeInfo:
# 0: admin
# 1: invoice
@@ -180,8 +180,8 @@ async def get_key_type(
async def require_admin_key(
r: Request,
- api_key_header: str = Security(api_key_header), #type: ignore
- api_key_query: str = Security(api_key_query) #type: ignore
+ api_key_header: str = Security(api_key_header), # type: ignore
+ api_key_query: str = Security(api_key_query), # type: ignore
):
token = api_key_header if api_key_header else api_key_query
@@ -199,8 +199,8 @@ async def require_admin_key(
async def require_invoice_key(
r: Request,
- api_key_header: str = Security(api_key_header), #type: ignore
- api_key_query: str = Security(api_key_query) #type: ignore
+ api_key_header: str = Security(api_key_header), # type: ignore
+ api_key_query: str = Security(api_key_query), # type: ignore
):
token = api_key_header if api_key_header else api_key_query
From 5810a4993b6bb90107aa07146476e0a557f08721 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Tue, 26 Jul 2022 09:08:16 +0200
Subject: [PATCH 59/84] Update lnbits/core/views/api.py
Co-authored-by: calle <93376500+callebtc@users.noreply.github.com>
---
lnbits/core/views/api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 97dff480d..3fc82382e 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -308,7 +308,7 @@ async def api_payments_pay_lnurl(
timeout=40,
)
if r.is_error:
- raise httpx.ConnectError("LNURL Callback Connection Error")
+ raise httpx.ConnectError("LNURL callback connection error")
except (httpx.ConnectError, httpx.RequestError):
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
From e4078910c43c87dc58a91cd8c250e526db57471d Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 26 Jul 2022 09:38:31 +0200
Subject: [PATCH 60/84] fix another fastapi type
---
lnbits/core/views/generic.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index 8b9437166..e9ef38778 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -218,7 +218,7 @@ async def lnurl_full_withdraw_callback(request: Request):
@core_html_routes.get("/deletewallet", response_class=RedirectResponse)
-async def deletewallet(request: Request, wal=Query(...), usr=Query(...)):
+async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): # type: ignore
user = await get_user(usr)
assert user is not None
user_wallet_ids = [u.id for u in user.wallets]
From 345cf8654432faae7600c44fe24304d2aeddbf29 Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 26 Jul 2022 09:52:57 +0200
Subject: [PATCH 61/84] fix another fastapi type
---
lnbits/wallets/macaroon/macaroon.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py
index aa00cd3e6..84c4909d1 100644
--- a/lnbits/wallets/macaroon/macaroon.py
+++ b/lnbits/wallets/macaroon/macaroon.py
@@ -73,7 +73,7 @@ class AESCipher(object):
final_key += key
return final_key[:output]
- def decrypt(self, encrypted) -> str:
+ def decrypt(self, encrypted: str) -> str: #type: ignore
"""Decrypts a string using AES-256-CBC."""
passphrase = self.passphrase
encrypted = base64.b64decode(encrypted)
From 1c543e3a848f514cc548116897002e8046ea4b79 Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 26 Jul 2022 09:56:46 +0200
Subject: [PATCH 62/84] ignore macaroon type errors
---
lnbits/wallets/macaroon/macaroon.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py
index 84c4909d1..1c69ca0b1 100644
--- a/lnbits/wallets/macaroon/macaroon.py
+++ b/lnbits/wallets/macaroon/macaroon.py
@@ -76,7 +76,7 @@ class AESCipher(object):
def decrypt(self, encrypted: str) -> str: #type: ignore
"""Decrypts a string using AES-256-CBC."""
passphrase = self.passphrase
- encrypted = base64.b64decode(encrypted)
+ encrypted = base64.b64decode(encrypted) #type: ignore
assert encrypted[0:8] == b"Salted__"
salt = encrypted[8:16]
key_iv = self.bytes_to_key(passphrase.encode(), salt, 32 + 16)
@@ -84,7 +84,7 @@ class AESCipher(object):
iv = key_iv[32:]
aes = AES.new(key, AES.MODE_CBC, iv)
try:
- return self.unpad(aes.decrypt(encrypted[16:])).decode()
+ return self.unpad(aes.decrypt(encrypted[16:])).decode() #type: ignore
except UnicodeDecodeError:
raise ValueError("Wrong passphrase")
From 27ce17cd74534d59ef68286a1fcae0ffb89c3f14 Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 26 Jul 2022 10:10:07 +0200
Subject: [PATCH 63/84] blacked
---
lnbits/wallets/macaroon/macaroon.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lnbits/wallets/macaroon/macaroon.py b/lnbits/wallets/macaroon/macaroon.py
index 1c69ca0b1..091551238 100644
--- a/lnbits/wallets/macaroon/macaroon.py
+++ b/lnbits/wallets/macaroon/macaroon.py
@@ -73,10 +73,10 @@ class AESCipher(object):
final_key += key
return final_key[:output]
- def decrypt(self, encrypted: str) -> str: #type: ignore
+ def decrypt(self, encrypted: str) -> str: # type: ignore
"""Decrypts a string using AES-256-CBC."""
passphrase = self.passphrase
- encrypted = base64.b64decode(encrypted) #type: ignore
+ encrypted = base64.b64decode(encrypted) # type: ignore
assert encrypted[0:8] == b"Salted__"
salt = encrypted[8:16]
key_iv = self.bytes_to_key(passphrase.encode(), salt, 32 + 16)
@@ -84,7 +84,7 @@ class AESCipher(object):
iv = key_iv[32:]
aes = AES.new(key, AES.MODE_CBC, iv)
try:
- return self.unpad(aes.decrypt(encrypted[16:])).decode() #type: ignore
+ return self.unpad(aes.decrypt(encrypted[16:])).decode() # type: ignore
except UnicodeDecodeError:
raise ValueError("Wrong passphrase")
From e63c4c9fa29e8d286ff7b56273bdf01a0ea14d74 Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 26 Jul 2022 12:07:16 +0200
Subject: [PATCH 64/84] revert self.wallet change
---
lnbits/decorators.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index e95e53f16..3a67e9439 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -53,13 +53,12 @@ class KeyChecker(SecurityBase):
# FIXME: Find another way to validate the key. A fetch from DB should be avoided here.
# Also, we should not return the wallet here - thats silly.
# Possibly store it in a Redis DB
- wallet = await get_wallet_for_key(key_value, self._key_type)
- if not wallet:
+ self.wallet = await get_wallet_for_key(key_value, self._key_type)
+ if not self.wallet:
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED,
detail="Invalid key or expired key.",
)
- self.wallet = wallet
except KeyError:
raise HTTPException(
From 00dba54ac88740513fa467f07194e0674baf13e9 Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 26 Jul 2022 12:21:21 +0200
Subject: [PATCH 65/84] remove FIXME add couple of ignores
---
lnbits/core/services.py | 3 ---
lnbits/core/views/api.py | 23 -----------------------
lnbits/decorators.py | 16 +++++++---------
3 files changed, 7 insertions(+), 35 deletions(-)
diff --git a/lnbits/core/services.py b/lnbits/core/services.py
index 2416ed315..b9aec8997 100644
--- a/lnbits/core/services.py
+++ b/lnbits/core/services.py
@@ -270,9 +270,6 @@ async def perform_lnurlauth(
k1 = unhexlify(parse_qs(cb.query)["k1"][0])
- # FIXME: wallet.wallet can be None here
- assert wallet.wallet is not None
-
key = wallet.wallet.lnurlauth_key(cb.netloc)
def int_to_bytes_suitable_der(x: int) -> bytes:
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 3fc82382e..d63e802db 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -56,16 +56,12 @@ from ..tasks import api_invoice_listeners
@core_app.get("/api/v1/wallet")
async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
if wallet.wallet_type == 0:
- # FIXME: wallet.wallet can be None here
- assert wallet.wallet is not None
return {
"id": wallet.wallet.id,
"name": wallet.wallet.name,
"balance": wallet.wallet.balance_msat,
}
else:
- # FIXME: wallet.wallet can be None here
- assert wallet.wallet is not None
return {"name": wallet.wallet.name, "balance": wallet.wallet.balance_msat}
@@ -73,9 +69,6 @@ async def api_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
async def api_update_balance(
amount: int, wallet: WalletTypeInfo = Depends(get_key_type)
):
- # FIXME: wallet.wallet can be None here
- assert wallet.wallet is not None
-
if wallet.wallet.user not in LNBITS_ADMIN_USERS:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user"
@@ -105,9 +98,6 @@ async def api_update_balance(
async def api_update_wallet(
new_name: str, wallet: WalletTypeInfo = Depends(require_admin_key)
):
- # FIXME: wallet.wallet can be None here
- assert wallet.wallet is not None
-
await update_wallet(wallet.wallet.id, new_name)
return {
"id": wallet.wallet.id,
@@ -122,9 +112,6 @@ async def api_payments(
offset: Optional[int] = None,
wallet: WalletTypeInfo = Depends(get_key_type),
):
- # FIXME: wallet.wallet can be None here
- assert wallet.wallet is not None
-
pendingPayments = await get_payments(
wallet_id=wallet.wallet.id,
pending=True,
@@ -269,15 +256,11 @@ async def api_payments_create(
status_code=HTTPStatus.BAD_REQUEST,
detail="BOLT11 string is invalid or not given",
)
- # FIXME: wallet.wallet can be None here
- assert wallet.wallet is not None
return await api_payments_pay_invoice(
invoiceData.bolt11, wallet.wallet
) # admin key
elif not invoiceData.out:
# invoice key
- # FIXME: wallet.wallet can be None here
- assert wallet.wallet is not None
return await api_payments_create_invoice(invoiceData, wallet.wallet)
else:
raise HTTPException(
@@ -342,8 +325,6 @@ async def api_payments_pay_lnurl(
if data.comment:
extra["comment"] = data.comment
assert data.description is not None, "description is required"
- # FIXME: wallet.wallet can be None here
- assert wallet.wallet is not None
payment_hash = await pay_invoice(
wallet_id=wallet.wallet.id,
payment_request=params["pr"],
@@ -397,8 +378,6 @@ async def subscribe(request: Request, wallet: Wallet):
async def api_payments_sse(
request: Request, wallet: WalletTypeInfo = Depends(get_key_type)
):
- # FIXME: wallet.wallet can be None here
- assert wallet.wallet is not None
return EventSourceResponse(
subscribe(request, wallet.wallet), ping=20, media_type="text/event-stream"
)
@@ -477,8 +456,6 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
params.update(kind="auth")
params.update(callback=url) # with k1 already in it
- # FIXME: wallet.wallet can be None here
- assert wallet.wallet is not None
lnurlauth_key = wallet.wallet.lnurlauth_key(domain)
params.update(pubkey=lnurlauth_key.verifying_key.to_string("compressed").hex())
else:
diff --git a/lnbits/decorators.py b/lnbits/decorators.py
index 3a67e9439..6685cfb2d 100644
--- a/lnbits/decorators.py
+++ b/lnbits/decorators.py
@@ -41,6 +41,7 @@ class KeyChecker(SecurityBase):
name="X-API-KEY",
description="Wallet API Key - HEADER",
)
+ self.wallet = None # type: ignore
self.model: APIKey = key
async def __call__(self, request: Request):
@@ -53,7 +54,7 @@ class KeyChecker(SecurityBase):
# FIXME: Find another way to validate the key. A fetch from DB should be avoided here.
# Also, we should not return the wallet here - thats silly.
# Possibly store it in a Redis DB
- self.wallet = await get_wallet_for_key(key_value, self._key_type)
+ self.wallet = await get_wallet_for_key(key_value, self._key_type) # type: ignore
if not self.wallet:
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED,
@@ -100,9 +101,9 @@ class WalletAdminKeyChecker(KeyChecker):
class WalletTypeInfo:
wallet_type: int
- wallet: Union[Wallet, None]
+ wallet: Wallet
- def __init__(self, wallet_type: int, wallet: Union[Wallet, None]) -> None:
+ def __init__(self, wallet_type: int, wallet: Wallet) -> None:
self.wallet_type = wallet_type
self.wallet = wallet
@@ -137,8 +138,7 @@ async def get_key_type(
try:
admin_checker = WalletAdminKeyChecker(api_key=token)
await admin_checker.__call__(r)
- wallet = WalletTypeInfo(0, admin_checker.wallet)
- assert wallet.wallet is not None
+ wallet = WalletTypeInfo(0, admin_checker.wallet) # type: ignore
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
):
@@ -157,9 +157,7 @@ async def get_key_type(
try:
invoice_checker = WalletInvoiceKeyChecker(api_key=token)
await invoice_checker.__call__(r)
- wallet = WalletTypeInfo(1, invoice_checker.wallet)
- # FIXME: wallet.wallet can be None here
- assert wallet.wallet is not None
+ wallet = WalletTypeInfo(1, invoice_checker.wallet) # type: ignore
if (LNBITS_ADMIN_USERS and wallet.wallet.user not in LNBITS_ADMIN_USERS) and (
LNBITS_ADMIN_EXTENSIONS and pathname in LNBITS_ADMIN_EXTENSIONS
):
@@ -171,7 +169,7 @@ async def get_key_type(
if e.status_code == HTTPStatus.BAD_REQUEST:
raise
if e.status_code == HTTPStatus.UNAUTHORIZED:
- return WalletTypeInfo(2, None)
+ return WalletTypeInfo(2, None) # type: ignore
except:
raise
return wallet
From 7667c2f5318286fd54271dbe4be62fc62e7a1308 Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 26 Jul 2022 12:31:01 +0200
Subject: [PATCH 66/84] doesnt touch class invoice and leave fake wallet as is
---
lnbits/bolt11.py | 1 -
lnbits/wallets/fake.py | 6 ++----
2 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/lnbits/bolt11.py b/lnbits/bolt11.py
index cc8c60401..cc8415852 100644
--- a/lnbits/bolt11.py
+++ b/lnbits/bolt11.py
@@ -23,7 +23,6 @@ class Route(NamedTuple):
class Invoice(object):
payment_hash: str
- checking_id: Optional[str] = None
amount_msat: int = 0
description: Optional[str] = None
description_hash: Optional[str] = None
diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py
index ba2956c58..3126ee46a 100644
--- a/lnbits/wallets/fake.py
+++ b/lnbits/wallets/fake.py
@@ -80,11 +80,9 @@ class FakeWallet(Wallet):
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
invoice = decode(bolt11)
- # TODO: no data here?
- data: Dict = {"privkey": "missing"}
if (
- invoice.checking_id is not None
- and invoice.checking_id[6:] == data["privkey"][:6]
+ hasattr(invoice, "checking_id")
+ and invoice.checking_id[6:] == data["privkey"][:6] # type: ignore
):
return PaymentResponse(True, invoice.payment_hash, 0)
else:
From 36cb1eb47befe8a5a8631bb263b6b85a2a8b3bc8 Mon Sep 17 00:00:00 2001
From: dni
Date: Tue, 26 Jul 2022 12:46:43 +0200
Subject: [PATCH 67/84] fix int type by changing the order
---
lnbits/core/views/api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index d63e802db..627d3aed3 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -603,8 +603,8 @@ class ConversionData(BaseModel):
async def api_fiat_as_sats(data: ConversionData):
output = {}
if data.from_ == "sat":
- output["sats"] = data.amount
output["BTC"] = data.amount / 100000000
+ output["sats"] = int(data.amount)
for currency in data.to.split(","):
output[currency.strip().upper()] = await satoshis_amount_as_fiat(
data.amount, currency.strip()
From 7da496dbb4a13f7924f812dda82cd3c50a197a50 Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 27 Jul 2022 09:39:52 +0200
Subject: [PATCH 68/84] remove asserts and add ignore instead for now
---
lnbits/core/tasks.py | 3 +--
lnbits/core/views/generic.py | 32 ++++++++++++++------------------
2 files changed, 15 insertions(+), 20 deletions(-)
diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py
index 8234b5fc4..07b8a8933 100644
--- a/lnbits/core/tasks.py
+++ b/lnbits/core/tasks.py
@@ -55,8 +55,7 @@ async def dispatch_webhook(payment: Payment):
data = payment.dict()
try:
logger.debug("sending webhook", payment.webhook)
- assert payment.webhook is not None
- r = await client.post(payment.webhook, json=data, timeout=40)
+ r = await client.post(payment.webhook, json=data, timeout=40) # type: ignore
await mark_webhook_sent(payment, r.status_code)
except (httpx.ConnectError, httpx.RequestError):
await mark_webhook_sent(payment, -1)
diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py
index e9ef38778..21572b285 100644
--- a/lnbits/core/views/generic.py
+++ b/lnbits/core/views/generic.py
@@ -121,11 +121,9 @@ async def wallet(
if not user_id:
user = await get_user((await create_account()).id)
- assert user is not None
- logger.info(f"Create user {user.id}")
+ logger.info(f"Create user {user.id}") # type: ignore
else:
user = await get_user(user_id)
- assert user is not None
if not user:
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": "User does not exist."}
@@ -137,21 +135,21 @@ async def wallet(
if LNBITS_ADMIN_USERS and user_id in LNBITS_ADMIN_USERS:
user.admin = True
if not wallet_id:
- if user.wallets and not wallet_name:
- wallet = user.wallets[0]
+ if user.wallets and not wallet_name: # type: ignore
+ wallet = user.wallets[0] # type: ignore
else:
- wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name)
+ wallet = await create_wallet(user_id=user.id, wallet_name=wallet_name) # type: ignore
logger.info(
- f"Created new wallet {wallet_name if wallet_name else '(no name)'} for user {user.id}"
+ f"Created new wallet {wallet_name if wallet_name else '(no name)'} for user {user.id}" # type: ignore
)
return RedirectResponse(
- f"/wallet?usr={user.id}&wal={wallet.id}",
+ f"/wallet?usr={user.id}&wal={wallet.id}", # type: ignore
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
)
logger.debug(f"Access wallet {wallet_name}{'of user '+ user.id if user else ''}")
- userwallet = user.get_wallet(wallet_id)
+ userwallet = user.get_wallet(wallet_id) # type: ignore
if not userwallet:
return template_renderer().TemplateResponse(
"error.html", {"request": request, "err": "Wallet not found"}
@@ -161,10 +159,10 @@ async def wallet(
"core/wallet.html",
{
"request": request,
- "user": user.dict(),
+ "user": user.dict(), # type: ignore
"wallet": userwallet.dict(),
"service_fee": service_fee,
- "web_manifest": f"/manifest/{user.id}.webmanifest",
+ "web_manifest": f"/manifest/{user.id}.webmanifest", # type: ignore
},
)
@@ -220,19 +218,18 @@ async def lnurl_full_withdraw_callback(request: Request):
@core_html_routes.get("/deletewallet", response_class=RedirectResponse)
async def deletewallet(request: Request, wal: str = Query(...), usr: str = Query(...)): # type: ignore
user = await get_user(usr)
- assert user is not None
- user_wallet_ids = [u.id for u in user.wallets]
+ user_wallet_ids = [u.id for u in user.wallets] # type: ignore
if wal not in user_wallet_ids:
raise HTTPException(HTTPStatus.FORBIDDEN, "Not your wallet.")
else:
- await delete_wallet(user_id=user.id, wallet_id=wal)
+ await delete_wallet(user_id=user.id, wallet_id=wal) # type: ignore
user_wallet_ids.remove(wal)
logger.debug("Deleted wallet {wal} of user {user.id}")
if user_wallet_ids:
return RedirectResponse(
- url_for("/wallet", usr=user.id, wal=user_wallet_ids[0]),
+ url_for("/wallet", usr=user.id, wal=user_wallet_ids[0]), # type: ignore
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
)
@@ -255,8 +252,7 @@ async def lnurlwallet(request: Request):
async with db.connect() as conn:
account = await create_account(conn=conn)
user = await get_user(account.id, conn=conn)
- assert user is not None
- wallet = await create_wallet(user_id=user.id, conn=conn)
+ wallet = await create_wallet(user_id=user.id, conn=conn) # type: ignore
asyncio.create_task(
redeem_lnurl_withdraw(
@@ -269,7 +265,7 @@ async def lnurlwallet(request: Request):
)
return RedirectResponse(
- f"/wallet?usr={user.id}&wal={wallet.id}",
+ f"/wallet?usr={user.id}&wal={wallet.id}", # type: ignore
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
)
From c72834e47ea0611764753cf4ac26da5a509095aa Mon Sep 17 00:00:00 2001
From: dni
Date: Wed, 27 Jul 2022 09:50:16 +0200
Subject: [PATCH 69/84] revert attribute check
---
lnbits/core/views/api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 3ef92ea60..b1366b077 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -181,7 +181,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
lnurl_response: Union[None, bool, str] = None
if data.lnurl_callback:
- if hasattr(data, "lnurl_balance_check"):
+ if "lnurl_balance_check" in data:
assert (
data.lnurl_balance_check is not None
), "lnurl_balance_check is required"
From 6e344561e391fa647882edc9f011f53c6c733ad1 Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 27 Jul 2022 14:49:13 +0100
Subject: [PATCH 70/84] Updated requirements
---
requirements.txt | 68 ++++++++++++++++++++++++------------------------
1 file changed, 34 insertions(+), 34 deletions(-)
diff --git a/requirements.txt b/requirements.txt
index f8ccf47cb..23d428e54 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,52 +1,52 @@
-aiofiles==0.7.0
-anyio==3.3.1
-asgiref==3.4.1
+aiofiles==0.8.0
+anyio==3.6.1
asyncio==3.4.3
-attrs==21.2.0
+attrs==21.4.0
bech32==1.2.0
bitstring==3.1.9
cerberus==1.3.4
-certifi==2021.5.30
-charset-normalizer==2.0.6
-click==8.0.1
-ecdsa==0.17.0
-embit==0.4.9
-environs==9.3.3
-fastapi==0.68.1
+certifi==2022.6.15
+cffi==1.15.0
+click==8.1.3
+ecdsa==0.18.0
+embit==0.5.0
+environs==9.5.0
+fastapi==0.79.0
h11==0.12.0
httpcore==0.15.0
-httptools==0.2.0
+httptools==0.4.0
httpx==0.23.0
-idna==3.2
-importlib-metadata==4.8.1
+idna==3.3
jinja2==3.0.1
lnurl==0.3.6
loguru==0.6.0
-markupsafe==2.0.1
-marshmallow==3.13.0
-outcome==1.1.0
-psycopg2-binary==2.9.1
-pycryptodomex==3.14.1
-pydantic==1.8.2
-pypng==0.0.21
+markupsafe==2.1.1
+marshmallow==3.17.0
+outcome==1.2.0
+packaging==21.3
+psycopg2-binary==2.9.3
+pycparser==2.21
+pycryptodomex==3.15.0
+pydantic==1.9.1
+pyngrok==5.1.0
+pyparsing==3.0.9
+pypng==0.20220715.0
pyqrcode==1.2.1
-pyscss==1.3.7
-python-dotenv==0.19.0
-pyyaml==5.4.1
+pyscss==1.4.0
+python-dotenv==0.20.0
+pyyaml==6.0
represent==1.6.0.post0
rfc3986==1.5.0
secp256k1==0.14.0
-cffi==1.15.0
-shortuuid==1.0.1
+shortuuid==1.0.9
six==1.16.0
sniffio==1.2.0
+sqlalchemy-aio==0.17.0
sqlalchemy==1.3.23
-sqlalchemy-aio==0.16.0
-sse-starlette==0.6.2
-starlette==0.14.2
-typing-extensions==3.10.0.2
-uvicorn==0.15.0
+sse-starlette==0.10.3
+starlette==0.19.1
+typing-extensions==4.3.0
+uvicorn==0.18.2
uvloop==0.16.0
-watchgod==0.7
-websockets==10.0
-zipp==3.5.0
+watchfiles==0.16.0
+websockets==10.3
From ee1cc28187848aa2e3f833260005d9ab6a137537 Mon Sep 17 00:00:00 2001
From: Arc <33088785+arcbtc@users.noreply.github.com>
Date: Wed, 27 Jul 2022 15:01:23 +0100
Subject: [PATCH 71/84] Update installation.md
---
docs/guide/installation.md | 25 ++++++++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index 591516f1a..380c756d7 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -12,9 +12,28 @@ You can choose between two python package managers, `venv` and `pipenv`. Both ar
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).
-## Option 1: pipenv
+## Option 1: poetry
-You can also use Pipenv to manage your python packages.
+```sh
+git clone https://github.com/lnbits/lnbits-legend.git
+cd lnbits-legend/
+
+curl -sSL https://install.python-poetry.org | python3 -
+poetry install
+
+# You may need to install python 3.9, update your python following this guide https://linuxize.com/post/how-to-install-python-3-9-on-ubuntu-20-04/
+
+ mkdir data && cp .env.example .env
+```
+
+#### Running the server
+
+```sh
+poetry run lnbits
+# To change port/host pass 'poetry run lnbits --port 9000 --host 0.0.0.0'
+```
+
+## Option 2: pipenv
```sh
git clone https://github.com/lnbits/lnbits-legend.git
@@ -44,7 +63,7 @@ pipenv run python -m uvicorn lnbits.__main__:app --port 5000 --host 0.0.0.0
Add the flag `--reload` for development (includes hot-reload).
-## Option 2: venv
+## Option 3: venv
Download this repo and install the dependencies:
From 2b811ec1bbdf38ac321c0fef3d3c355ab33923b4 Mon Sep 17 00:00:00 2001
From: Arc <33088785+arcbtc@users.noreply.github.com>
Date: Wed, 27 Jul 2022 15:07:16 +0100
Subject: [PATCH 72/84] Update installation.md
---
docs/guide/installation.md | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index 380c756d7..0b28f803c 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -65,8 +65,6 @@ Add the flag `--reload` for development (includes hot-reload).
## Option 3: venv
-Download this repo and install the dependencies:
-
```sh
git clone https://github.com/lnbits/lnbits-legend.git
cd lnbits-legend/
@@ -86,6 +84,26 @@ 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/
+# Install nix, modern debian distros usually already include
+sh <(curl -L https://nixos.org/nix/install) --daemon
+
+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 8cac577f2f912abd829ad44809f598723bb4b862 Mon Sep 17 00:00:00 2001
From: Arc <33088785+arcbtc@users.noreply.github.com>
Date: Wed, 27 Jul 2022 15:09:41 +0100
Subject: [PATCH 73/84] 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 0b28f803c..f38f606d1 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -8,7 +8,7 @@ nav_order: 2
# Basic installation
-You can choose between two python package managers, `venv` and `pipenv`. Both are fine but if you don't know what you're doing, just go for the first option.
+You can choose between four package managers, `poetry`, `pipenv`, `venv` and `nix`.
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).
From 41dc1b05125700f906920e352e77967c2c74791c Mon Sep 17 00:00:00 2001
From: Tiago Vasconcelos
Date: Wed, 27 Jul 2022 15:45:17 +0100
Subject: [PATCH 74/84] Fix/hotfix tpos 2 (#783)
* fix older tpos without tips
* bad math requesting invoice
* make format
---
lnbits/app.py | 4 +++-
lnbits/extensions/offlineshop/__init__.py | 1 -
lnbits/extensions/tpos/templates/tpos/tpos.html | 7 ++++---
lnbits/server.py | 11 ++++++-----
4 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 83a9749bb..8e32e38bc 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -48,7 +48,9 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
app = FastAPI()
app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static")
app.mount(
- "/core/static", StaticFiles(packages=[("lnbits.core", "static")]), name="core_static"
+ "/core/static",
+ StaticFiles(packages=[("lnbits.core", "static")]),
+ name="core_static",
)
origins = ["*"]
diff --git a/lnbits/extensions/offlineshop/__init__.py b/lnbits/extensions/offlineshop/__init__.py
index 2dbaa7d0f..0b776a8ce 100644
--- a/lnbits/extensions/offlineshop/__init__.py
+++ b/lnbits/extensions/offlineshop/__init__.py
@@ -21,7 +21,6 @@ def offlineshop_renderer():
return template_renderer(["lnbits/extensions/offlineshop/templates"])
-
from .lnurl import * # noqa
from .views import * # noqa
from .views_api import * # noqa
diff --git a/lnbits/extensions/tpos/templates/tpos/tpos.html b/lnbits/extensions/tpos/templates/tpos/tpos.html
index e11f60797..ebc6595e2 100644
--- a/lnbits/extensions/tpos/templates/tpos/tpos.html
+++ b/lnbits/extensions/tpos/templates/tpos/tpos.html
@@ -308,7 +308,9 @@
},
sat: function () {
if (!this.exchangeRate) return 0
- return Math.ceil((this.amount / this.exchangeRate) * 100000000)
+ return Math.ceil(
+ ((this.amount - this.tipAmount) / this.exchangeRate) * 100000000
+ )
},
tipAmountSat: function () {
if (!this.exchangeRate) return 0
@@ -423,10 +425,9 @@
'{{ tpos.tip_options | tojson }}' == 'null'
? null
: JSON.parse('{{ tpos.tip_options }}')
- console.log(typeof this.tip_options, this.tip_options)
setInterval(function () {
getRates()
- }, 20000)
+ }, 120000)
}
})
diff --git a/lnbits/server.py b/lnbits/server.py
index c0efb8953..4a63b3b7b 100644
--- a/lnbits/server.py
+++ b/lnbits/server.py
@@ -1,17 +1,18 @@
+import click
import uvicorn
-import click
@click.command()
-@click.option('--port', default="5000", help='Port to run LNBits on')
-@click.option('--host', default="127.0.0.1", help='Host to run LNBits on')
+@click.option("--port", default="5000", help="Port to run LNBits on")
+@click.option("--host", default="127.0.0.1", help="Host to run LNBits on")
def main(port, host):
"""Launched with `poetry run lnbits` at root level"""
uvicorn.run("lnbits.__main__:app", port=port, host=host)
-if __name__ == '__main__':
+
+if __name__ == "__main__":
main()
-#def main():
+# def main():
# """Launched with `poetry run start` at root level"""
# uvicorn.run("lnbits.__main__:app")
From 1c77d165e27921c58fa06cbf46d3d98fb6b62d50 Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 27 Jul 2022 17:32:03 +0100
Subject: [PATCH 75/84] Made reliable
Fixed issue scanning tickets by adding "ticket://" prefix
---
lnbits/extensions/events/crud.py | 2 +-
.../events/templates/events/display.html | 18 ++++-----
.../events/templates/events/register.html | 2 +-
.../events/templates/events/ticket.html | 5 +--
lnbits/extensions/events/views_api.py | 40 +++++++++----------
5 files changed, 32 insertions(+), 35 deletions(-)
diff --git a/lnbits/extensions/events/crud.py b/lnbits/extensions/events/crud.py
index 9e04476d3..4cc86ac4d 100644
--- a/lnbits/extensions/events/crud.py
+++ b/lnbits/extensions/events/crud.py
@@ -16,7 +16,7 @@ async def create_ticket(
INSERT INTO events.ticket (id, wallet, event, name, email, registered, paid)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
- (payment_hash, wallet, event, name, email, False, False),
+ (payment_hash, wallet, event, name, email, False, True),
)
ticket = await get_ticket(payment_hash)
diff --git a/lnbits/extensions/events/templates/events/display.html b/lnbits/extensions/events/templates/events/display.html
index 4c1f557f1..7e62b7a74 100644
--- a/lnbits/extensions/events/templates/events/display.html
+++ b/lnbits/extensions/events/templates/events/display.html
@@ -135,14 +135,8 @@
var self = this
axios
- .post(
- '/events/api/v1/tickets/' + '{{ event_id }}/{{ event_price }}',
- {
- event: '{{ event_id }}',
- event_name: '{{ event_name }}',
- name: self.formDialog.data.name,
- email: self.formDialog.data.email
- }
+ .get(
+ '/events/api/v1/tickets/' + '{{ event_id }}'
)
.then(function (response) {
self.paymentReq = response.data.payment_request
@@ -161,7 +155,13 @@
paymentChecker = setInterval(function () {
axios
- .get('/events/api/v1/tickets/' + self.paymentCheck)
+ .post('/events/api/v1/tickets/' + '{{ event_id }}/' + self.paymentCheck,
+ {
+ event: '{{ event_id }}',
+ event_name: '{{ event_name }}',
+ name: self.formDialog.data.name,
+ email: self.formDialog.data.email
+ })
.then(function (res) {
if (res.data.paid) {
clearInterval(paymentChecker)
diff --git a/lnbits/extensions/events/templates/events/register.html b/lnbits/extensions/events/templates/events/register.html
index 4dff9afbb..3913cc0c4 100644
--- a/lnbits/extensions/events/templates/events/register.html
+++ b/lnbits/extensions/events/templates/events/register.html
@@ -133,7 +133,7 @@
var self = this
LNbits.api
- .request('GET', '/events/api/v1/register/ticket/' + res)
+ .request('GET', '/events/api/v1/register/ticket/' + res.split("//")[1])
.then(function (response) {
self.$q.notify({
type: 'positive',
diff --git a/lnbits/extensions/events/templates/events/ticket.html b/lnbits/extensions/events/templates/events/ticket.html
index a53f834f9..21b7cfa82 100644
--- a/lnbits/extensions/events/templates/events/ticket.html
+++ b/lnbits/extensions/events/templates/events/ticket.html
@@ -13,9 +13,8 @@
diff --git a/lnbits/extensions/events/views_api.py b/lnbits/extensions/events/views_api.py
index 87edb07da..45ee4de05 100644
--- a/lnbits/extensions/events/views_api.py
+++ b/lnbits/extensions/events/views_api.py
@@ -97,8 +97,8 @@ async def api_tickets(
return [ticket.dict() for ticket in await get_tickets(wallet_ids)]
-@events_ext.post("/api/v1/tickets/{event_id}/{sats}")
-async def api_ticket_make_ticket(event_id, sats, data: CreateTicket):
+@events_ext.get("/api/v1/tickets/{event_id}")
+async def api_ticket_make_ticket(event_id):
event = await get_event(event_id)
if not event:
raise HTTPException(
@@ -107,37 +107,35 @@ async def api_ticket_make_ticket(event_id, sats, data: CreateTicket):
try:
payment_hash, payment_request = await create_invoice(
wallet_id=event.wallet,
- amount=int(sats),
+ amount=event.price_per_ticket,
memo=f"{event_id}",
extra={"tag": "events"},
)
except Exception as e:
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
- ticket = await create_ticket(
- payment_hash=payment_hash,
- wallet=event.wallet,
- event=event_id,
- name=data.name,
- email=data.email,
- )
-
- if not ticket:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail=f"Event could not be fetched."
- )
-
return {"payment_hash": payment_hash, "payment_request": payment_request}
-@events_ext.get("/api/v1/tickets/{payment_hash}")
-async def api_ticket_send_ticket(payment_hash):
- ticket = await get_ticket(payment_hash)
-
+@events_ext.post("/api/v1/tickets/{event_id}/{payment_hash}")
+async def api_ticket_send_ticket(event_id, payment_hash, data: CreateTicket):
+ event = await get_event(event_id)
try:
status = await api_payment(payment_hash)
if status["paid"]:
- await set_ticket_paid(payment_hash=payment_hash)
+ ticket = await create_ticket(
+ payment_hash=payment_hash,
+ wallet=event.wallet,
+ event=event_id,
+ name=data.name,
+ email=data.email,
+ )
+
+ if not ticket:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail=f"Event could not be fetched."
+ )
+
return {"paid": True, "ticket_id": ticket.id}
except Exception:
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Not paid")
From bb002c6d4091056132483802a256ebb8c01af4df Mon Sep 17 00:00:00 2001
From: Arc <33088785+arcbtc@users.noreply.github.com>
Date: Wed, 27 Jul 2022 17:57:59 +0100
Subject: [PATCH 76/84] Update wallets.md
---
docs/guide/wallets.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/docs/guide/wallets.md b/docs/guide/wallets.md
index 7a3b6a273..dfea66aa2 100644
--- a/docs/guide/wallets.md
+++ b/docs/guide/wallets.md
@@ -17,7 +17,6 @@ A backend wallet can be configured using the following LNbits environment variab
### CLightning
Using this wallet requires the installation of the `pylightning` Python package.
-If you want to use LNURLp you should use SparkWallet because of an issue with description_hash and CLightning.
- `LNBITS_BACKEND_WALLET_CLASS`: **CLightningWallet**
- `CLIGHTNING_RPC`: /file/path/lightning-rpc
From 58659d2f47be25c30620576186374e79e20ab926 Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 27 Jul 2022 18:20:36 +0100
Subject: [PATCH 77/84] Added meta for api /docs
---
lnbits/app.py | 9 +++++++-
.../events/templates/events/display.html | 22 ++++++++++---------
.../events/templates/events/register.html | 5 ++++-
3 files changed, 24 insertions(+), 12 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 8e32e38bc..19482b067 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -45,7 +45,14 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
"""
configure_logger()
- app = FastAPI()
+ app = FastAPI(
+ title="LNbits API",
+ description="API for LNbits, the free and open source bitcoin wallet and accounts system with plugins.",
+ license_info={
+ "name": "MIT License",
+ "url": "https://raw.githubusercontent.com/lnbits/lnbits-legend/main/LICENSE",
+ },
+ )
app.mount("/static", StaticFiles(packages=[("lnbits", "static")]), name="static")
app.mount(
"/core/static",
diff --git a/lnbits/extensions/events/templates/events/display.html b/lnbits/extensions/events/templates/events/display.html
index 7e62b7a74..4589c5785 100644
--- a/lnbits/extensions/events/templates/events/display.html
+++ b/lnbits/extensions/events/templates/events/display.html
@@ -135,9 +135,7 @@
var self = this
axios
- .get(
- '/events/api/v1/tickets/' + '{{ event_id }}'
- )
+ .get('/events/api/v1/tickets/' + '{{ event_id }}')
.then(function (response) {
self.paymentReq = response.data.payment_request
self.paymentCheck = response.data.payment_hash
@@ -155,13 +153,17 @@
paymentChecker = setInterval(function () {
axios
- .post('/events/api/v1/tickets/' + '{{ event_id }}/' + self.paymentCheck,
- {
- event: '{{ event_id }}',
- event_name: '{{ event_name }}',
- name: self.formDialog.data.name,
- email: self.formDialog.data.email
- })
+ .post(
+ '/events/api/v1/tickets/' +
+ '{{ event_id }}/' +
+ self.paymentCheck,
+ {
+ event: '{{ event_id }}',
+ event_name: '{{ event_name }}',
+ name: self.formDialog.data.name,
+ email: self.formDialog.data.email
+ }
+ )
.then(function (res) {
if (res.data.paid) {
clearInterval(paymentChecker)
diff --git a/lnbits/extensions/events/templates/events/register.html b/lnbits/extensions/events/templates/events/register.html
index 3913cc0c4..43d43070b 100644
--- a/lnbits/extensions/events/templates/events/register.html
+++ b/lnbits/extensions/events/templates/events/register.html
@@ -133,7 +133,10 @@
var self = this
LNbits.api
- .request('GET', '/events/api/v1/register/ticket/' + res.split("//")[1])
+ .request(
+ 'GET',
+ '/events/api/v1/register/ticket/' + res.split('//')[1]
+ )
.then(function (response) {
self.$q.notify({
type: 'positive',
From 3a28ff1020bae2ef4a91ca573dcffff06652a896 Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 27 Jul 2022 19:21:04 +0100
Subject: [PATCH 78/84] added swagger to extensions
---
.../bleskomat/templates/bleskomat/_api_docs.html | 1 +
lnbits/extensions/copilot/templates/copilot/_api_docs.html | 1 +
.../discordbot/templates/discordbot/_api_docs.html | 1 +
lnbits/extensions/events/templates/events/_api_docs.html | 1 +
lnbits/extensions/jukebox/templates/jukebox/_api_docs.html | 2 ++
.../livestream/templates/livestream/_api_docs.html | 2 ++
.../lnaddress/templates/lnaddress/_api_docs.html | 1 +
.../extensions/lndhub/templates/lndhub/_instructions.html | 1 +
.../extensions/lnticket/templates/lnticket/_api_docs.html | 1 +
.../lnurldevice/templates/lnurldevice/_api_docs.html | 6 ++++++
lnbits/extensions/lnurlp/templates/lnurlp/_api_docs.html | 2 ++
.../lnurlpayout/{config.json.example => config.json} | 2 +-
.../lnurlpayout/templates/lnurlpayout/_api_docs.html | 7 +++++++
.../offlineshop/templates/offlineshop/_api_docs.html | 1 +
lnbits/extensions/paywall/templates/paywall/_api_docs.html | 1 +
.../extensions/satsdice/templates/satsdice/_api_docs.html | 1 +
lnbits/extensions/satspay/templates/satspay/_api_docs.html | 1 +
.../splitpayments/templates/splitpayments/_api_docs.html | 6 ++++++
.../streamalerts/templates/streamalerts/_api_docs.html | 1 +
.../subdomains/templates/subdomains/_api_docs.html | 1 +
lnbits/extensions/tipjar/templates/tipjar/_api_docs.html | 1 +
lnbits/extensions/tpos/templates/tpos/_api_docs.html | 1 +
.../usermanager/templates/usermanager/_api_docs.html | 1 +
.../watchonly/templates/watchonly/_api_docs.html | 1 +
.../extensions/withdraw/templates/withdraw/_api_docs.html | 1 +
25 files changed, 44 insertions(+), 1 deletion(-)
rename lnbits/extensions/lnurlpayout/{config.json.example => config.json} (71%)
diff --git a/lnbits/extensions/bleskomat/templates/bleskomat/_api_docs.html b/lnbits/extensions/bleskomat/templates/bleskomat/_api_docs.html
index 210d534c2..2a7160bd8 100644
--- a/lnbits/extensions/bleskomat/templates/bleskomat/_api_docs.html
+++ b/lnbits/extensions/bleskomat/templates/bleskomat/_api_docs.html
@@ -62,4 +62,5 @@
+
diff --git a/lnbits/extensions/copilot/templates/copilot/_api_docs.html b/lnbits/extensions/copilot/templates/copilot/_api_docs.html
index eed25169b..6105d1691 100644
--- a/lnbits/extensions/copilot/templates/copilot/_api_docs.html
+++ b/lnbits/extensions/copilot/templates/copilot/_api_docs.html
@@ -14,6 +14,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/discordbot/templates/discordbot/_api_docs.html b/lnbits/extensions/discordbot/templates/discordbot/_api_docs.html
index fcda086a3..b57e4ab1f 100644
--- a/lnbits/extensions/discordbot/templates/discordbot/_api_docs.html
+++ b/lnbits/extensions/discordbot/templates/discordbot/_api_docs.html
@@ -34,6 +34,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/events/templates/events/_api_docs.html b/lnbits/extensions/events/templates/events/_api_docs.html
index a5c821747..d2fa890e5 100644
--- a/lnbits/extensions/events/templates/events/_api_docs.html
+++ b/lnbits/extensions/events/templates/events/_api_docs.html
@@ -20,4 +20,5 @@
+
diff --git a/lnbits/extensions/jukebox/templates/jukebox/_api_docs.html b/lnbits/extensions/jukebox/templates/jukebox/_api_docs.html
index f4daaf39e..4bae49651 100644
--- a/lnbits/extensions/jukebox/templates/jukebox/_api_docs.html
+++ b/lnbits/extensions/jukebox/templates/jukebox/_api_docs.html
@@ -24,6 +24,8 @@
label="API info"
:content-inset-level="0.5"
>
+
+
diff --git a/lnbits/extensions/livestream/templates/livestream/_api_docs.html b/lnbits/extensions/livestream/templates/livestream/_api_docs.html
index fb4853ced..34b6518dc 100644
--- a/lnbits/extensions/livestream/templates/livestream/_api_docs.html
+++ b/lnbits/extensions/livestream/templates/livestream/_api_docs.html
@@ -17,6 +17,8 @@
label="API info"
:content-inset-level="0.5"
>
+
+
+
diff --git a/lnbits/extensions/lndhub/templates/lndhub/_instructions.html b/lnbits/extensions/lndhub/templates/lndhub/_instructions.html
index 4db79aba8..005bced5e 100644
--- a/lnbits/extensions/lndhub/templates/lndhub/_instructions.html
+++ b/lnbits/extensions/lndhub/templates/lndhub/_instructions.html
@@ -31,5 +31,6 @@
+
diff --git a/lnbits/extensions/lnticket/templates/lnticket/_api_docs.html b/lnbits/extensions/lnticket/templates/lnticket/_api_docs.html
index 69328f384..e93409244 100644
--- a/lnbits/extensions/lnticket/templates/lnticket/_api_docs.html
+++ b/lnbits/extensions/lnticket/templates/lnticket/_api_docs.html
@@ -19,4 +19,5 @@
+
diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
index 940d4691e..7f9afa27b 100644
--- a/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
+++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
@@ -17,6 +17,12 @@
label="API info"
:content-inset-level="0.5"
>
+
+
@@ -51,6 +52,7 @@
expand-separator
label="Create a pay link"
>
+
POST /lnurlp/api/v1/links
diff --git a/lnbits/extensions/lnurlpayout/config.json.example b/lnbits/extensions/lnurlpayout/config.json
similarity index 71%
rename from lnbits/extensions/lnurlpayout/config.json.example
rename to lnbits/extensions/lnurlpayout/config.json
index 1e72c0c1e..b4160d7bd 100644
--- a/lnbits/extensions/lnurlpayout/config.json.example
+++ b/lnbits/extensions/lnurlpayout/config.json
@@ -2,5 +2,5 @@
"name": "LNURLPayout",
"short_description": "Autodump wallet funds to LNURLpay",
"icon": "exit_to_app",
- "contributors": ["arcbtc"]
+ "contributors": ["arcbtc","talvasconcelos"]
}
diff --git a/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html b/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html
index 7febea44c..4f921bb57 100644
--- a/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html
+++ b/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html
@@ -4,6 +4,12 @@
label="API info"
:content-inset-level="0.5"
>
+
@@ -32,6 +38,7 @@
expand-separator
label="Create a lnurlpayout"
>
+
+
+
diff --git a/lnbits/extensions/satsdice/templates/satsdice/_api_docs.html b/lnbits/extensions/satsdice/templates/satsdice/_api_docs.html
index a80fd37ad..e85e9586c 100644
--- a/lnbits/extensions/satsdice/templates/satsdice/_api_docs.html
+++ b/lnbits/extensions/satsdice/templates/satsdice/_api_docs.html
@@ -4,6 +4,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/satspay/templates/satspay/_api_docs.html b/lnbits/extensions/satspay/templates/satspay/_api_docs.html
index 77451ae59..336ab8997 100644
--- a/lnbits/extensions/satspay/templates/satspay/_api_docs.html
+++ b/lnbits/extensions/satspay/templates/satspay/_api_docs.html
@@ -15,6 +15,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/splitpayments/templates/splitpayments/_api_docs.html b/lnbits/extensions/splitpayments/templates/splitpayments/_api_docs.html
index 4cf7190c3..4b5ed979c 100644
--- a/lnbits/extensions/splitpayments/templates/splitpayments/_api_docs.html
+++ b/lnbits/extensions/splitpayments/templates/splitpayments/_api_docs.html
@@ -28,6 +28,12 @@
label="API info"
:content-inset-level="0.5"
>
+
+
diff --git a/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html b/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html
index b839c641d..db3b24771 100644
--- a/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html
+++ b/lnbits/extensions/subdomains/templates/subdomains/_api_docs.html
@@ -22,5 +22,6 @@
>
+
diff --git a/lnbits/extensions/tipjar/templates/tipjar/_api_docs.html b/lnbits/extensions/tipjar/templates/tipjar/_api_docs.html
index 95ba6e064..cfb8136b4 100644
--- a/lnbits/extensions/tipjar/templates/tipjar/_api_docs.html
+++ b/lnbits/extensions/tipjar/templates/tipjar/_api_docs.html
@@ -12,4 +12,5 @@
>
+
diff --git a/lnbits/extensions/tpos/templates/tpos/_api_docs.html b/lnbits/extensions/tpos/templates/tpos/_api_docs.html
index 8930d9903..cbb21be13 100644
--- a/lnbits/extensions/tpos/templates/tpos/_api_docs.html
+++ b/lnbits/extensions/tpos/templates/tpos/_api_docs.html
@@ -4,6 +4,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html
index 34b3c39b0..886589e66 100644
--- a/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html
+++ b/lnbits/extensions/usermanager/templates/usermanager/_api_docs.html
@@ -28,6 +28,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html b/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html
index b421186ad..94b44a443 100644
--- a/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html
+++ b/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html
@@ -20,6 +20,7 @@
label="API info"
:content-inset-level="0.5"
>
+
diff --git a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html
index 095aad3ad..ff88189d5 100644
--- a/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html
+++ b/lnbits/extensions/withdraw/templates/withdraw/_api_docs.html
@@ -4,6 +4,7 @@
label="API info"
:content-inset-level="0.5"
>
+
Date: Wed, 27 Jul 2022 19:36:32 +0100
Subject: [PATCH 79/84] disabled lnurlpayout again
---
.../extensions/lnurlpayout/{config.json => config.json.example} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename lnbits/extensions/lnurlpayout/{config.json => config.json.example} (100%)
diff --git a/lnbits/extensions/lnurlpayout/config.json b/lnbits/extensions/lnurlpayout/config.json.example
similarity index 100%
rename from lnbits/extensions/lnurlpayout/config.json
rename to lnbits/extensions/lnurlpayout/config.json.example
From 354fedc0b8ca9e5cb27ff085d2b67d30fe09498e Mon Sep 17 00:00:00 2001
From: ben
Date: Wed, 27 Jul 2022 20:16:32 +0100
Subject: [PATCH 80/84] prettier/black
---
lnbits/extensions/events/views_api.py | 3 ++-
.../lnurlpayout/templates/lnurlpayout/_api_docs.html | 8 +-------
result | 1 +
3 files changed, 4 insertions(+), 8 deletions(-)
create mode 120000 result
diff --git a/lnbits/extensions/events/views_api.py b/lnbits/extensions/events/views_api.py
index 45ee4de05..56e6b06ca 100644
--- a/lnbits/extensions/events/views_api.py
+++ b/lnbits/extensions/events/views_api.py
@@ -133,7 +133,8 @@ async def api_ticket_send_ticket(event_id, payment_hash, data: CreateTicket):
if not ticket:
raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail=f"Event could not be fetched."
+ status_code=HTTPStatus.NOT_FOUND,
+ detail=f"Event could not be fetched.",
)
return {"paid": True, "ticket_id": ticket.id}
diff --git a/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html b/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html
index 4f921bb57..afe24c423 100644
--- a/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html
+++ b/lnbits/extensions/lnurlpayout/templates/lnurlpayout/_api_docs.html
@@ -4,12 +4,7 @@
label="API info"
:content-inset-level="0.5"
>
-
+
@@ -38,7 +33,6 @@
expand-separator
label="Create a lnurlpayout"
>
-
Date: Thu, 28 Jul 2022 09:30:52 +0100
Subject: [PATCH 81/84] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index f0f9df627..5c6b702a9 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ LNbits

-# 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 82/84] 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 f26e6c386..fb114ff8d 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 83/84] 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 fb114ff8d..0d9afdac7 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 84/84] 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 b1366b077..623b979a8 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,