From 8e06d779e7068e7446d19136526a971adda89661 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Fri, 23 Sep 2022 15:56:14 +0200
Subject: [PATCH 01/57] add dependencies for corelightning and lndgrpc (#1003)
* add dependencies for corelightning and lndgrpc
* added wrong cln library, :facepalm:
---
.github/workflows/regtest.yml | 2 -
docs/guide/wallets.md | 4 -
poetry.lock | 415 +++++++++++++++++++++++++++-------
pyproject.toml | 3 +
4 files changed, 335 insertions(+), 89 deletions(-)
diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml
index 0029eed47..6572ccdb6 100644
--- a/.github/workflows/regtest.yml
+++ b/.github/workflows/regtest.yml
@@ -65,7 +65,6 @@ jobs:
- name: Install dependencies
run: |
poetry install
- poetry add grpcio protobuf
- name: Run tests
env:
PYTHONUNBUFFERED: 1
@@ -106,7 +105,6 @@ jobs:
- name: Install dependencies
run: |
poetry install
- poetry add pyln-client
- name: Run tests
env:
PYTHONUNBUFFERED: 1
diff --git a/docs/guide/wallets.md b/docs/guide/wallets.md
index 592c29ef1..80fb54c04 100644
--- a/docs/guide/wallets.md
+++ b/docs/guide/wallets.md
@@ -15,8 +15,6 @@ A backend wallet can be configured using the following LNbits environment variab
### CoreLightning
-Using this wallet requires the installation of the `pylightning` Python package.
-
- `LNBITS_BACKEND_WALLET_CLASS`: **CoreLightningWallet**
- `CORELIGHTNING_RPC`: /file/path/lightning-rpc
@@ -39,8 +37,6 @@ or
### LND (gRPC)
-Using this wallet requires the installation of the `grpcio` and `protobuf` Python packages.
-
- `LNBITS_BACKEND_WALLET_CLASS`: **LndWallet**
- `LND_GRPC_ENDPOINT`: ip_address
- `LND_GRPC_PORT`: port
diff --git a/poetry.lock b/poetry.lock
index 38f08575b..ea83e25e2 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -20,8 +20,8 @@ sniffio = ">=1.1"
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[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)"]
+doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
+test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"]
trio = ["trio (>=0.16)"]
[[package]]
@@ -36,15 +36,15 @@ python-versions = ">=3.6"
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
-tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
+tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"]
[[package]]
-name = "atomicwrites"
-version = "1.4.1"
-description = "Atomic file writes."
-category = "dev"
+name = "asn1crypto"
+version = "1.5.1"
+description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP"
+category = "main"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+python-versions = "*"
[[package]]
name = "attrs"
@@ -55,10 +55,21 @@ 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"]
+dev = ["coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"]
+docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"]
+tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"]
+
+[[package]]
+name = "base58"
+version = "2.1.1"
+description = "Base58 and Base58Check implementation."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+tests = ["PyHamcrest (>=2.0.2)", "mypy", "pytest (>=4.6)", "pytest-benchmark", "pytest-cov", "pytest-flake8"]
[[package]]
name = "bech32"
@@ -78,7 +89,7 @@ python-versions = "*"
[[package]]
name = "black"
-version = "22.6.0"
+version = "22.8.0"
description = "The uncompromising code formatter."
category = "dev"
optional = false
@@ -149,6 +160,18 @@ python-versions = ">=3.6"
colorama = {version = "*", markers = "platform_system == \"Windows\""}
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
+[[package]]
+name = "coincurve"
+version = "17.0.0"
+description = "Cross-platform Python CFFI bindings for libsecp256k1"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+asn1crypto = "*"
+cffi = ">=1.3.0"
+
[[package]]
name = "colorama"
version = "0.4.5"
@@ -171,6 +194,25 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1
[package.extras]
toml = ["tomli"]
+[[package]]
+name = "cryptography"
+version = "36.0.2"
+description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+cffi = ">=1.12"
+
+[package.extras]
+docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"]
+docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
+pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
+sdist = ["setuptools_rust (>=0.11.4)"]
+ssh = ["bcrypt (>=3.1.5)"]
+test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"]
+
[[package]]
name = "ecdsa"
version = "0.17.0"
@@ -215,10 +257,10 @@ 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"]
+dev = ["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)", "pytest", "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"]
+tests = ["dj-database-url", "dj-email-url", "django-cache-url", "pytest"]
[[package]]
name = "fastapi"
@@ -233,10 +275,24 @@ pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.
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)"]
+all = ["email_validator (>=1.1.1,<2.0.0)", "itsdangerous (>=1.1.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "orjson (>=3.2.1,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<7.0.0)", "requests (>=2.24.0,<3.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)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
+dev = ["autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "pre-commit (>=2.17.0,<3.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"]
+doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer (>=0.4.1,<0.5.0)"]
+test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==22.3.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "email_validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "orjson (>=3.2.1,<4.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "types-dataclasses (==0.6.5)", "types-orjson (==3.6.2)", "types-ujson (==4.2.1)", "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)"]
+
+[[package]]
+name = "grpcio"
+version = "1.49.1"
+description = "HTTP/2-based RPC framework"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+six = ">=1.5.2"
+
+[package.extras]
+protobuf = ["grpcio-tools (>=1.49.1)"]
[[package]]
name = "h11"
@@ -290,8 +346,8 @@ rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
sniffio = "*"
[package.extras]
-brotli = ["brotlicffi", "brotli"]
-cli = ["click (>=8.0.0,<9.0.0)", "rich (>=10,<13)", "pygments (>=2.0.0,<3.0.0)"]
+brotli = ["brotli", "brotlicffi"]
+cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (>=1.0.0,<2.0.0)"]
@@ -316,9 +372,9 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5"
[package.extras]
-docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"]
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)"]
+testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy", "pytest-perf (>=0.9.2)"]
[[package]]
name = "iniconfig"
@@ -337,10 +393,10 @@ optional = false
python-versions = ">=3.6.1,<4.0"
[package.extras]
-pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
-requirements_deprecated_finder = ["pipreqs", "pip-api"]
colors = ["colorama (>=0.4.3,<0.5.0)"]
+pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
plugins = ["setuptools"]
+requirements_deprecated_finder = ["pip-api", "pipreqs"]
[[package]]
name = "jinja2"
@@ -382,7 +438,7 @@ 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)"]
+dev = ["Sphinx (>=2.2.1)", "black (>=19.10b0)", "codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)", "tox-travis (>=0.12)"]
[[package]]
name = "markupsafe"
@@ -404,9 +460,9 @@ python-versions = ">=3.7"
packaging = ">=17.0"
[package.extras]
-dev = ["pytest", "pytz", "simplejson", "mypy (==0.961)", "flake8 (==4.0.1)", "flake8-bugbear (==22.6.22)", "pre-commit (>=2.4,<3.0)", "tox"]
-docs = ["sphinx (==4.5.0)", "sphinx-issues (==3.0.1)", "alabaster (==0.7.12)", "sphinx-version-warning (==1.1.2)", "autodocsumm (==0.2.8)"]
-lint = ["mypy (==0.961)", "flake8 (==4.0.1)", "flake8-bugbear (==22.6.22)", "pre-commit (>=2.4,<3.0)"]
+dev = ["flake8 (==4.0.1)", "flake8-bugbear (==22.6.22)", "mypy (==0.961)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"]
+docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.8)", "sphinx (==4.5.0)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"]
+lint = ["flake8 (==4.0.1)", "flake8-bugbear (==22.6.22)", "mypy (==0.961)", "pre-commit (>=2.4,<3.0)"]
tests = ["pytest", "pytz", "simplejson"]
[[package]]
@@ -418,7 +474,7 @@ optional = false
python-versions = ">=3.6"
[package.extras]
-build = ["twine", "wheel", "blurb"]
+build = ["blurb", "twine", "wheel"]
docs = ["sphinx"]
test = ["pytest (<5.4)", "pytest-cov"]
@@ -484,11 +540,11 @@ six = "*"
[[package]]
name = "pathspec"
-version = "0.9.0"
+version = "0.10.1"
description = "Utility library for gitignore style pattern matching of file paths."
category = "dev"
optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+python-versions = ">=3.7"
[[package]]
name = "platformdirs"
@@ -499,8 +555,8 @@ optional = false
python-versions = ">=3.7"
[package.extras]
-docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"]
-test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"]
+docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"]
+test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"]
[[package]]
name = "pluggy"
@@ -514,8 +570,16 @@ python-versions = ">=3.6"
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
[package.extras]
-testing = ["pytest-benchmark", "pytest"]
-dev = ["tox", "pre-commit"]
+dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
+
+[[package]]
+name = "protobuf"
+version = "4.21.6"
+description = ""
+category = "main"
+optional = false
+python-versions = ">=3.7"
[[package]]
name = "psycopg2-binary"
@@ -564,6 +628,41 @@ typing-extensions = ">=3.7.4.3"
dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"]
+[[package]]
+name = "pyln-bolt7"
+version = "1.0.246"
+description = "BOLT7"
+category = "main"
+optional = false
+python-versions = ">=3.7,<4.0"
+
+[[package]]
+name = "pyln-client"
+version = "0.12.0.post1"
+description = "Client library and plugin library for Core Lightning"
+category = "main"
+optional = false
+python-versions = ">=3.7,<4.0"
+
+[package.dependencies]
+pyln-bolt7 = ">=1.0,<2.0"
+pyln-proto = ">=0.11,<0.12"
+
+[[package]]
+name = "pyln-proto"
+version = "0.11.1"
+description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)."
+category = "main"
+optional = false
+python-versions = ">=3.7,<4.0"
+
+[package.dependencies]
+base58 = ">=2.1.1,<3.0.0"
+bitstring = ">=3.1.9,<4.0.0"
+coincurve = ">=17.0.0,<18.0.0"
+cryptography = ">=36.0.1,<37.0.0"
+PySocks = ">=1.7.1,<2.0.0"
+
[[package]]
name = "pyparsing"
version = "3.0.9"
@@ -573,7 +672,7 @@ optional = false
python-versions = ">=3.6.8"
[package.extras]
-diagrams = ["railroad-diagrams", "jinja2"]
+diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pypng"
@@ -607,16 +706,23 @@ enum34 = "*"
pathlib2 = "*"
six = "*"
+[[package]]
+name = "pysocks"
+version = "1.7.1"
+description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
[[package]]
name = "pytest"
-version = "7.1.2"
+version = "7.1.3"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
-atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
@@ -642,7 +748,7 @@ pytest = ">=6.1.0"
typing-extensions = {version = ">=3.7.2", markers = "python_version < \"3.8\""}
[package.extras]
-testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)", "flaky (>=3.5.0)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
+testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
[[package]]
name = "pytest-cov"
@@ -657,7 +763,7 @@ coverage = {version = ">=5.2.1", extras = ["toml"]}
pytest = ">=4.6"
[package.extras]
-testing = ["virtualenv", "pytest-xdist", "six", "process-tests", "hunter", "fields"]
+testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
[[package]]
name = "python-dotenv"
@@ -690,7 +796,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
six = ">=1.8.0"
[package.extras]
-test = ["ipython", "pytest (>=3.0.5)", "mock"]
+test = ["ipython", "mock", "pytest (>=3.0.5)"]
[[package]]
name = "rfc3986"
@@ -759,7 +865,7 @@ postgresql = ["psycopg2"]
postgresql_pg8000 = ["pg8000 (<1.16.6)"]
postgresql_psycopg2binary = ["psycopg2-binary"]
postgresql_psycopg2cffi = ["psycopg2cffi"]
-pymysql = ["pymysql (<1)", "pymysql"]
+pymysql = ["pymysql", "pymysql (<1)"]
[[package]]
name = "sqlalchemy-aio"
@@ -820,7 +926,7 @@ python-versions = ">=3.6"
[[package]]
name = "types-protobuf"
-version = "3.19.22"
+version = "3.20.4"
description = "Typing stubs for protobuf"
category = "dev"
optional = false
@@ -848,7 +954,7 @@ h11 = ">=0.8"
typing-extensions = {version = "*", markers = "python_version < \"3.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)"]
+standard = ["PyYAML (>=5.1)", "colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"]
[[package]]
name = "uvloop"
@@ -859,9 +965,9 @@ 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)"]
+dev = ["Cython (>=0.29.24,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.7.0,<2.8.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"]
+docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"]
+test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.7.0,<2.8.0)"]
[[package]]
name = "watchgod"
@@ -912,13 +1018,13 @@ 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"]
+docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"]
+testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"]
[metadata]
lock-version = "1.1"
-python-versions = "^3.9 | ^3.8 | ^3.7"
-content-hash = "ac8c4117d537aaf8853d35038f2821ea4bc04b29c7971b91fd46329365008b95"
+python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
+content-hash = "d0556d4a307864ba04a1e5da517884e523396c98a00ae09d9192c37b1d2c555b"
[metadata.files]
aiofiles = [
@@ -933,13 +1039,18 @@ asgiref = [
{file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
{file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"},
]
-atomicwrites = [
- {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"},
+asn1crypto = [
+ {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"},
+ {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"},
]
attrs = [
{file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
{file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
]
+base58 = [
+ {file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"},
+ {file = "base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c"},
+]
bech32 = [
{file = "bech32-1.2.0-py3-none-any.whl", hash = "sha256:990dc8e5a5e4feabbdf55207b5315fdd9b73db40be294a19b3752cde9e79d981"},
{file = "bech32-1.2.0.tar.gz", hash = "sha256:7d6db8214603bd7871fcfa6c0826ef68b85b0abd90fa21c285a9c5e21d2bd899"},
@@ -950,29 +1061,29 @@ bitstring = [
{file = "bitstring-3.1.9.tar.gz", hash = "sha256:a5848a3f63111785224dca8bb4c0a75b62ecdef56a042c8d6be74b16f7e860e7"},
]
black = [
- {file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"},
- {file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"},
- {file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"},
- {file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"},
- {file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"},
- {file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"},
- {file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"},
- {file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"},
- {file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"},
- {file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"},
- {file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"},
- {file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"},
- {file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"},
- {file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"},
- {file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"},
- {file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"},
- {file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"},
- {file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"},
- {file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"},
- {file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"},
- {file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"},
- {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"},
- {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"},
+ {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"},
+ {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"},
+ {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"},
+ {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"},
+ {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"},
+ {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"},
+ {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"},
+ {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"},
+ {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"},
+ {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"},
+ {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"},
+ {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"},
+ {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"},
+ {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"},
+ {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"},
+ {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"},
+ {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"},
+ {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"},
+ {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"},
+ {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"},
+ {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"},
+ {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"},
+ {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"},
]
cerberus = [
{file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"},
@@ -1041,6 +1152,42 @@ click = [
{file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
{file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
]
+coincurve = [
+ {file = "coincurve-17.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac8c87d6fd080faa74e7ecf64a6ed20c11a254863238759eb02c3f13ad12b0c4"},
+ {file = "coincurve-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:25dfa105beba24c8de886f8ed654bb1133866e4e22cfd7ea5ad8438cae6ed924"},
+ {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:698efdd53e4fe1bbebaee9b75cbc851be617974c1c60098e9145cb7198ae97fb"},
+ {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30dd44d1039f1d237aaa2da6d14a455ca88df3bcb00610b41f3253fdca1be97b"},
+ {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d154e2eb5711db8c5ef52fcd80935b5a0e751c057bc6ffb215a7bb409aedef03"},
+ {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c71caffb97dd3d0c243beb62352669b1e5dafa3a4bccdbb27d36bd82f5e65d20"},
+ {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:747215254e51dd4dfbe6dded9235491263da5d88fe372d66541ca16b51ea078f"},
+ {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad2f6df39ba1e2b7b14bb984505ffa7d0a0ecdd697e8d7dbd19e04bc245c87ed"},
+ {file = "coincurve-17.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0503326963916c85b61d16f611ea0545f03c9e418fa8007c233c815429e381e8"},
+ {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1013c1597b65684ae1c3e42497f9ef5a04527fa6136a84a16b34602606428c74"},
+ {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4beef321fd6434448aab03a0c245f31c4e77f43b54b82108c0948d29852ac7e"},
+ {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f47806527d3184da3e8b146fac92a8ed567bbd225194f4517943d8cdc85f9542"},
+ {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:51e56373ac79f4ec1cfc5da53d72c55f5e5ac28d848b0849ef5e687ace857888"},
+ {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3d694ad194bee9e8792e2e75879dc5238d8a184010cde36c5ad518fcfe2cd8f2"},
+ {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:74cedb3d3a1dc5abe0c9c2396e1b82cc64496babc5b42e007e72e185cb1edad8"},
+ {file = "coincurve-17.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:db874c5c1dcb1f3a19379773b5e8cffc777625a7a7a60dd9a67206e31e62e2e9"},
+ {file = "coincurve-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:896b01941254f0a218cf331a9bddfe2d43892f7f1ba10d6e372e2eb744a744c2"},
+ {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6aec70238dbe7a5d66b5f9438ff45b08eb5e0990d49c32ebb65247c5d5b89d7a"},
+ {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24284d17162569df917a640f19d9654ba3b43cf560ced8864f270da903f73a5"},
+ {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ea057f777842396d387103c606babeb3a1b4c6126769cc0a12044312fc6c465"},
+ {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b88642edf7f281649b0c0b6ffade051945ccceae4b885e40445634877d0b3049"},
+ {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a80a207131813b038351c5bdae8f20f5f774bbf53622081f208d040dd2b7528f"},
+ {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1ef72574aa423bc33665ef4be859164a478bad24d48442da874ef3dc39a474d"},
+ {file = "coincurve-17.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfd4fab857bcd975edc39111cb5f5c104f138dac2e9ace35ea8434d37bcea3be"},
+ {file = "coincurve-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:73f39579dd651a9fc29da5a8fc0d8153d872bcbc166f876457baced1a1c01501"},
+ {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8852dc01af4f0fe941ffd04069f7e4fecdce9b867a016f823a02286a1a1f07b5"},
+ {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1bef812da1da202cdd601a256825abcf26d86e8634fac3ec3e615e3bb3ff08c"},
+ {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abbefc9ccb170cb255a31df32457c2e43084b9f37589d0694dacc2dea6ddaf7c"},
+ {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:abbd9d017a7638dc38a3b9bb4851f8801b7818d4e5ac22e0c75e373b3c1dbff0"},
+ {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e2c2e8a1f0b1f8e48049c891af4ae3cad65d115d358bde72f6b8abdbb8a23170"},
+ {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8c571445b166c714af4f8155e38a894376c16c0431e88963f2fff474a9985d87"},
+ {file = "coincurve-17.0.0-py3-none-win32.whl", hash = "sha256:b956b0b2c85e25a7d00099970ff5d8338254b45e46f0a940f4a2379438ce0dde"},
+ {file = "coincurve-17.0.0-py3-none-win_amd64.whl", hash = "sha256:630388080da3026e0b0176cc6762eaabecba857ee3fc85767577dea063ea7c6e"},
+ {file = "coincurve-17.0.0.tar.gz", hash = "sha256:68da55aff898702952fda3ee04fd6ed60bb6b91f919c69270786ed766b548b93"},
+]
colorama = [
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
@@ -1097,6 +1244,28 @@ coverage = [
{file = "coverage-6.4.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1"},
{file = "coverage-6.4.4.tar.gz", hash = "sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58"},
]
+cryptography = [
+ {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:4e2dddd38a5ba733be6a025a1475a9f45e4e41139d1321f412c6b360b19070b6"},
+ {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:4881d09298cd0b669bb15b9cfe6166f16fc1277b4ed0d04a22f3d6430cb30f1d"},
+ {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea634401ca02367c1567f012317502ef3437522e2fc44a3ea1844de028fa4b84"},
+ {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7be666cc4599b415f320839e36367b273db8501127b38316f3b9f22f17a0b815"},
+ {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8241cac0aae90b82d6b5c443b853723bcc66963970c67e56e71a2609dc4b5eaf"},
+ {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2d54e787a884ffc6e187262823b6feb06c338084bbe80d45166a1cb1c6c5bf"},
+ {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:c2c5250ff0d36fd58550252f54915776940e4e866f38f3a7866d92b32a654b86"},
+ {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ec6597aa85ce03f3e507566b8bcdf9da2227ec86c4266bd5e6ab4d9e0cc8dab2"},
+ {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ca9f686517ec2c4a4ce930207f75c00bf03d94e5063cbc00a1dc42531511b7eb"},
+ {file = "cryptography-36.0.2-cp36-abi3-win32.whl", hash = "sha256:f64b232348ee82f13aac22856515ce0195837f6968aeaa94a3d0353ea2ec06a6"},
+ {file = "cryptography-36.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:53e0285b49fd0ab6e604f4c5d9c5ddd98de77018542e88366923f152dbeb3c29"},
+ {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:32db5cc49c73f39aac27574522cecd0a4bb7384e71198bc65a0d23f901e89bb7"},
+ {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b3d199647468d410994dbeb8cec5816fb74feb9368aedf300af709ef507e3e"},
+ {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:da73d095f8590ad437cd5e9faf6628a218aa7c387e1fdf67b888b47ba56a17f0"},
+ {file = "cryptography-36.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0a3bf09bb0b7a2c93ce7b98cb107e9170a90c51a0162a20af1c61c765b90e60b"},
+ {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8897b7b7ec077c819187a123174b645eb680c13df68354ed99f9b40a50898f77"},
+ {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82740818f2f240a5da8dfb8943b360e4f24022b093207160c77cadade47d7c85"},
+ {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1f64a62b3b75e4005df19d3b5235abd43fa6358d5516cfc43d87aeba8d08dd51"},
+ {file = "cryptography-36.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e167b6b710c7f7bc54e67ef593f8731e1f45aa35f8a8a7b72d6e42ec76afd4b3"},
+ {file = "cryptography-36.0.2.tar.gz", hash = "sha256:70f8f4f7bb2ac9f340655cbac89d68c527af5bb4387522a8413e841e3e6628c9"},
+]
ecdsa = [
{file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"},
{file = "ecdsa-0.17.0.tar.gz", hash = "sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"},
@@ -1117,6 +1286,53 @@ fastapi = [
{file = "fastapi-0.78.0-py3-none-any.whl", hash = "sha256:15fcabd5c78c266fa7ae7d8de9b384bfc2375ee0503463a6febbe3bab69d6f65"},
{file = "fastapi-0.78.0.tar.gz", hash = "sha256:3233d4a789ba018578658e2af1a4bb5e38bdd122ff722b313666a9b2c6786a83"},
]
+grpcio = [
+ {file = "grpcio-1.49.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:fd86040232e805b8e6378b2348c928490ee595b058ce9aaa27ed8e4b0f172b20"},
+ {file = "grpcio-1.49.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6fd0c9cede9552bf00f8c5791d257d5bf3790d7057b26c59df08be5e7a1e021d"},
+ {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:d0d402e158d4e84e49c158cb5204119d55e1baf363ee98d6cb5dce321c3a065d"},
+ {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ceec743d42a627e64ea266059a62d214c5a3cdfcd0d7fe2b7a8e4e82527c7"},
+ {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2106d9c16527f0a85e2eea6e6b91a74fc99579c60dd810d8690843ea02bc0f5f"},
+ {file = "grpcio-1.49.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:52dd02b7e7868233c571b49bc38ebd347c3bb1ff8907bb0cb74cb5f00c790afc"},
+ {file = "grpcio-1.49.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:120fecba2ec5d14b5a15d11063b39783fda8dc8d24addd83196acb6582cabd9b"},
+ {file = "grpcio-1.49.1-cp310-cp310-win32.whl", hash = "sha256:f1a3b88e3c53c1a6e6bed635ec1bbb92201bb6a1f2db186179f7f3f244829788"},
+ {file = "grpcio-1.49.1-cp310-cp310-win_amd64.whl", hash = "sha256:a7d0017b92d3850abea87c1bdec6ea41104e71c77bca44c3e17f175c6700af62"},
+ {file = "grpcio-1.49.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:9fb17ff8c0d56099ac6ebfa84f670c5a62228d6b5c695cf21c02160c2ac1446b"},
+ {file = "grpcio-1.49.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:075f2d06e3db6b48a2157a1bcd52d6cbdca980dd18988fe6afdb41795d51625f"},
+ {file = "grpcio-1.49.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46d93a1b4572b461a227f1db6b8d35a88952db1c47e5fadcf8b8a2f0e1dd9201"},
+ {file = "grpcio-1.49.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc79b2b37d779ac42341ddef40ad5bf0966a64af412c89fc2b062e3ddabb093f"},
+ {file = "grpcio-1.49.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5f8b3a971c7820ea9878f3fd70086240a36aeee15d1b7e9ecbc2743b0e785568"},
+ {file = "grpcio-1.49.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49b301740cf5bc8fed4fee4c877570189ae3951432d79fa8e524b09353659811"},
+ {file = "grpcio-1.49.1-cp311-cp311-win32.whl", hash = "sha256:1c66a25afc6c71d357867b341da594a5587db5849b48f4b7d5908d236bb62ede"},
+ {file = "grpcio-1.49.1-cp311-cp311-win_amd64.whl", hash = "sha256:6b6c3a95d27846f4145d6967899b3ab25fffc6ae99544415e1adcacef84842d2"},
+ {file = "grpcio-1.49.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:1cc400c8a2173d1c042997d98a9563e12d9bb3fb6ad36b7f355bc77c7663b8af"},
+ {file = "grpcio-1.49.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:34f736bd4d0deae90015c0e383885b431444fe6b6c591dea288173df20603146"},
+ {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:196082b9c89ebf0961dcd77cb114bed8171964c8e3063b9da2fb33536a6938ed"},
+ {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c9f89c42749890618cd3c2464e1fbf88446e3d2f67f1e334c8e5db2f3272bbd"},
+ {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64419cb8a5b612cdb1550c2fd4acbb7d4fb263556cf4625f25522337e461509e"},
+ {file = "grpcio-1.49.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8a5272061826e6164f96e3255405ef6f73b88fd3e8bef464c7d061af8585ac62"},
+ {file = "grpcio-1.49.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ea9d0172445241ad7cb49577314e39d0af2c5267395b3561d7ced5d70458a9f3"},
+ {file = "grpcio-1.49.1-cp37-cp37m-win32.whl", hash = "sha256:2070e87d95991473244c72d96d13596c751cb35558e11f5df5414981e7ed2492"},
+ {file = "grpcio-1.49.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fcedcab49baaa9db4a2d240ac81f2d57eb0052b1c6a9501b46b8ae912720fbf"},
+ {file = "grpcio-1.49.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:afbb3475cf7f4f7d380c2ca37ee826e51974f3e2665613996a91d6a58583a534"},
+ {file = "grpcio-1.49.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a4f9ba141380abde6c3adc1727f21529137a2552002243fa87c41a07e528245c"},
+ {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:cf0a1fb18a7204b9c44623dfbd1465b363236ce70c7a4ed30402f9f60d8b743b"},
+ {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17bb6fe72784b630728c6cff9c9d10ccc3b6d04e85da6e0a7b27fb1d135fac62"},
+ {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18305d5a082d1593b005a895c10041f833b16788e88b02bb81061f5ebcc465df"},
+ {file = "grpcio-1.49.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b6a1b39e59ac5a3067794a0e498911cf2e37e4b19ee9e9977dc5e7051714f13f"},
+ {file = "grpcio-1.49.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e20d59aafc086b1cc68400463bddda6e41d3e5ed30851d1e2e0f6a2e7e342d3"},
+ {file = "grpcio-1.49.1-cp38-cp38-win32.whl", hash = "sha256:e1e83233d4680863a421f3ee4a7a9b80d33cd27ee9ed7593bc93f6128302d3f2"},
+ {file = "grpcio-1.49.1-cp38-cp38-win_amd64.whl", hash = "sha256:221d42c654d2a41fa31323216279c73ed17d92f533bc140a3390cc1bd78bf63c"},
+ {file = "grpcio-1.49.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:fa9e6e61391e99708ac87fc3436f6b7b9c6b845dc4639b406e5e61901e1aacde"},
+ {file = "grpcio-1.49.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9b449e966ef518ce9c860d21f8afe0b0f055220d95bc710301752ac1db96dd6a"},
+ {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:aa34d2ad9f24e47fa9a3172801c676e4037d862247e39030165fe83821a7aafd"},
+ {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5207f4eed1b775d264fcfe379d8541e1c43b878f2b63c0698f8f5c56c40f3d68"},
+ {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b24a74651438d45619ac67004638856f76cc13d78b7478f2457754cbcb1c8ad"},
+ {file = "grpcio-1.49.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fe763781669790dc8b9618e7e677c839c87eae6cf28b655ee1fa69ae04eea03f"},
+ {file = "grpcio-1.49.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2f2ff7ba0f8f431f32d4b4bc3a3713426949d3533b08466c4ff1b2b475932ca8"},
+ {file = "grpcio-1.49.1-cp39-cp39-win32.whl", hash = "sha256:08ff74aec8ff457a89b97152d36cb811dcc1d17cd5a92a65933524e363327394"},
+ {file = "grpcio-1.49.1-cp39-cp39-win_amd64.whl", hash = "sha256:274ffbb39717918c514b35176510ae9be06e1d93121e84d50b350861dcb9a705"},
+ {file = "grpcio-1.49.1.tar.gz", hash = "sha256:d4725fc9ec8e8822906ae26bb26f5546891aa7fbc3443de970cc556d43a5c99f"},
+]
h11 = [
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
{file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
@@ -1314,8 +1530,8 @@ pathlib2 = [
{file = "pathlib2-2.3.7.post1.tar.gz", hash = "sha256:9fe0edad898b83c0c3e199c842b27ed216645d2e177757b2dd67384d4113c641"},
]
pathspec = [
- {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
- {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
+ {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"},
+ {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"},
]
platformdirs = [
{file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
@@ -1325,6 +1541,22 @@ pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
+protobuf = [
+ {file = "protobuf-4.21.6-cp310-abi3-win32.whl", hash = "sha256:49f88d56a9180dbb7f6199c920f5bb5c1dd0172f672983bb281298d57c2ac8eb"},
+ {file = "protobuf-4.21.6-cp310-abi3-win_amd64.whl", hash = "sha256:7a6cc8842257265bdfd6b74d088b829e44bcac3cca234c5fdd6052730017b9ea"},
+ {file = "protobuf-4.21.6-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:ba596b9ffb85c909fcfe1b1a23136224ed678af3faf9912d3fa483d5f9813c4e"},
+ {file = "protobuf-4.21.6-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4143513c766db85b9d7c18dbf8339673c8a290131b2a0fe73855ab20770f72b0"},
+ {file = "protobuf-4.21.6-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:b6cea204865595a92a7b240e4b65bcaaca3ad5d2ce25d9db3756eba06041138e"},
+ {file = "protobuf-4.21.6-cp37-cp37m-win32.whl", hash = "sha256:9666da97129138585b26afcb63ad4887f602e169cafe754a8258541c553b8b5d"},
+ {file = "protobuf-4.21.6-cp37-cp37m-win_amd64.whl", hash = "sha256:308173d3e5a3528787bb8c93abea81d5a950bdce62840d9760effc84127fb39c"},
+ {file = "protobuf-4.21.6-cp38-cp38-win32.whl", hash = "sha256:aa29113ec901281f29d9d27b01193407a98aa9658b8a777b0325e6d97149f5ce"},
+ {file = "protobuf-4.21.6-cp38-cp38-win_amd64.whl", hash = "sha256:8f9e60f7d44592c66e7b332b6a7b4b6e8d8b889393c79dbc3a91f815118f8eac"},
+ {file = "protobuf-4.21.6-cp39-cp39-win32.whl", hash = "sha256:80e6540381080715fddac12690ee42d087d0d17395f8d0078dfd6f1181e7be4c"},
+ {file = "protobuf-4.21.6-cp39-cp39-win_amd64.whl", hash = "sha256:77b355c8604fe285536155286b28b0c4cbc57cf81b08d8357bf34829ea982860"},
+ {file = "protobuf-4.21.6-py2.py3-none-any.whl", hash = "sha256:07a0bb9cc6114f16a39c866dc28b6e3d96fa4ffb9cc1033057412547e6e75cb9"},
+ {file = "protobuf-4.21.6-py3-none-any.whl", hash = "sha256:c7c864148a237f058c739ae7a05a2b403c0dfa4ce7d1f3e5213f352ad52d57c6"},
+ {file = "protobuf-4.21.6.tar.gz", hash = "sha256:6b1040a5661cd5f6e610cbca9cfaa2a17d60e2bb545309bc1b278bb05be44bdd"},
+]
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"},
@@ -1424,6 +1656,18 @@ pydantic = [
{file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"},
{file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"},
]
+pyln-bolt7 = [
+ {file = "pyln-bolt7-1.0.246.tar.gz", hash = "sha256:2b53744fa21c1b12d2c9c9df153651b122e38fa65d4a5c3f2957317ee148e089"},
+ {file = "pyln_bolt7-1.0.246-py3-none-any.whl", hash = "sha256:54d48ec27fdc8751762cb068b0a9f2757a58fb57933c6d8f8255d02c27eb63c5"},
+]
+pyln-client = [
+ {file = "pyln-client-0.12.0.post1.tar.gz", hash = "sha256:c80338e8e9f435720c0e5f552dc4016fc8fba16d4b79764f881067e0fcd5d5c7"},
+ {file = "pyln_client-0.12.0.post1-py3-none-any.whl", hash = "sha256:cfe3404eb88f294015145e668d774dd754b3baec36b44fe773fa354f1e1e48c1"},
+]
+pyln-proto = [
+ {file = "pyln-proto-0.11.1.tar.gz", hash = "sha256:9bed240f41917c4fd526b767218a77d0fbe69242876eef72c35a856796f922d6"},
+ {file = "pyln_proto-0.11.1-py3-none-any.whl", hash = "sha256:27b2e04a81b894f69018279c0ce4aa2e7ccd03b86dd9783f96b9d8d1498c8393"},
+]
pyparsing = [
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
@@ -1438,9 +1682,14 @@ pyqrcode = [
pyscss = [
{file = "pyScss-1.4.0.tar.gz", hash = "sha256:8f35521ffe36afa8b34c7d6f3195088a7057c185c2b8f15ee459ab19748669ff"},
]
+pysocks = [
+ {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"},
+ {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"},
+ {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"},
+]
pytest = [
- {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"},
- {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"},
+ {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"},
+ {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"},
]
pytest-asyncio = [
{file = "pytest-asyncio-0.19.0.tar.gz", hash = "sha256:ac4ebf3b6207259750bc32f4c1d8fcd7e79739edbc67ad0c58dd150b1d072fed"},
@@ -1612,8 +1861,8 @@ typed-ast = [
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
]
types-protobuf = [
- {file = "types-protobuf-3.19.22.tar.gz", hash = "sha256:d2b26861b0cb46a3c8669b0df507b7ef72e487da66d61f9f3576aa76ce028a83"},
- {file = "types_protobuf-3.19.22-py3-none-any.whl", hash = "sha256:d291388678af91bb045fafa864f142dc4ac22f5d4cdca097c7d8d8a32fa9b3ab"},
+ {file = "types-protobuf-3.20.4.tar.gz", hash = "sha256:0dad3a5009895c985a56e2837f61902bad9594151265ac0ee907bb16d0b01eb7"},
+ {file = "types_protobuf-3.20.4-py3-none-any.whl", hash = "sha256:5082437afe64ce3b31c8db109eae86e02fda11e4d5f9ac59cb8578a8a138aa70"},
]
typing-extensions = [
{file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"},
diff --git a/pyproject.toml b/pyproject.toml
index e95c6a2ee..7f833aa55 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -60,6 +60,9 @@ zipp = "3.5.0"
loguru = "0.5.3"
cffi = "1.15.0"
websocket-client = "1.3.3"
+grpcio = "^1.49.1"
+protobuf = "^4.21.6"
+pyln-client = "^0.12.0"
[tool.poetry.dev-dependencies]
isort = "^5.10.1"
From dc11205128f2cc9dafbe51d3449852959a340733 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Loun=C3=A8s=20Ksouri?=
Date: Fri, 23 Sep 2022 21:11:54 +0200
Subject: [PATCH 02/57] feat(docker): dynamic port and host using environment
variables (#1006)
---
Dockerfile | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/Dockerfile b/Dockerfile
index fed097d21..6259fe7b8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,13 +1,23 @@
FROM python:3.9-slim
+
RUN apt-get clean
RUN apt-get update
RUN apt-get install -y curl pkg-config build-essential
RUN curl -sSL https://install.python-poetry.org | python3 -
+
ENV PATH="/root/.local/bin:$PATH"
+
WORKDIR /app
+
COPY . .
+
RUN poetry config virtualenvs.create false
RUN poetry install --no-dev --no-root
RUN poetry run python build.py
+
+ENV LNBITS_PORT="5000"
+ENV LNBITS_HOST="0.0.0.0"
+
EXPOSE 5000
-CMD ["poetry", "run", "lnbits", "--port", "5000", "--host", "0.0.0.0"]
+
+CMD ["sh", "-c", "poetry run lnbits --port $LNBITS_PORT --host $LNBITS_HOST"]
From 6e405d1fc4073b34d880f6045c5c7ec3f2f7ccf0 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Wed, 21 Sep 2022 17:13:05 +0300
Subject: [PATCH 03/57] fix: output address on confirmation based on selected
network
---
lnbits/extensions/watchonly/models.py | 1 +
.../watchonly/static/components/payment/payment.js | 6 ++++--
lnbits/extensions/watchonly/templates/watchonly/index.html | 1 +
lnbits/extensions/watchonly/views_api.py | 4 +++-
4 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/watchonly/models.py b/lnbits/extensions/watchonly/models.py
index cedaa2101..622f5ec80 100644
--- a/lnbits/extensions/watchonly/models.py
+++ b/lnbits/extensions/watchonly/models.py
@@ -80,6 +80,7 @@ class CreatePsbt(BaseModel):
class ExtractPsbt(BaseModel):
psbtBase64 = "" # // todo snake case
inputs: List[TransactionInput]
+ network = "Mainnet"
class SignedTransaction(BaseModel):
diff --git a/lnbits/extensions/watchonly/static/components/payment/payment.js b/lnbits/extensions/watchonly/static/components/payment/payment.js
index f2cccbbaf..9f38df1d0 100644
--- a/lnbits/extensions/watchonly/static/components/payment/payment.js
+++ b/lnbits/extensions/watchonly/static/components/payment/payment.js
@@ -11,7 +11,8 @@ async function payment(path) {
'mempool-endpoint',
'sats-denominated',
'serial-signer-ref',
- 'adminkey'
+ 'adminkey',
+ 'network'
],
watch: {
immediate: true,
@@ -279,7 +280,8 @@ async function payment(path) {
this.adminkey,
{
psbtBase64,
- inputs: this.tx.inputs
+ inputs: this.tx.inputs,
+ network: this.network
}
)
return data
diff --git a/lnbits/extensions/watchonly/templates/watchonly/index.html b/lnbits/extensions/watchonly/templates/watchonly/index.html
index 263f6d92a..91577cd28 100644
--- a/lnbits/extensions/watchonly/templates/watchonly/index.html
+++ b/lnbits/extensions/watchonly/templates/watchonly/index.html
@@ -136,6 +136,7 @@
:adminkey="g.user.wallets[0].adminkey"
:serial-signer-ref="$refs.serialSigner"
:sats-denominated="config.sats_denominated"
+ :network="config.network"
@broadcast-done="handleBroadcastSuccess"
>
diff --git a/lnbits/extensions/watchonly/views_api.py b/lnbits/extensions/watchonly/views_api.py
index 77d28fee1..750d46c98 100644
--- a/lnbits/extensions/watchonly/views_api.py
+++ b/lnbits/extensions/watchonly/views_api.py
@@ -4,6 +4,7 @@ from http import HTTPStatus
import httpx
from embit import finalizer, script
from embit.ec import PublicKey
+from embit.networks import NETWORKS
from embit.psbt import PSBT, DerivationPath
from embit.transaction import Transaction, TransactionInput, TransactionOutput
from fastapi import Query, Request
@@ -295,6 +296,7 @@ async def api_psbt_create(
async def api_psbt_extract_tx(
data: ExtractPsbt, w: WalletTypeInfo = Depends(require_admin_key)
):
+ network = NETWORKS["main"] if data.network == "Mainnet" else NETWORKS["test"]
res = SignedTransaction()
try:
psbt = PSBT.from_base64(data.psbtBase64)
@@ -316,7 +318,7 @@ async def api_psbt_extract_tx(
for out in transaction.vout:
tx["outputs"].append(
- {"amount": out.value, "address": out.script_pubkey.address()}
+ {"amount": out.value, "address": out.script_pubkey.address(network)}
)
res.tx_json = json.dumps(tx)
except Exception as e:
From 80a62569f9164c8fffe6ec33a04df38234dd44d1 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Thu, 22 Sep 2022 10:41:54 +0300
Subject: [PATCH 04/57] fix: wait for connection to init
---
.../components/serial-signer/serial-signer.js | 25 ++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
index 33d07d94d..332612806 100644
--- a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
+++ b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
@@ -15,7 +15,7 @@ async function serialSigner(path) {
receivedData: '',
config: {},
decryptionKey: null,
- sharedSecret: null, // todo: store in secure local storage
+ sharedSecret: null,
hww: {
password: null,
@@ -57,13 +57,16 @@ async function serialSigner(path) {
computed: {
pairedDevices: {
+ cache: false,
get: function () {
+ console.log('### get pairedDevices')
return (
JSON.parse(window.localStorage.getItem('lnbits-paired-devices')) ||
[]
)
},
set: function (devices) {
+ console.log('### set pairedDevices', devices)
window.localStorage.setItem(
'lnbits-paired-devices',
JSON.stringify(devices)
@@ -81,6 +84,7 @@ async function serialSigner(path) {
await this.openSerialPort(this.config)
},
openSerialPort: async function (config = {baudRate: 9600}) {
+ console.log('### openSerialPort', config)
if (!this.checkSerialPortSupported()) return false
if (this.selectedPort) {
this.$q.notify({
@@ -109,7 +113,10 @@ async function serialSigner(path) {
// Wait for the serial port to open.
await this.selectedPort.open(config)
+ // do not await
this.startSerialPortReading()
+ // wait to init
+ sleep(1000)
const textEncoder = new TextEncoderStream()
this.writableStreamClosed = textEncoder.readable.pipeTo(
@@ -307,7 +314,11 @@ async function serialSigner(path) {
},
hwwPing: async function () {
try {
+ console.log('### hwwPing 1', window.location.host)
+ // Send an empty ping. The serial port buffer might have some jubk data. Flush it.
+ await this.sendCommandClearText(COMMAND_PING)
await this.sendCommandClearText(COMMAND_PING, [window.location.host])
+ console.log('### hwwPing 2')
} catch (error) {
this.$q.notify({
type: 'warning',
@@ -582,7 +593,7 @@ async function serialSigner(path) {
hwwCheckPairing: async function () {
const iv = window.crypto.getRandomValues(new Uint8Array(16))
const encrypted = await this.encryptMessage(
- this.sharedSecret,
+ this.sharedSecret, // todo: revisit
iv,
PAIRING_CONTROL_TEXT.length + ' ' + PAIRING_CONTROL_TEXT
)
@@ -724,6 +735,7 @@ async function serialSigner(path) {
},
hwwHelp: async function () {
try {
+ console.log('### cmd help')
await this.sendCommandSecure(COMMAND_HELP)
this.$q.notify({
type: 'positive',
@@ -746,7 +758,7 @@ async function serialSigner(path) {
} catch (error) {
this.$q.notify({
type: 'warning',
- message: 'Failed to ask for help!',
+ message: 'Failed to wipe!',
caption: `${error}`,
timeout: 10000
})
@@ -861,7 +873,13 @@ async function serialSigner(path) {
sendCommandSecure: async function (command, attrs = []) {
const message = [command].concat(attrs).join(' ')
+ console.log('### sendCommandSecure', message)
const iv = window.crypto.getRandomValues(new Uint8Array(16))
+ if (!this.sharedSecret || !this.sharedSecret.length) {
+ throw new Error(
+ `Secure connection not estabileshed. Tried to run command: ${command}`
+ )
+ }
const encrypted = await this.encryptMessage(
this.sharedSecret,
iv,
@@ -874,6 +892,7 @@ async function serialSigner(path) {
},
sendCommandClearText: async function (command, attrs = []) {
const message = [command].concat(attrs).join(' ')
+ console.log('### sendCommandClearText', message)
await this.writer.write(message + '\n')
},
extractCommand: async function (value) {
From d5f78e83d927c61071a709175a95692e06188c49 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Thu, 22 Sep 2022 10:42:27 +0300
Subject: [PATCH 05/57] chore: update `created by`
---
.../watchonly/templates/watchonly/_api_docs.html | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html b/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html
index ba52c4fa7..e40ca81fd 100644
--- a/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html
+++ b/lnbits/extensions/watchonly/templates/watchonly/_api_docs.html
@@ -18,9 +18,21 @@
>directly from browser
-
Created by,
+
Created by
Ben Arc,
+ Tiago Vasconcelos,
+ motorina0
(using,
Date: Thu, 22 Sep 2022 10:58:19 +0300
Subject: [PATCH 06/57] fix: paired devices list
---
.../serial-signer/serial-signer.html | 2 +-
.../components/serial-signer/serial-signer.js | 21 +++++++++++--------
2 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.html b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.html
index 2ed2d49c5..a95a19069 100644
--- a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.html
+++ b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.html
@@ -49,7 +49,7 @@
diff --git a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
index 332612806..b27f2fa29 100644
--- a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
+++ b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
@@ -51,7 +51,8 @@ async function serialSigner(path) {
},
tx: null, // todo: move to hww
- showConsole: false
+ showConsole: false,
+ showPairedDevices: true
}
},
@@ -59,14 +60,12 @@ async function serialSigner(path) {
pairedDevices: {
cache: false,
get: function () {
- console.log('### get pairedDevices')
return (
JSON.parse(window.localStorage.getItem('lnbits-paired-devices')) ||
[]
)
},
set: function (devices) {
- console.log('### set pairedDevices', devices)
window.localStorage.setItem(
'lnbits-paired-devices',
JSON.stringify(devices)
@@ -84,7 +83,6 @@ async function serialSigner(path) {
await this.openSerialPort(this.config)
},
openSerialPort: async function (config = {baudRate: 9600}) {
- console.log('### openSerialPort', config)
if (!this.checkSerialPortSupported()) return false
if (this.selectedPort) {
this.$q.notify({
@@ -314,11 +312,9 @@ async function serialSigner(path) {
},
hwwPing: async function () {
try {
- console.log('### hwwPing 1', window.location.host)
// Send an empty ping. The serial port buffer might have some jubk data. Flush it.
await this.sendCommandClearText(COMMAND_PING)
await this.sendCommandClearText(COMMAND_PING, [window.location.host])
- console.log('### hwwPing 2')
} catch (error) {
this.$q.notify({
type: 'warning',
@@ -735,7 +731,6 @@ async function serialSigner(path) {
},
hwwHelp: async function () {
try {
- console.log('### cmd help')
await this.sendCommandSecure(COMMAND_HELP)
this.$q.notify({
type: 'positive',
@@ -873,7 +868,6 @@ async function serialSigner(path) {
sendCommandSecure: async function (command, attrs = []) {
const message = [command].concat(attrs).join(' ')
- console.log('### sendCommandSecure', message)
const iv = window.crypto.getRandomValues(new Uint8Array(16))
if (!this.sharedSecret || !this.sharedSecret.length) {
throw new Error(
@@ -892,7 +886,6 @@ async function serialSigner(path) {
},
sendCommandClearText: async function (command, attrs = []) {
const message = [command].concat(attrs).join(' ')
- console.log('### sendCommandClearText', message)
await this.writer.write(message + '\n')
},
extractCommand: async function (value) {
@@ -968,6 +961,11 @@ async function serialSigner(path) {
devices.splice(deviceIndex, 1)
}
this.pairedDevices = devices
+ this.showPairedDevices = false
+ setTimeout(() => {
+ // force UI refresh
+ this.showPairedDevices = true
+ })
},
addPairedDevice: function (deviceId, sharedSecretHex, config) {
const devices = this.pairedDevices
@@ -979,6 +977,11 @@ async function serialSigner(path) {
config
})
this.pairedDevices = devices
+ this.showPairedDevices = false
+ setTimeout(() => {
+ // force UI refresh
+ this.showPairedDevices = true
+ })
},
updatePairedDeviceConfig(deviceId, config) {
const device = this.getPairedDevice(deviceId)
From 649c653836502a9cc29ec468874e2f9d3860a305 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 27 Sep 2022 17:19:32 +0300
Subject: [PATCH 07/57] fix: do not show command data in logs
---
.../static/components/serial-signer/serial-signer.js | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
index b27f2fa29..67268eebf 100644
--- a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
+++ b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
@@ -230,8 +230,9 @@ async function serialSigner(path) {
while (true) {
const {value, done} = await readStringUntil('\n')
if (value) {
- this.handleSerialPortResponse(value)
- this.updateSerialPortConsole(value)
+ const {command, commandData} = await this.extractCommand(value)
+ this.handleSerialPortResponse(command, commandData)
+ this.updateSerialPortConsole(command)
}
if (done) return
}
@@ -245,8 +246,7 @@ async function serialSigner(path) {
}
}
},
- handleSerialPortResponse: async function (value) {
- const {command, commandData} = await this.extractCommand(value)
+ handleSerialPortResponse: async function (command, commandData) {
this.logPublicCommandsResponse(command, commandData)
switch (command) {
@@ -287,7 +287,7 @@ async function serialSigner(path) {
)
break
default:
- console.log(` %c${value}`, 'background: #222; color: red')
+ console.log(` %c${command}`, 'background: #222; color: red')
}
},
logPublicCommandsResponse: function (command, commandData) {
From f1670c3153d990aea8060b5305bc0861d2712573 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 27 Sep 2022 17:20:33 +0300
Subject: [PATCH 08/57] chore: version bump
---
lnbits/extensions/watchonly/templates/watchonly/index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/watchonly/templates/watchonly/index.html b/lnbits/extensions/watchonly/templates/watchonly/index.html
index 91577cd28..67f898101 100644
--- a/lnbits/extensions/watchonly/templates/watchonly/index.html
+++ b/lnbits/extensions/watchonly/templates/watchonly/index.html
@@ -150,7 +150,7 @@
{{SITE_TITLE}} Onchain Wallet (watch-only) Extension
- (v0.2)
+ (v0.3)
From a8b90bd5952601577f6aa5b4c9ff8e4b499d361c Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Tue, 27 Sep 2022 17:35:27 +0300
Subject: [PATCH 09/57] fix: close serial port when re-pairing fails
---
.../components/serial-signer/serial-signer.js | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
index 67268eebf..8d3c1fecd 100644
--- a/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
+++ b/lnbits/extensions/watchonly/static/components/serial-signer/serial-signer.js
@@ -610,10 +610,10 @@ async function serialSigner(path) {
}
},
handleCheckPairingResponse: async function (res = '') {
- const [statusCode, encryptedMessage] = res.split(' ')
+ const [statusCode, message] = res.split(' ')
switch (statusCode) {
case '0':
- const controlText = await this.decryptData(encryptedMessage)
+ const controlText = await this.decryptData(message)
if (controlText == PAIRING_CONTROL_TEXT) {
this.$q.notify({
type: 'positive',
@@ -629,6 +629,16 @@ async function serialSigner(path) {
})
}
break
+ case '1':
+ this.closeSerialPort()
+ this.$q.notify({
+ type: 'warning',
+ message:
+ 'Re-pairing failed. Remove (forget) device and try again!',
+ caption: `Error: ${message}`,
+ timeout: 10000
+ })
+ break
default:
// noting to do here yet
break
From 19b1374d51ffe333c1862e8dbcb8332704f5b8cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Tue, 27 Sep 2022 17:44:52 +0200
Subject: [PATCH 10/57] fixing tests for new poetry version ^1.2
---
.github/workflows/formatting.yml | 13 ++++++++++++-
.github/workflows/migrations.yml | 8 ++++++--
.github/workflows/mypy.yml | 8 ++++++--
.github/workflows/regtest.yml | 24 ++++++++++++++++++------
.github/workflows/tests.yml | 19 ++++++++++++++-----
5 files changed, 56 insertions(+), 16 deletions(-)
diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml
index e106ace30..e3d0fd35d 100644
--- a/.github/workflows/formatting.yml
+++ b/.github/workflows/formatting.yml
@@ -9,9 +9,20 @@ on:
jobs:
checks:
runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ python-version: ["3.9"]
+ poetry-version: ["1.2.1"]
steps:
- uses: actions/checkout@v2
- - uses: abatilo/actions-poetry@v2.1.3
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Set up Poetry ${{ matrix.poetry-version }}
+ uses: abatilo/actions-poetry@v2
+ with:
+ poetry-version: ${{ matrix.poetry-version }}
- name: Install packages
run: poetry install
- name: Check black
diff --git a/.github/workflows/migrations.yml b/.github/workflows/migrations.yml
index 90006d2a8..c280ad7d9 100644
--- a/.github/workflows/migrations.yml
+++ b/.github/workflows/migrations.yml
@@ -22,14 +22,18 @@ jobs:
--health-retries 5
strategy:
matrix:
- python-version: [3.9]
+ python-version: ["3.9"]
+ poetry-version: ["1.2.1"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- - uses: abatilo/actions-poetry@v2.1.3
+ - name: Set up Poetry ${{ matrix.poetry-version }}
+ uses: abatilo/actions-poetry@v2
+ with:
+ poetry-version: ${{ matrix.poetry-version }}
- name: Install dependencies
run: |
poetry install
diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml
index 616017316..d80da678a 100644
--- a/.github/workflows/mypy.yml
+++ b/.github/workflows/mypy.yml
@@ -7,14 +7,18 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.9]
+ python-version: ["3.9"]
+ poetry-version: ["1.2.1"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- - uses: abatilo/actions-poetry@v2.1.3
+ - name: Set up Poetry ${{ matrix.poetry-version }}
+ uses: abatilo/actions-poetry@v2
+ with:
+ poetry-version: ${{ matrix.poetry-version }}
- name: Install dependencies
run: |
poetry install
diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml
index 6572ccdb6..2d7aae6b3 100644
--- a/.github/workflows/regtest.yml
+++ b/.github/workflows/regtest.yml
@@ -7,14 +7,18 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.9]
+ python-version: ["3.9"]
+ poetry-version: ["1.2.1"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- - uses: abatilo/actions-poetry@v2.1.3
+ - name: Set up Poetry ${{ matrix.poetry-version }}
+ uses: abatilo/actions-poetry@v2
+ with:
+ poetry-version: ${{ matrix.poetry-version }}
- name: Setup Regtest
run: |
docker build -t lnbitsdocker/lnbits-legend .
@@ -46,14 +50,18 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.9]
+ python-version: ["3.9"]
+ poetry-version: ["1.2.1"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- - uses: abatilo/actions-poetry@v2.1.3
+ - name: Set up Poetry ${{ matrix.poetry-version }}
+ uses: abatilo/actions-poetry@v2
+ with:
+ poetry-version: ${{ matrix.poetry-version }}
- name: Setup Regtest
run: |
docker build -t lnbitsdocker/lnbits-legend .
@@ -86,14 +94,18 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.9]
+ python-version: ["3.9"]
+ poetry-version: ["1.2.1"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- - uses: abatilo/actions-poetry@v2.1.3
+ - name: Set up Poetry ${{ matrix.poetry-version }}
+ uses: abatilo/actions-poetry@v2
+ with:
+ poetry-version: ${{ matrix.poetry-version }}
- name: Setup Regtest
run: |
docker build -t lnbitsdocker/lnbits-legend .
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 298d7ff04..5d368fbb1 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -7,7 +7,8 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.9]
+ python-version: ["3.9"]
+ poetry-version: ["1.2.1"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
@@ -29,14 +30,18 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: [3.9]
+ python-version: ["3.9"]
+ poetry-version: ["1.2.1"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- - uses: abatilo/actions-poetry@v2.1.3
+ - name: Set up Poetry ${{ matrix.poetry-version }}
+ uses: abatilo/actions-poetry@v2
+ with:
+ poetry-version: ${{ matrix.poetry-version }}
- name: Install dependencies
env:
VIRTUAL_ENV: ./venv
@@ -64,14 +69,18 @@ jobs:
--health-retries 5
strategy:
matrix:
- python-version: [3.9]
+ python-version: ["3.9"]
+ poetry-version: ["1.2.1"]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- - uses: abatilo/actions-poetry@v2.1.3
+ - name: Set up Poetry ${{ matrix.poetry-version }}
+ uses: abatilo/actions-poetry@v2
+ with:
+ poetry-version: ${{ matrix.poetry-version }}
- name: Install dependencies
run: |
poetry install
From 463941ec94cee61814a3f0714f2a76247175a6bc Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Wed, 28 Sep 2022 10:33:43 +0300
Subject: [PATCH 11/57] fix: always include change output for fee
---
.../extensions/watchonly/static/components/payment/payment.html | 2 +-
.../watchonly/static/components/serial-signer/serial-signer.js | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/lnbits/extensions/watchonly/static/components/payment/payment.html b/lnbits/extensions/watchonly/static/components/payment/payment.html
index cde65ca29..6e1d94c7a 100644
--- a/lnbits/extensions/watchonly/static/components/payment/payment.html
+++ b/lnbits/extensions/watchonly/static/components/payment/payment.html
@@ -5,7 +5,7 @@
Date: Thu, 29 Sep 2022 10:31:10 +0100
Subject: [PATCH 12/57] Added Caddy
---
docs/guide/installation.md | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index 2b058754d..e77e301d2 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -292,6 +292,41 @@ Save the file and run the following commands:
sudo systemctl enable lnbits.service
sudo systemctl start lnbits.service
```
+## Running using Caddy reverse proxy with automatic https
+
+Point your domain at the IP of the server you're running LNbits on, by making an `A` record.
+
+Install Caddy on the server
+https://caddyserver.com/docs/install#debian-ubuntu-raspbian
+
+```
+sudo caddy stop
+```
+Create a Caddyfile
+```
+sudo nano Caddyfile
+```
+Assuming your LNbits is running on port `5000`:
+```
+yourdomain.com {
+ handle /api/v1/payments/sse* {
+ reverse_proxy 0.0.0.0:5000 {
+ header_up X-Forwarded-Host legend.lnbits.com
+ transport http {
+ keepalive off
+ compression off
+ }
+ }
+ }
+ reverse_proxy 0.0.0.0:5000 {
+ header_up X-Forwarded-Host legend.lnbits.com
+ }
+}
+```
+Save and exit `CTRL + x`
+```
+sudo caddy start
+```
## Running behind an apache2 reverse proxy over https
Install apache2 and enable apache2 mods
From 55b0814d932bda58545ba25cf95f5cac02477039 Mon Sep 17 00:00:00 2001
From: ben
Date: Thu, 29 Sep 2022 10:34:03 +0100
Subject: [PATCH 13/57] added intro
---
docs/guide/installation.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index e77e301d2..21ee307a8 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -292,7 +292,9 @@ Save the file and run the following commands:
sudo systemctl enable lnbits.service
sudo systemctl start lnbits.service
```
-## Running using Caddy reverse proxy with automatic https
+## Setting up a Caddy reverse proxy with automatic https
+
+USe Caddy to easily make your LNbits install accessible over clearnet with a domain and https cert.
Point your domain at the IP of the server you're running LNbits on, by making an `A` record.
From 7de17485d39e262f81e0dfd0cbc61b22cab3f1dc Mon Sep 17 00:00:00 2001
From: ben
Date: Thu, 29 Sep 2022 10:34:53 +0100
Subject: [PATCH 14/57] typo
---
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 21ee307a8..d79363af3 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -294,7 +294,7 @@ sudo systemctl start lnbits.service
```
## Setting up a Caddy reverse proxy with automatic https
-USe Caddy to easily make your LNbits install accessible over clearnet with a domain and https cert.
+Use Caddy to make your LNbits install accessible over clearnet with a domain and https cert.
Point your domain at the IP of the server you're running LNbits on, by making an `A` record.
From 9c575b7d3a46f355ec01dad4b27de70b9be4c440 Mon Sep 17 00:00:00 2001
From: ben
Date: Thu, 29 Sep 2022 10:37:16 +0100
Subject: [PATCH 15/57] typo
---
docs/guide/installation.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index d79363af3..7f2aefdcf 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -292,7 +292,7 @@ Save the file and run the following commands:
sudo systemctl enable lnbits.service
sudo systemctl start lnbits.service
```
-## Setting up a Caddy reverse proxy with automatic https
+## Reverse proxy with automatic https using Caddy
Use Caddy to make your LNbits install accessible over clearnet with a domain and https cert.
@@ -308,7 +308,7 @@ Create a Caddyfile
```
sudo nano Caddyfile
```
-Assuming your LNbits is running on port `5000`:
+Assuming your LNbits is running on port `5000` add:
```
yourdomain.com {
handle /api/v1/payments/sse* {
From 07293e7d5ce765807f753c7ef0f0e6723f95f4a4 Mon Sep 17 00:00:00 2001
From: Arc <33088785+arcbtc@users.noreply.github.com>
Date: Thu, 29 Sep 2022 10:45:09 +0100
Subject: [PATCH 16/57] Update installation.md
---
docs/guide/installation.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index 7f2aefdcf..87679ed5b 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -313,7 +313,7 @@ Assuming your LNbits is running on port `5000` add:
yourdomain.com {
handle /api/v1/payments/sse* {
reverse_proxy 0.0.0.0:5000 {
- header_up X-Forwarded-Host legend.lnbits.com
+ header_up X-Forwarded-Host yourdomain.com
transport http {
keepalive off
compression off
@@ -321,7 +321,7 @@ yourdomain.com {
}
}
reverse_proxy 0.0.0.0:5000 {
- header_up X-Forwarded-Host legend.lnbits.com
+ header_up X-Forwarded-Host yourdomain.com
}
}
```
From 325139066c309d4080ab7c2e94ad01ede63cdba3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 29 Sep 2022 20:40:58 +0200
Subject: [PATCH 17/57] fix service-worker.js issue #1009 (#1017)
* service-worker.js fix for issue #1009
fixup
* remove weird comment
---
lnbits/core/static/js/service-worker.js | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/lnbits/core/static/js/service-worker.js b/lnbits/core/static/js/service-worker.js
index 041b9f32f..98ae497ec 100644
--- a/lnbits/core/static/js/service-worker.js
+++ b/lnbits/core/static/js/service-worker.js
@@ -3,7 +3,11 @@ const CACHE_VERSION = 1
const CURRENT_CACHE = `lnbits-${CACHE_VERSION}-`
const getApiKey = request => {
- return request.headers.get('X-Api-Key') || 'none'
+ let api_key = request.headers.get('X-Api-Key')
+ if (!api_key || api_key == 'undefined') {
+ api_key = 'no_api_key'
+ }
+ return api_key
}
// on activation we clean up the previously registered service workers
@@ -26,8 +30,10 @@ self.addEventListener('activate', evt =>
// If no response is found, it populates the runtime cache with the response
// from the network before returning it to the page.
self.addEventListener('fetch', event => {
- // Skip cross-origin requests, like those for Google Analytics.
if (
+ !event.request.url.startsWith(
+ self.location.origin + '/api/v1/payments/sse'
+ ) &&
event.request.url.startsWith(self.location.origin) &&
event.request.method == 'GET'
) {
From 04cfdc2bf3b15bc9d9bdc5b0137d0d729c0ccadf Mon Sep 17 00:00:00 2001
From: Taylor Helsper
Date: Thu, 29 Sep 2022 23:06:45 -0500
Subject: [PATCH 18/57] Update index.html
---
lnbits/core/templates/core/index.html | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/lnbits/core/templates/core/index.html b/lnbits/core/templates/core/index.html
index f769b44f6..1319fa1f0 100644
--- a/lnbits/core/templates/core/index.html
+++ b/lnbits/core/templates/core/index.html
@@ -171,6 +171,19 @@
+
From fb58f1ed5e04895582ac79e889a74e2d9a07dd57 Mon Sep 17 00:00:00 2001
From: Taylor Helsper
Date: Thu, 29 Sep 2022 23:42:38 -0500
Subject: [PATCH 19/57] Update myNode images
---
lnbits/static/images/mynode.png | Bin 3757 -> 13994 bytes
lnbits/static/images/mynodel.png | Bin 14352 -> 9272 bytes
2 files changed, 0 insertions(+), 0 deletions(-)
diff --git a/lnbits/static/images/mynode.png b/lnbits/static/images/mynode.png
index cf25bc586797b750a64ddba848b2499a0698a5b2..390446b8ee906da9e01142b477a59506269ce5e7 100644
GIT binary patch
literal 13994
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
diff --git a/lnbits/static/images/mynodel.png b/lnbits/static/images/mynodel.png
index b8afb9ffcd0dbe9c3c07cbd1e007b1067d92caa9..344b54b6db53406258f7f1207b78f72b9fb1cbf3 100644
GIT binary patch
delta 5190
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
delta 10444
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
Date: Fri, 30 Sep 2022 12:41:35 +0100
Subject: [PATCH 20/57] add small description in UI (#1023)
---
.../extensions/scrub/templates/scrub/index.html | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/lnbits/extensions/scrub/templates/scrub/index.html b/lnbits/extensions/scrub/templates/scrub/index.html
index c063c858e..5a1cae585 100644
--- a/lnbits/extensions/scrub/templates/scrub/index.html
+++ b/lnbits/extensions/scrub/templates/scrub/index.html
@@ -68,6 +68,21 @@
{{SITE_TITLE}} Scrub extension
+
+ Automatically forward funds (Scrub) that get paid to the LNbits
+ wallet, to an LNURLpay or Lightning Address.
+
+ More info in Scrub's
+ readme.
+
+
+ Important: wallet will need a float to account for
+ any fees, before being able to push a payment
+
From 4ad3c841528de3efafefe48f667e6800eb7074e3 Mon Sep 17 00:00:00 2001
From: calle <93376500+callebtc@users.noreply.github.com>
Date: Fri, 30 Sep 2022 13:55:46 +0200
Subject: [PATCH 21/57] downgrade pyln from 0.12.0 to 0.11.1 (#1021)
---
poetry.lock | 36 ++++++++++++++++++++++++++++--------
pyproject.toml | 2 +-
2 files changed, 29 insertions(+), 9 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index ea83e25e2..2a57a5c12 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -118,6 +118,9 @@ category = "main"
optional = false
python-versions = ">=2.7"
+[package.dependencies]
+setuptools = "*"
+
[[package]]
name = "certifi"
version = "2021.5.30"
@@ -206,7 +209,7 @@ python-versions = ">=3.6"
cffi = ">=1.12"
[package.extras]
-docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"]
+docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx_rtd_theme"]
docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
sdist = ["setuptools_rust (>=0.11.4)"]
@@ -638,7 +641,7 @@ python-versions = ">=3.7,<4.0"
[[package]]
name = "pyln-client"
-version = "0.12.0.post1"
+version = "0.11.1"
description = "Client library and plugin library for Core Lightning"
category = "main"
optional = false
@@ -707,7 +710,7 @@ pathlib2 = "*"
six = "*"
[[package]]
-name = "pysocks"
+name = "PySocks"
version = "1.7.1"
description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
category = "main"
@@ -823,6 +826,19 @@ python-versions = "*"
[package.dependencies]
cffi = ">=1.3.0"
+[[package]]
+name = "setuptools"
+version = "65.4.0"
+description = "Easily download, build, install, upgrade, and uninstall Python packages"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
+testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
+
[[package]]
name = "shortuuid"
version = "1.0.1"
@@ -860,7 +876,7 @@ mssql = ["pyodbc"]
mssql_pymssql = ["pymssql"]
mssql_pyodbc = ["pyodbc"]
mysql = ["mysqlclient"]
-oracle = ["cx-oracle"]
+oracle = ["cx_oracle"]
postgresql = ["psycopg2"]
postgresql_pg8000 = ["pg8000 (<1.16.6)"]
postgresql_psycopg2binary = ["psycopg2-binary"]
@@ -1024,7 +1040,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (
[metadata]
lock-version = "1.1"
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
-content-hash = "d0556d4a307864ba04a1e5da517884e523396c98a00ae09d9192c37b1d2c555b"
+content-hash = "72e4462285d0bc5e2cb83c88c613726beced959b268bd30b984d8baaeff178ea"
[metadata.files]
aiofiles = [
@@ -1661,8 +1677,8 @@ pyln-bolt7 = [
{file = "pyln_bolt7-1.0.246-py3-none-any.whl", hash = "sha256:54d48ec27fdc8751762cb068b0a9f2757a58fb57933c6d8f8255d02c27eb63c5"},
]
pyln-client = [
- {file = "pyln-client-0.12.0.post1.tar.gz", hash = "sha256:c80338e8e9f435720c0e5f552dc4016fc8fba16d4b79764f881067e0fcd5d5c7"},
- {file = "pyln_client-0.12.0.post1-py3-none-any.whl", hash = "sha256:cfe3404eb88f294015145e668d774dd754b3baec36b44fe773fa354f1e1e48c1"},
+ {file = "pyln-client-0.11.1.tar.gz", hash = "sha256:f5ea648840b030e2bbcf8c66ee72d25a5817f89854434a28d30e887547138c8e"},
+ {file = "pyln_client-0.11.1-py3-none-any.whl", hash = "sha256:497db443406b80c98c0434e2938eb1b2a17e88fd9aa63b018124068198df6141"},
]
pyln-proto = [
{file = "pyln-proto-0.11.1.tar.gz", hash = "sha256:9bed240f41917c4fd526b767218a77d0fbe69242876eef72c35a856796f922d6"},
@@ -1682,7 +1698,7 @@ pyqrcode = [
pyscss = [
{file = "pyScss-1.4.0.tar.gz", hash = "sha256:8f35521ffe36afa8b34c7d6f3195088a7057c185c2b8f15ee459ab19748669ff"},
]
-pysocks = [
+PySocks = [
{file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"},
{file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"},
{file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"},
@@ -1767,6 +1783,10 @@ secp256k1 = [
{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"},
]
+setuptools = [
+ {file = "setuptools-65.4.0-py3-none-any.whl", hash = "sha256:c2d2709550f15aab6c9110196ea312f468f41cd546bceb24127a1be6fdcaeeb1"},
+ {file = "setuptools-65.4.0.tar.gz", hash = "sha256:a8f6e213b4b0661f590ccf40de95d28a177cd747d098624ad3f69c40287297e9"},
+]
shortuuid = [
{file = "shortuuid-1.0.1-py3-none-any.whl", hash = "sha256:492c7402ff91beb1342a5898bd61ea953985bf24a41cd9f247409aa2e03c8f77"},
{file = "shortuuid-1.0.1.tar.gz", hash = "sha256:3c11d2007b915c43bee3e10625f068d8a349e04f0d81f08f5fa08507427ebf1f"},
diff --git a/pyproject.toml b/pyproject.toml
index 7f833aa55..864500f7b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -62,7 +62,7 @@ cffi = "1.15.0"
websocket-client = "1.3.3"
grpcio = "^1.49.1"
protobuf = "^4.21.6"
-pyln-client = "^0.12.0"
+pyln-client = "0.11.1"
[tool.poetry.dev-dependencies]
isort = "^5.10.1"
From 148789a86f61aef3b241497fd6ca668edeb44a70 Mon Sep 17 00:00:00 2001
From: Taylor Helsper
Date: Fri, 30 Sep 2022 10:19:17 -0500
Subject: [PATCH 22/57] Fix formatting
---
lnbits/core/templates/core/index.html | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/lnbits/core/templates/core/index.html b/lnbits/core/templates/core/index.html
index 1319fa1f0..68a7b7ede 100644
--- a/lnbits/core/templates/core/index.html
+++ b/lnbits/core/templates/core/index.html
@@ -180,9 +180,7 @@
>
-
-
-
+
From 41668d6f3288fa75ac47be685e338242e0ec85c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Sat, 1 Oct 2022 11:33:58 +0200
Subject: [PATCH 23/57] add warning for voidwallet for better coloring in logs
---
lnbits/wallets/void.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/wallets/void.py b/lnbits/wallets/void.py
index 0de387aae..b74eb2450 100644
--- a/lnbits/wallets/void.py
+++ b/lnbits/wallets/void.py
@@ -23,7 +23,7 @@ class VoidWallet(Wallet):
raise Unsupported("")
async def status(self) -> StatusResponse:
- logger.info(
+ logger.warning(
"This backend does nothing, it is here just as a placeholder, you must configure an actual backend before being able to do anything useful with LNbits."
)
return StatusResponse(None, 0)
From 3aa4d09103ac94b1e3703086f1528da1020f53e3 Mon Sep 17 00:00:00 2001
From: Indra <86242283+VajraOfIndra@users.noreply.github.com>
Date: Sun, 2 Oct 2022 16:45:16 +0100
Subject: [PATCH 24/57] Adding missing theme options in conf file
Missing bitcoin theme in comment.
Missing flamingo theme in list of values for LNBITS_THEME_OPTIONS.
---
.env.example | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.env.example b/.env.example
index 93b823250..7d6de35f4 100644
--- a/.env.example
+++ b/.env.example
@@ -37,8 +37,8 @@ LNBITS_RESERVE_FEE_PERCENT=1.0
LNBITS_SITE_TITLE="LNbits"
LNBITS_SITE_TAGLINE="free and open-source lightning wallet"
LNBITS_SITE_DESCRIPTION="Some description about your service, will display if title is not 'LNbits'"
-# Choose from mint, flamingo, freedom, salvador, autumn, monochrome, classic
-LNBITS_THEME_OPTIONS="classic, bitcoin, freedom, mint, autumn, monochrome, salvador"
+# Choose from bitcoin, mint, flamingo, freedom, salvador, autumn, monochrome, classic
+LNBITS_THEME_OPTIONS="classic, bitcoin, flamingo, freedom, mint, autumn, monochrome, salvador"
# LNBITS_CUSTOM_LOGO="https://lnbits.com/assets/images/logo/logo.svg"
# Choose from LNPayWallet, OpenNodeWallet, LntxbotWallet, ClicheWallet
@@ -91,4 +91,4 @@ LNBITS_DENOMINATION=sats
# EclairWallet
ECLAIR_URL=http://127.0.0.1:8283
-ECLAIR_PASS=eclairpw
\ No newline at end of file
+ECLAIR_PASS=eclairpw
From 5a12f4f237a1a292c394a8bfeb3d5d40577afac7 Mon Sep 17 00:00:00 2001
From: calle <93376500+callebtc@users.noreply.github.com>
Date: Tue, 4 Oct 2022 09:51:47 +0200
Subject: [PATCH 25/57] Improved SSE listeners (#865)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* logging listeners
* comments
* generate privkey upon init
* listener queue
* remove duplicate check
* make format
* reuse channel
* error handling in sse listener
* uuid for listeners
* register named invoices
* uuid for listeners and listener list
* fix poetry lock
* setuptools
* requirements asyncio timeout
* setuptool;s
* make format
* remove async-timeout
* async_timeout readd
* try lower setuptools version
* try lower lower setuptools version
* back to current version + fix, maybe
* fix worflows to use poetry 1.2.1
* remove uneeded setuptools from build-system
* fix up formatting workflow
* debug to trace
* more traces
* debug logs to trace
Co-authored-by: dni ⚡
---
lnbits/app.py | 3 +-
lnbits/core/services.py | 2 +-
lnbits/core/tasks.py | 40 ++++++++---
lnbits/core/views/api.py | 45 ++++++++-----
lnbits/core/views/public_api.py | 4 +-
lnbits/extensions/boltcards/tasks.py | 3 +-
lnbits/extensions/boltz/boltz.py | 4 +-
lnbits/extensions/boltz/mempool.py | 4 +-
lnbits/extensions/boltz/tasks.py | 3 +-
lnbits/extensions/copilot/tasks.py | 3 +-
lnbits/extensions/jukebox/tasks.py | 3 +-
lnbits/extensions/livestream/tasks.py | 4 +-
lnbits/extensions/lnaddress/tasks.py | 3 +-
lnbits/extensions/lnticket/__init__.py | 1 +
lnbits/extensions/lnticket/tasks.py | 3 +-
lnbits/extensions/lnurlp/tasks.py | 3 +-
lnbits/extensions/lnurlpayout/tasks.py | 3 +-
lnbits/extensions/satspay/tasks.py | 3 +-
lnbits/extensions/scrub/tasks.py | 3 +-
lnbits/extensions/splitpayments/tasks.py | 4 +-
lnbits/extensions/subdomains/tasks.py | 3 +-
lnbits/extensions/tpos/tasks.py | 4 +-
lnbits/helpers.py | 23 +++++++
lnbits/tasks.py | 84 +++++++++++++++++-------
lnbits/wallets/fake.py | 32 ++++-----
poetry.lock | 57 ++++++++++------
pyproject.toml | 3 +-
requirements.txt | 2 +
28 files changed, 239 insertions(+), 110 deletions(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index f612c32c3..514825380 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -34,7 +34,6 @@ from .tasks import (
check_pending_payments,
internal_invoice_listener,
invoice_listener,
- run_deferred_async,
webhook_handler,
)
@@ -185,7 +184,7 @@ def register_async_tasks(app):
loop.create_task(catch_everything_and_restart(invoice_listener))
loop.create_task(catch_everything_and_restart(internal_invoice_listener))
await register_task_listeners()
- await run_deferred_async()
+ # await run_deferred_async() # calle: doesn't do anyting?
@app.on_event("shutdown")
async def stop_listeners():
diff --git a/lnbits/core/services.py b/lnbits/core/services.py
index 961eb7b2c..5d993b4c5 100644
--- a/lnbits/core/services.py
+++ b/lnbits/core/services.py
@@ -186,9 +186,9 @@ async def pay_invoice(
)
# notify receiver asynchronously
-
from lnbits.tasks import internal_invoice_queue
+ logger.debug(f"enqueuing internal invoice {internal_checking_id}")
await internal_invoice_queue.put(internal_checking_id)
else:
logger.debug(f"backend: sending payment {temp_id}")
diff --git a/lnbits/core/tasks.py b/lnbits/core/tasks.py
index 07b8a8933..b57e26257 100644
--- a/lnbits/core/tasks.py
+++ b/lnbits/core/tasks.py
@@ -1,30 +1,43 @@
import asyncio
-from typing import List
+from typing import Dict
import httpx
from loguru import logger
-from lnbits.tasks import register_invoice_listener
+from lnbits.helpers import get_current_extension_name
+from lnbits.tasks import SseListenersDict, register_invoice_listener
from . import db
from .crud import get_balance_notify
from .models import Payment
-api_invoice_listeners: List[asyncio.Queue] = []
+api_invoice_listeners: Dict[str, asyncio.Queue] = SseListenersDict(
+ "api_invoice_listeners"
+)
async def register_task_listeners():
+ """
+ Registers an invoice listener queue for the core tasks.
+ Incoming payaments in this queue will eventually trigger the signals sent to all other extensions
+ and fulfill other core tasks such as dispatching webhooks.
+ """
invoice_paid_queue = asyncio.Queue(5)
- register_invoice_listener(invoice_paid_queue)
+ # we register invoice_paid_queue to receive all incoming invoices
+ register_invoice_listener(invoice_paid_queue, "core/tasks.py")
+ # register a worker that will react to invoices
asyncio.create_task(wait_for_paid_invoices(invoice_paid_queue))
async def wait_for_paid_invoices(invoice_paid_queue: asyncio.Queue):
+ """
+ This worker dispatches events to all extensions, dispatches webhooks and balance notifys.
+ """
while True:
payment = await invoice_paid_queue.get()
- logger.debug("received invoice paid event")
+ logger.trace("received invoice paid event")
# send information to sse channel
- await dispatch_invoice_listener(payment)
+ await dispatch_api_invoice_listeners(payment)
# dispatch webhook
if payment.webhook and not payment.webhook_status:
@@ -41,16 +54,23 @@ async def wait_for_paid_invoices(invoice_paid_queue: asyncio.Queue):
pass
-async def dispatch_invoice_listener(payment: Payment):
- for send_channel in api_invoice_listeners:
+async def dispatch_api_invoice_listeners(payment: Payment):
+ """
+ Emits events to invoice listener subscribed from the API.
+ """
+ for chan_name, send_channel in api_invoice_listeners.items():
try:
+ logger.debug(f"sending invoice paid event to {chan_name}")
send_channel.put_nowait(payment)
except asyncio.QueueFull:
- logger.debug("removing sse listener", send_channel)
- api_invoice_listeners.remove(send_channel)
+ logger.error(f"removing sse listener {send_channel}:{chan_name}")
+ api_invoice_listeners.pop(chan_name)
async def dispatch_webhook(payment: Payment):
+ """
+ Dispatches the webhook to the webhook url.
+ """
async with httpx.AsyncClient() as client:
data = payment.dict()
try:
diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py
index 7a2bbbe60..c07df568a 100644
--- a/lnbits/core/views/api.py
+++ b/lnbits/core/views/api.py
@@ -3,11 +3,13 @@ import binascii
import hashlib
import json
import time
+import uuid
from http import HTTPStatus
from io import BytesIO
from typing import Dict, List, Optional, Tuple, Union
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
+import async_timeout
import httpx
import pyqrcode
from fastapi import Depends, Header, Query, Request
@@ -16,7 +18,7 @@ from fastapi.params import Body
from loguru import logger
from pydantic import BaseModel
from pydantic.fields import Field
-from sse_starlette.sse import EventSourceResponse
+from sse_starlette.sse import EventSourceResponse, ServerSentEvent
from starlette.responses import HTMLResponse, StreamingResponse
from lnbits import bolt11, lnurl
@@ -366,37 +368,48 @@ async def api_payments_pay_lnurl(
}
-async def subscribe(request: Request, wallet: Wallet):
+async def subscribe_wallet_invoices(request: Request, wallet: Wallet):
+ """
+ Subscribe to new invoices for a wallet. Can be wrapped in EventSourceResponse.
+ Listenes invoming payments for a wallet and yields jsons with payment details.
+ """
this_wallet_id = wallet.id
payment_queue: asyncio.Queue[Payment] = asyncio.Queue(0)
- logger.debug("adding sse listener", payment_queue)
- api_invoice_listeners.append(payment_queue)
+ uid = f"{this_wallet_id}_{str(uuid.uuid4())[:8]}"
+ logger.debug(f"adding sse listener for wallet: {uid}")
+ api_invoice_listeners[uid] = payment_queue
send_queue: asyncio.Queue[Tuple[str, Payment]] = asyncio.Queue(0)
async def payment_received() -> None:
while True:
- payment: Payment = await payment_queue.get()
- if payment.wallet_id == this_wallet_id:
- logger.debug("payment received", payment)
- await send_queue.put(("payment-received", payment))
+ try:
+ async with async_timeout.timeout(1):
+ payment: Payment = await payment_queue.get()
+ if payment.wallet_id == this_wallet_id:
+ logger.debug("sse listener: payment receieved", payment)
+ await send_queue.put(("payment-received", payment))
+ except asyncio.TimeoutError:
+ pass
- asyncio.create_task(payment_received())
+ task = asyncio.create_task(payment_received())
try:
while True:
+ if await request.is_disconnected():
+ await request.close()
+ break
typ, data = await send_queue.get()
-
if data:
jdata = json.dumps(dict(data.dict(), pending=False))
- # yield dict(id=1, event="this", data="1234")
- # await asyncio.sleep(2)
yield dict(data=jdata, event=typ)
- # yield dict(data=jdata.encode("utf-8"), event=typ.encode("utf-8"))
- except asyncio.CancelledError:
+ except asyncio.CancelledError as e:
+ logger.debug(f"CancelledError on listener {uid}: {e}")
+ api_invoice_listeners.pop(uid)
+ task.cancel()
return
@@ -405,7 +418,9 @@ async def api_payments_sse(
request: Request, wallet: WalletTypeInfo = Depends(get_key_type)
):
return EventSourceResponse(
- subscribe(request, wallet.wallet), ping=20, media_type="text/event-stream"
+ subscribe_wallet_invoices(request, wallet.wallet),
+ ping=20,
+ media_type="text/event-stream",
)
diff --git a/lnbits/core/views/public_api.py b/lnbits/core/views/public_api.py
index 2d2cdd66c..9b0ebc98b 100644
--- a/lnbits/core/views/public_api.py
+++ b/lnbits/core/views/public_api.py
@@ -46,8 +46,8 @@ async def api_public_payment_longpolling(payment_hash):
payment_queue = asyncio.Queue(0)
- logger.debug("adding standalone invoice listener", payment_hash, payment_queue)
- api_invoice_listeners.append(payment_queue)
+ logger.debug(f"adding standalone invoice listener for hash: {payment_hash}")
+ api_invoice_listeners[payment_hash] = payment_queue
response = None
diff --git a/lnbits/extensions/boltcards/tasks.py b/lnbits/extensions/boltcards/tasks.py
index 1b51c98ba..c1e99b765 100644
--- a/lnbits/extensions/boltcards/tasks.py
+++ b/lnbits/extensions/boltcards/tasks.py
@@ -5,6 +5,7 @@ import httpx
from lnbits.core import db as core_db
from lnbits.core.models import Payment
+from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
from .crud import create_refund, get_hit
@@ -12,7 +13,7 @@ from .crud import create_refund, get_hit
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/extensions/boltz/boltz.py b/lnbits/extensions/boltz/boltz.py
index 4e5fecd00..ac99d4f4a 100644
--- a/lnbits/extensions/boltz/boltz.py
+++ b/lnbits/extensions/boltz/boltz.py
@@ -34,8 +34,8 @@ from .models import (
from .utils import check_balance, get_timestamp, req_wrap
net = NETWORKS[BOLTZ_NETWORK]
-logger.debug(f"BOLTZ_URL: {BOLTZ_URL}")
-logger.debug(f"Bitcoin Network: {net['name']}")
+logger.trace(f"BOLTZ_URL: {BOLTZ_URL}")
+logger.trace(f"Bitcoin Network: {net['name']}")
async def create_swap(data: CreateSubmarineSwap) -> SubmarineSwap:
diff --git a/lnbits/extensions/boltz/mempool.py b/lnbits/extensions/boltz/mempool.py
index ee3052577..a44c0f02c 100644
--- a/lnbits/extensions/boltz/mempool.py
+++ b/lnbits/extensions/boltz/mempool.py
@@ -11,8 +11,8 @@ from lnbits.settings import BOLTZ_MEMPOOL_SPACE_URL, BOLTZ_MEMPOOL_SPACE_URL_WS
from .utils import req_wrap
-logger.debug(f"BOLTZ_MEMPOOL_SPACE_URL: {BOLTZ_MEMPOOL_SPACE_URL}")
-logger.debug(f"BOLTZ_MEMPOOL_SPACE_URL_WS: {BOLTZ_MEMPOOL_SPACE_URL_WS}")
+logger.trace(f"BOLTZ_MEMPOOL_SPACE_URL: {BOLTZ_MEMPOOL_SPACE_URL}")
+logger.trace(f"BOLTZ_MEMPOOL_SPACE_URL_WS: {BOLTZ_MEMPOOL_SPACE_URL_WS}")
websocket_url = f"{BOLTZ_MEMPOOL_SPACE_URL_WS}/api/v1/ws"
diff --git a/lnbits/extensions/boltz/tasks.py b/lnbits/extensions/boltz/tasks.py
index d6f72edf9..ace94557b 100644
--- a/lnbits/extensions/boltz/tasks.py
+++ b/lnbits/extensions/boltz/tasks.py
@@ -5,6 +5,7 @@ from loguru import logger
from lnbits.core.models import Payment
from lnbits.core.services import check_transaction_status
+from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
from .boltz import (
@@ -127,7 +128,7 @@ async def check_for_pending_swaps():
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/extensions/copilot/tasks.py b/lnbits/extensions/copilot/tasks.py
index f3c5cff84..c59ef4cc8 100644
--- a/lnbits/extensions/copilot/tasks.py
+++ b/lnbits/extensions/copilot/tasks.py
@@ -7,6 +7,7 @@ from starlette.exceptions import HTTPException
from lnbits.core import db as core_db
from lnbits.core.models import Payment
+from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
from .crud import get_copilot
@@ -15,7 +16,7 @@ from .views import updater
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/extensions/jukebox/tasks.py b/lnbits/extensions/jukebox/tasks.py
index 70a2e65dd..5614d9261 100644
--- a/lnbits/extensions/jukebox/tasks.py
+++ b/lnbits/extensions/jukebox/tasks.py
@@ -1,6 +1,7 @@
import asyncio
from lnbits.core.models import Payment
+from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
from .crud import update_jukebox_payment
@@ -8,7 +9,7 @@ from .crud import update_jukebox_payment
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/extensions/livestream/tasks.py b/lnbits/extensions/livestream/tasks.py
index 85bdd5e0a..626c698ca 100644
--- a/lnbits/extensions/livestream/tasks.py
+++ b/lnbits/extensions/livestream/tasks.py
@@ -6,7 +6,7 @@ from loguru import logger
from lnbits.core import db as core_db
from lnbits.core.crud import create_payment
from lnbits.core.models import Payment
-from lnbits.helpers import urlsafe_short_hash
+from lnbits.helpers import get_current_extension_name, urlsafe_short_hash
from lnbits.tasks import internal_invoice_listener, register_invoice_listener
from .crud import get_livestream_by_track, get_producer, get_track
@@ -14,7 +14,7 @@ from .crud import get_livestream_by_track, get_producer, get_track
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/extensions/lnaddress/tasks.py b/lnbits/extensions/lnaddress/tasks.py
index 9abe10c3d..0c377eecd 100644
--- a/lnbits/extensions/lnaddress/tasks.py
+++ b/lnbits/extensions/lnaddress/tasks.py
@@ -3,6 +3,7 @@ import asyncio
import httpx
from lnbits.core.models import Payment
+from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
from .crud import get_address, get_domain, set_address_paid, set_address_renewed
@@ -10,7 +11,7 @@ from .crud import get_address, get_domain, set_address_paid, set_address_renewed
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/extensions/lnticket/__init__.py b/lnbits/extensions/lnticket/__init__.py
index 792b11751..cb793f4dc 100644
--- a/lnbits/extensions/lnticket/__init__.py
+++ b/lnbits/extensions/lnticket/__init__.py
@@ -1,4 +1,5 @@
import asyncio
+import json
from fastapi import APIRouter
diff --git a/lnbits/extensions/lnticket/tasks.py b/lnbits/extensions/lnticket/tasks.py
index 7e672115d..746ebea94 100644
--- a/lnbits/extensions/lnticket/tasks.py
+++ b/lnbits/extensions/lnticket/tasks.py
@@ -3,6 +3,7 @@ import asyncio
from loguru import logger
from lnbits.core.models import Payment
+from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
from .crud import get_ticket, set_ticket_paid
@@ -10,7 +11,7 @@ from .crud import get_ticket, set_ticket_paid
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/extensions/lnurlp/tasks.py b/lnbits/extensions/lnurlp/tasks.py
index 525d36ce4..86f1579a0 100644
--- a/lnbits/extensions/lnurlp/tasks.py
+++ b/lnbits/extensions/lnurlp/tasks.py
@@ -5,6 +5,7 @@ import httpx
from lnbits.core import db as core_db
from lnbits.core.models import Payment
+from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
from .crud import get_pay_link
@@ -12,7 +13,7 @@ from .crud import get_pay_link
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/extensions/lnurlpayout/tasks.py b/lnbits/extensions/lnurlpayout/tasks.py
index b621876cb..71f299be5 100644
--- a/lnbits/extensions/lnurlpayout/tasks.py
+++ b/lnbits/extensions/lnurlpayout/tasks.py
@@ -10,6 +10,7 @@ from lnbits.core.crud import get_wallet
from lnbits.core.models import Payment
from lnbits.core.services import pay_invoice
from lnbits.core.views.api import api_payments_decode
+from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
from .crud import get_lnurlpayout_from_wallet
@@ -17,7 +18,7 @@ from .crud import get_lnurlpayout_from_wallet
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/extensions/satspay/tasks.py b/lnbits/extensions/satspay/tasks.py
index d325405b8..46c16bbc9 100644
--- a/lnbits/extensions/satspay/tasks.py
+++ b/lnbits/extensions/satspay/tasks.py
@@ -4,6 +4,7 @@ from loguru import logger
from lnbits.core.models import Payment
from lnbits.extensions.satspay.crud import check_address_balance, get_charge
+from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
# from .crud import get_ticket, set_ticket_paid
@@ -11,7 +12,7 @@ from lnbits.tasks import register_invoice_listener
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/extensions/scrub/tasks.py b/lnbits/extensions/scrub/tasks.py
index 87e1364b7..320d34da0 100644
--- a/lnbits/extensions/scrub/tasks.py
+++ b/lnbits/extensions/scrub/tasks.py
@@ -9,6 +9,7 @@ from fastapi import HTTPException
from lnbits import bolt11
from lnbits.core.models import Payment
from lnbits.core.services import pay_invoice
+from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
from .crud import get_scrub_by_wallet
@@ -16,7 +17,7 @@ from .crud import get_scrub_by_wallet
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/extensions/splitpayments/tasks.py b/lnbits/extensions/splitpayments/tasks.py
index 0948e849a..b7cf1750c 100644
--- a/lnbits/extensions/splitpayments/tasks.py
+++ b/lnbits/extensions/splitpayments/tasks.py
@@ -6,7 +6,7 @@ from loguru import logger
from lnbits.core import db as core_db
from lnbits.core.crud import create_payment
from lnbits.core.models import Payment
-from lnbits.helpers import urlsafe_short_hash
+from lnbits.helpers import get_current_extension_name, urlsafe_short_hash
from lnbits.tasks import internal_invoice_queue, register_invoice_listener
from .crud import get_targets
@@ -14,7 +14,7 @@ from .crud import get_targets
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/extensions/subdomains/tasks.py b/lnbits/extensions/subdomains/tasks.py
index d8f351618..04ee2dd48 100644
--- a/lnbits/extensions/subdomains/tasks.py
+++ b/lnbits/extensions/subdomains/tasks.py
@@ -3,6 +3,7 @@ import asyncio
import httpx
from lnbits.core.models import Payment
+from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
from .cloudflare import cloudflare_create_subdomain
@@ -11,7 +12,7 @@ from .crud import get_domain, set_subdomain_paid
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/extensions/tpos/tasks.py b/lnbits/extensions/tpos/tasks.py
index af9663cc9..f18d16892 100644
--- a/lnbits/extensions/tpos/tasks.py
+++ b/lnbits/extensions/tpos/tasks.py
@@ -4,7 +4,7 @@ import json
from lnbits.core import db as core_db
from lnbits.core.crud import create_payment
from lnbits.core.models import Payment
-from lnbits.helpers import urlsafe_short_hash
+from lnbits.helpers import get_current_extension_name, urlsafe_short_hash
from lnbits.tasks import internal_invoice_queue, register_invoice_listener
from .crud import get_tpos
@@ -12,7 +12,7 @@ from .crud import get_tpos
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
- register_invoice_listener(invoice_queue)
+ register_invoice_listener(invoice_queue, get_current_extension_name())
while True:
payment = await invoice_queue.get()
diff --git a/lnbits/helpers.py b/lnbits/helpers.py
index e97fc7bbb..e213240cd 100644
--- a/lnbits/helpers.py
+++ b/lnbits/helpers.py
@@ -183,3 +183,26 @@ def template_renderer(additional_folders: List = []) -> Jinja2Templates:
t.env.globals["VENDORED_CSS"] = ["/static/bundle.css"]
return t
+
+
+def get_current_extension_name() -> str:
+ """
+ Returns the name of the extension that calls this method.
+ """
+ import inspect
+ import json
+ import os
+
+ callee_filepath = inspect.stack()[1].filename
+ callee_dirname, callee_filename = os.path.split(callee_filepath)
+
+ path = os.path.normpath(callee_dirname)
+ extension_director_name = path.split(os.sep)[-1]
+ try:
+ config_path = os.path.join(callee_dirname, "config.json")
+ with open(config_path) as json_file:
+ config = json.load(json_file)
+ ext_name = config["name"]
+ except:
+ ext_name = extension_director_name
+ return ext_name
diff --git a/lnbits/tasks.py b/lnbits/tasks.py
index 41287ff24..94e43dcfd 100644
--- a/lnbits/tasks.py
+++ b/lnbits/tasks.py
@@ -1,8 +1,9 @@
import asyncio
import time
import traceback
+import uuid
from http import HTTPStatus
-from typing import Callable, List
+from typing import Callable, Dict, List
from fastapi.exceptions import HTTPException
from loguru import logger
@@ -18,20 +19,6 @@ from lnbits.settings import WALLET
from .core import db
-deferred_async: List[Callable] = []
-
-
-def record_async(func: Callable) -> Callable:
- def recorder(state):
- deferred_async.append(func)
-
- return recorder
-
-
-async def run_deferred_async():
- for func in deferred_async:
- asyncio.create_task(catch_everything_and_restart(func))
-
async def catch_everything_and_restart(func):
try:
@@ -50,18 +37,48 @@ async def send_push_promise(a, b) -> None:
pass
-invoice_listeners: List[asyncio.Queue] = []
+class SseListenersDict(dict):
+ """
+ A dict of sse listeners.
+ """
+
+ def __init__(self, name: str = None):
+ self.name = name or f"sse_listener_{str(uuid.uuid4())[:8]}"
+
+ def __setitem__(self, key, value):
+ assert type(key) == str, f"{key} is not a string"
+ assert type(value) == asyncio.Queue, f"{value} is not an asyncio.Queue"
+ logger.trace(f"sse: adding listener {key} to {self.name}. len = {len(self)+1}")
+ return super().__setitem__(key, value)
+
+ def __delitem__(self, key):
+ logger.trace(f"sse: removing listener from {self.name}. len = {len(self)-1}")
+ return super().__delitem__(key)
+
+ _RaiseKeyError = object() # singleton for no-default behavior
+
+ def pop(self, key, v=_RaiseKeyError) -> None:
+ logger.trace(f"sse: removing listener from {self.name}. len = {len(self)-1}")
+ return super().pop(key)
-def register_invoice_listener(send_chan: asyncio.Queue):
+invoice_listeners: Dict[str, asyncio.Queue] = SseListenersDict("invoice_listeners")
+
+
+def register_invoice_listener(send_chan: asyncio.Queue, name: str = None):
"""
- A method intended for extensions to call when they want to be notified about
- new invoice payments incoming.
+ A method intended for extensions (and core/tasks.py) to call when they want to be notified about
+ new invoice payments incoming. Will emit all incoming payments.
"""
- invoice_listeners.append(send_chan)
+ name_unique = f"{name or 'no_name'}_{str(uuid.uuid4())[:8]}"
+ logger.trace(f"sse: registering invoice listener {name_unique}")
+ invoice_listeners[name_unique] = send_chan
async def webhook_handler():
+ """
+ Returns the webhook_handler for the selected wallet if present. Used by API.
+ """
handler = getattr(WALLET, "webhook_listener", None)
if handler:
return await handler()
@@ -72,18 +89,36 @@ internal_invoice_queue: asyncio.Queue = asyncio.Queue(0)
async def internal_invoice_listener():
+ """
+ internal_invoice_queue will be filled directly in core/services.py
+ after the payment was deemed to be settled internally.
+
+ Called by the app startup sequence.
+ """
while True:
checking_id = await internal_invoice_queue.get()
+ logger.info("> got internal payment notification", checking_id)
asyncio.create_task(invoice_callback_dispatcher(checking_id))
async def invoice_listener():
+ """
+ invoice_listener will collect all invoices that come directly
+ from the backend wallet.
+
+ Called by the app startup sequence.
+ """
async for checking_id in WALLET.paid_invoices_stream():
logger.info("> got a payment notification", checking_id)
asyncio.create_task(invoice_callback_dispatcher(checking_id))
async def check_pending_payments():
+ """
+ check_pending_payments is called during startup to check for pending payments with
+ the backend and also to delete expired invoices. Incoming payments will be
+ checked only once, outgoing pending payments will be checked regularly.
+ """
outgoing = True
incoming = True
@@ -133,9 +168,14 @@ async def perform_balance_checks():
async def invoice_callback_dispatcher(checking_id: str):
+ """
+ Takes incoming payments, sets pending=False, and dispatches them to
+ invoice_listeners from core and extensions.
+ """
payment = await get_standalone_payment(checking_id, incoming=True)
if payment and payment.is_in:
- logger.trace("sending invoice callback for payment", checking_id)
+ logger.trace(f"sse sending invoice callback for payment {checking_id}")
await payment.set_pending(False)
- for send_chan in invoice_listeners:
+ for chan_name, send_chan in invoice_listeners.items():
+ logger.trace(f"sse sending to chan: {chan_name}")
await send_chan.put(payment)
diff --git a/lnbits/wallets/fake.py b/lnbits/wallets/fake.py
index 8424001b2..a07ef4d89 100644
--- a/lnbits/wallets/fake.py
+++ b/lnbits/wallets/fake.py
@@ -8,9 +8,7 @@ from typing import AsyncGenerator, Dict, Optional
from environs import Env # type: ignore
from loguru import logger
-from lnbits.helpers import urlsafe_short_hash
-
-from ..bolt11 import decode, encode
+from ..bolt11 import Invoice, decode, encode
from .base import (
InvoiceResponse,
PaymentResponse,
@@ -24,6 +22,16 @@ env.read_env()
class FakeWallet(Wallet):
+ queue: asyncio.Queue = asyncio.Queue(0)
+ secret: str = env.str("FAKE_WALLET_SECTRET", default="ToTheMoon1")
+ privkey: str = hashlib.pbkdf2_hmac(
+ "sha256",
+ secret.encode("utf-8"),
+ ("FakeWallet").encode("utf-8"),
+ 2048,
+ 32,
+ ).hex()
+
async def status(self) -> StatusResponse:
logger.info(
"FakeWallet funding source is for using LNbits as a centralised, stand-alone payment system with brrrrrr."
@@ -39,18 +47,12 @@ class FakeWallet(Wallet):
) -> InvoiceResponse:
# we set a default secret since FakeWallet is used for internal=True invoices
# and the user might not have configured a secret yet
- secret = env.str("FAKE_WALLET_SECTRET", default="ToTheMoon1")
+
data: Dict = {
"out": False,
"amount": amount,
"currency": "bc",
- "privkey": hashlib.pbkdf2_hmac(
- "sha256",
- secret.encode("utf-8"),
- ("FakeWallet").encode("utf-8"),
- 2048,
- 32,
- ).hex(),
+ "privkey": self.privkey,
"memo": None,
"description_hash": None,
"description": "",
@@ -86,8 +88,9 @@ class FakeWallet(Wallet):
invoice = decode(bolt11)
if (
hasattr(invoice, "checking_id")
- and invoice.checking_id[6:] == data["privkey"][:6] # type: ignore
+ and invoice.checking_id[:6] == self.privkey[:6] # type: ignore
):
+ await self.queue.put(invoice)
return PaymentResponse(True, invoice.payment_hash, 0)
else:
return PaymentResponse(
@@ -101,7 +104,6 @@ class FakeWallet(Wallet):
return PaymentStatus(None)
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
- self.queue: asyncio.Queue = asyncio.Queue(0)
while True:
- value = await self.queue.get()
- yield value
+ value: Invoice = await self.queue.get()
+ yield value.payment_hash
diff --git a/poetry.lock b/poetry.lock
index 2a57a5c12..343fffbfb 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -46,6 +46,17 @@ category = "main"
optional = false
python-versions = "*"
+[[package]]
+name = "async-timeout"
+version = "4.0.2"
+description = "Timeout context manager for asyncio programs"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""}
+
[[package]]
name = "attrs"
version = "21.2.0"
@@ -111,7 +122,7 @@ jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
[[package]]
-name = "cerberus"
+name = "Cerberus"
version = "1.3.4"
description = "Lightweight, extensible schema and data validation tool for Python dictionaries."
category = "main"
@@ -402,7 +413,7 @@ plugins = ["setuptools"]
requirements_deprecated_finder = ["pip-api", "pipreqs"]
[[package]]
-name = "jinja2"
+name = "Jinja2"
version = "3.0.1"
description = "A very fast and expressive template engine."
category = "main"
@@ -444,7 +455,7 @@ win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
dev = ["Sphinx (>=2.2.1)", "black (>=19.10b0)", "codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "isort (>=5.1.1)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)", "tox-travis (>=0.12)"]
[[package]]
-name = "markupsafe"
+name = "MarkupSafe"
version = "2.0.1"
description = "Safely add untrusted strings to HTML/XML markup."
category = "main"
@@ -648,12 +659,12 @@ optional = false
python-versions = ">=3.7,<4.0"
[package.dependencies]
-pyln-bolt7 = ">=1.0,<2.0"
-pyln-proto = ">=0.11,<0.12"
+pyln-bolt7 = ">=1.0"
+pyln-proto = ">=0.12"
[[package]]
name = "pyln-proto"
-version = "0.11.1"
+version = "0.12.0"
description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)."
category = "main"
optional = false
@@ -686,7 +697,7 @@ optional = false
python-versions = "*"
[[package]]
-name = "pyqrcode"
+name = "PyQRCode"
version = "1.2.1"
description = "A QR code generator written purely in Python with SVG, EPS, PNG and terminal output."
category = "main"
@@ -697,7 +708,7 @@ python-versions = "*"
PNG = ["pypng (>=0.0.13)"]
[[package]]
-name = "pyscss"
+name = "pyScss"
version = "1.4.0"
description = "pyScss, a Scss compiler for Python"
category = "main"
@@ -780,7 +791,7 @@ python-versions = ">=3.5"
cli = ["click (>=5.0)"]
[[package]]
-name = "pyyaml"
+name = "PyYAML"
version = "5.4.1"
description = "YAML parser and emitter for Python"
category = "main"
@@ -788,7 +799,7 @@ optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
[[package]]
-name = "represent"
+name = "Represent"
version = "1.6.0.post0"
description = "Create __repr__ automatically or declaratively."
category = "main"
@@ -864,7 +875,7 @@ optional = false
python-versions = ">=3.5"
[[package]]
-name = "sqlalchemy"
+name = "SQLAlchemy"
version = "1.3.23"
description = "Database Abstraction Library"
category = "main"
@@ -1059,6 +1070,10 @@ asn1crypto = [
{file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"},
{file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"},
]
+async-timeout = [
+ {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
+ {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
+]
attrs = [
{file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
{file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
@@ -1101,7 +1116,7 @@ black = [
{file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"},
{file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"},
]
-cerberus = [
+Cerberus = [
{file = "Cerberus-1.3.4.tar.gz", hash = "sha256:d1b21b3954b2498d9a79edf16b3170a3ac1021df88d197dc2ce5928ba519237c"},
]
certifi = [
@@ -1413,7 +1428,7 @@ isort = [
{file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
{file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
]
-jinja2 = [
+Jinja2 = [
{file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"},
{file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"},
]
@@ -1425,7 +1440,7 @@ loguru = [
{file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"},
{file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"},
]
-markupsafe = [
+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"},
@@ -1681,8 +1696,8 @@ pyln-client = [
{file = "pyln_client-0.11.1-py3-none-any.whl", hash = "sha256:497db443406b80c98c0434e2938eb1b2a17e88fd9aa63b018124068198df6141"},
]
pyln-proto = [
- {file = "pyln-proto-0.11.1.tar.gz", hash = "sha256:9bed240f41917c4fd526b767218a77d0fbe69242876eef72c35a856796f922d6"},
- {file = "pyln_proto-0.11.1-py3-none-any.whl", hash = "sha256:27b2e04a81b894f69018279c0ce4aa2e7ccd03b86dd9783f96b9d8d1498c8393"},
+ {file = "pyln-proto-0.12.0.tar.gz", hash = "sha256:3214d99d8385f2135a94937f0dc1da626a33b257e9ebc320841656edaefabbe5"},
+ {file = "pyln_proto-0.12.0-py3-none-any.whl", hash = "sha256:dedef5d8e476a9ade5a0b2eb919ccc37e4a57f2a78fdc399f1c5e0de17e41604"},
]
pyparsing = [
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
@@ -1691,11 +1706,11 @@ pyparsing = [
pypng = [
{file = "pypng-0.0.21-py3-none-any.whl", hash = "sha256:76f8a1539ec56451da7ab7121f12a361969fe0f2d48d703d198ce2a99d6c5afd"},
]
-pyqrcode = [
+PyQRCode = [
{file = "PyQRCode-1.2.1.tar.gz", hash = "sha256:fdbf7634733e56b72e27f9bce46e4550b75a3a2c420414035cae9d9d26b234d5"},
{file = "PyQRCode-1.2.1.zip", hash = "sha256:1b2812775fa6ff5c527977c4cd2ccb07051ca7d0bc0aecf937a43864abe5eff6"},
]
-pyscss = [
+pyScss = [
{file = "pyScss-1.4.0.tar.gz", hash = "sha256:8f35521ffe36afa8b34c7d6f3195088a7057c185c2b8f15ee459ab19748669ff"},
]
PySocks = [
@@ -1719,7 +1734,7 @@ 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 = [
+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"},
@@ -1750,7 +1765,7 @@ pyyaml = [
{file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
{file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
]
-represent = [
+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"},
]
@@ -1799,7 +1814,7 @@ sniffio = [
{file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
{file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
]
-sqlalchemy = [
+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"},
diff --git a/pyproject.toml b/pyproject.toml
index 864500f7b..19dac8600 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -15,7 +15,6 @@ 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"
@@ -62,6 +61,8 @@ cffi = "1.15.0"
websocket-client = "1.3.3"
grpcio = "^1.49.1"
protobuf = "^4.21.6"
+Cerberus = "^1.3.4"
+async-timeout = "^4.0.2"
pyln-client = "0.11.1"
[tool.poetry.dev-dependencies]
diff --git a/requirements.txt b/requirements.txt
index 697ea1d4d..eb9a6e5e8 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -51,3 +51,5 @@ uvloop==0.16.0
watchfiles==0.16.0
websockets==10.3
websocket-client==1.3.3
+async-timeout==4.0.2
+setuptools==65.4.0
\ No newline at end of file
From b6755abc8e9911c34b96d3039140b2b64ff5634f Mon Sep 17 00:00:00 2001
From: HODLmeTight <35168804+TrezorHannes@users.noreply.github.com>
Date: Wed, 5 Oct 2022 09:16:54 +0200
Subject: [PATCH 26/57] updated poetry install notes
- referenced option for python3.10 and newer
- poetry install with --only main, instead of --no-dev (deprecated)
---
docs/guide/installation.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index 87679ed5b..5e9fdb28a 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -26,8 +26,8 @@ sudo apt install python3.9 python3.9-distutils
curl -sSL https://install.python-poetry.org | python3 -
export PATH="/home/ubuntu/.local/bin:$PATH" # or whatever is suggested in the poetry install notes printed to terminal
-poetry env use python3.9
-poetry install --no-dev
+poetry env use python3.9 # you can exchange with python3.10 or newer versions. Identify your version with python3 --version and specify here
+poetry install --only main
poetry run python build.py
mkdir data
From bc2068cc87f5d7b867ce0e9f4ccc8fabf8af7732 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 6 Oct 2022 09:42:38 +0200
Subject: [PATCH 27/57] fix poetry lock in main
---
poetry.lock | 152 ++++++++++++++++++++++++++--------------------------
1 file changed, 76 insertions(+), 76 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 343fffbfb..5b283d758 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -196,7 +196,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "coverage"
-version = "6.4.4"
+version = "6.5.0"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
@@ -589,7 +589,7 @@ testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "protobuf"
-version = "4.21.6"
+version = "4.21.7"
description = ""
category = "main"
optional = false
@@ -659,12 +659,12 @@ optional = false
python-versions = ">=3.7,<4.0"
[package.dependencies]
-pyln-bolt7 = ">=1.0"
-pyln-proto = ">=0.12"
+pyln-bolt7 = ">=1.0,<2.0"
+pyln-proto = ">=0.11,<0.12"
[[package]]
name = "pyln-proto"
-version = "0.12.0"
+version = "0.11.1"
description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)."
category = "main"
optional = false
@@ -839,14 +839,14 @@ cffi = ">=1.3.0"
[[package]]
name = "setuptools"
-version = "65.4.0"
+version = "65.4.1"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "main"
optional = false
python-versions = ">=3.7"
[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
@@ -1051,7 +1051,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (
[metadata]
lock-version = "1.1"
python-versions = "^3.10 | ^3.9 | ^3.8 | ^3.7"
-content-hash = "72e4462285d0bc5e2cb83c88c613726beced959b268bd30b984d8baaeff178ea"
+content-hash = "c4a01d5bfc24a8008348b6bd954717354554310afaaecbfc2a14222ad25aca42"
[metadata.files]
aiofiles = [
@@ -1224,56 +1224,56 @@ colorama = [
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
]
coverage = [
- {file = "coverage-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc"},
- {file = "coverage-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60"},
- {file = "coverage-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdbb0d89923c80dbd435b9cf8bba0ff55585a3cdb28cbec65f376c041472c60d"},
- {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f9346aeebea54e845d29b487eb38ec95f2ecf3558a3cffb26ee3f0dcc3e760"},
- {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c499c14efd858b98c4e03595bf914089b98400d30789511577aa44607a1b74"},
- {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c35cca192ba700979d20ac43024a82b9b32a60da2f983bec6c0f5b84aead635c"},
- {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9cc4f107009bca5a81caef2fca843dbec4215c05e917a59dec0c8db5cff1d2aa"},
- {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f444627b3664b80d078c05fe6a850dd711beeb90d26731f11d492dcbadb6973"},
- {file = "coverage-6.4.4-cp310-cp310-win32.whl", hash = "sha256:66e6df3ac4659a435677d8cd40e8eb1ac7219345d27c41145991ee9bf4b806a0"},
- {file = "coverage-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:35ef1f8d8a7a275aa7410d2f2c60fa6443f4a64fae9be671ec0696a68525b875"},
- {file = "coverage-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c1328d0c2f194ffda30a45f11058c02410e679456276bfa0bbe0b0ee87225fac"},
- {file = "coverage-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61b993f3998ee384935ee423c3d40894e93277f12482f6e777642a0141f55782"},
- {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5dd4b8e9cd0deb60e6fcc7b0647cbc1da6c33b9e786f9c79721fd303994832f"},
- {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7026f5afe0d1a933685d8f2169d7c2d2e624f6255fb584ca99ccca8c0e966fd7"},
- {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9c7b9b498eb0c0d48b4c2abc0e10c2d78912203f972e0e63e3c9dc21f15abdaa"},
- {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ee2b2fb6eb4ace35805f434e0f6409444e1466a47f620d1d5763a22600f0f892"},
- {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab066f5ab67059d1f1000b5e1aa8bbd75b6ed1fc0014559aea41a9eb66fc2ce0"},
- {file = "coverage-6.4.4-cp311-cp311-win32.whl", hash = "sha256:9d6e1f3185cbfd3d91ac77ea065d85d5215d3dfa45b191d14ddfcd952fa53796"},
- {file = "coverage-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e3d3c4cc38b2882f9a15bafd30aec079582b819bec1b8afdbde8f7797008108a"},
- {file = "coverage-6.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a095aa0a996ea08b10580908e88fbaf81ecf798e923bbe64fb98d1807db3d68a"},
- {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef6f44409ab02e202b31a05dd6666797f9de2aa2b4b3534e9d450e42dea5e817"},
- {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b7101938584d67e6f45f0015b60e24a95bf8dea19836b1709a80342e01b472f"},
- {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a32ec68d721c3d714d9b105c7acf8e0f8a4f4734c811eda75ff3718570b5e3"},
- {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6a864733b22d3081749450466ac80698fe39c91cb6849b2ef8752fd7482011f3"},
- {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08002f9251f51afdcc5e3adf5d5d66bb490ae893d9e21359b085f0e03390a820"},
- {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a3b2752de32c455f2521a51bd3ffb53c5b3ae92736afde67ce83477f5c1dd928"},
- {file = "coverage-6.4.4-cp37-cp37m-win32.whl", hash = "sha256:f855b39e4f75abd0dfbcf74a82e84ae3fc260d523fcb3532786bcbbcb158322c"},
- {file = "coverage-6.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ee6ae6bbcac0786807295e9687169fba80cb0617852b2fa118a99667e8e6815d"},
- {file = "coverage-6.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:564cd0f5b5470094df06fab676c6d77547abfdcb09b6c29c8a97c41ad03b103c"},
- {file = "coverage-6.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cbbb0e4cd8ddcd5ef47641cfac97d8473ab6b132dd9a46bacb18872828031685"},
- {file = "coverage-6.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6113e4df2fa73b80f77663445be6d567913fb3b82a86ceb64e44ae0e4b695de1"},
- {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d032bfc562a52318ae05047a6eb801ff31ccee172dc0d2504614e911d8fa83e"},
- {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e431e305a1f3126477abe9a184624a85308da8edf8486a863601d58419d26ffa"},
- {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cf2afe83a53f77aec067033199797832617890e15bed42f4a1a93ea24794ae3e"},
- {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:783bc7c4ee524039ca13b6d9b4186a67f8e63d91342c713e88c1865a38d0892a"},
- {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ff934ced84054b9018665ca3967fc48e1ac99e811f6cc99ea65978e1d384454b"},
- {file = "coverage-6.4.4-cp38-cp38-win32.whl", hash = "sha256:e1fabd473566fce2cf18ea41171d92814e4ef1495e04471786cbc943b89a3781"},
- {file = "coverage-6.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:4179502f210ebed3ccfe2f78bf8e2d59e50b297b598b100d6c6e3341053066a2"},
- {file = "coverage-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c0b9e9b572893cdb0a00e66cf961a238f8d870d4e1dc8e679eb8bdc2eb1b86"},
- {file = "coverage-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc600f6ec19b273da1d85817eda339fb46ce9eef3e89f220055d8696e0a06908"},
- {file = "coverage-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a98d6bf6d4ca5c07a600c7b4e0c5350cd483c85c736c522b786be90ea5bac4f"},
- {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01778769097dbd705a24e221f42be885c544bb91251747a8a3efdec6eb4788f2"},
- {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfa0b97eb904255e2ab24166071b27408f1f69c8fbda58e9c0972804851e0558"},
- {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fcbe3d9a53e013f8ab88734d7e517eb2cd06b7e689bedf22c0eb68db5e4a0a19"},
- {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:15e38d853ee224e92ccc9a851457fb1e1f12d7a5df5ae44544ce7863691c7a0d"},
- {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6913dddee2deff8ab2512639c5168c3e80b3ebb0f818fed22048ee46f735351a"},
- {file = "coverage-6.4.4-cp39-cp39-win32.whl", hash = "sha256:354df19fefd03b9a13132fa6643527ef7905712109d9c1c1903f2133d3a4e145"},
- {file = "coverage-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:1238b08f3576201ebf41f7c20bf59baa0d05da941b123c6656e42cdb668e9827"},
- {file = "coverage-6.4.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1"},
- {file = "coverage-6.4.4.tar.gz", hash = "sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58"},
+ {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"},
+ {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"},
+ {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"},
+ {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"},
+ {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"},
+ {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"},
+ {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"},
+ {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"},
+ {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"},
+ {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"},
+ {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"},
+ {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"},
+ {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"},
+ {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"},
+ {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"},
+ {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"},
+ {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"},
+ {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"},
+ {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"},
+ {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"},
+ {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"},
+ {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"},
+ {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"},
+ {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"},
+ {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"},
+ {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"},
+ {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"},
+ {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"},
+ {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"},
+ {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"},
+ {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"},
+ {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"},
+ {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"},
+ {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"},
+ {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"},
+ {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"},
+ {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"},
+ {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"},
+ {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"},
+ {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"},
+ {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"},
+ {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"},
+ {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"},
+ {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"},
+ {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"},
+ {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"},
+ {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"},
+ {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"},
+ {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"},
+ {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"},
]
cryptography = [
{file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:4e2dddd38a5ba733be6a025a1475a9f45e4e41139d1321f412c6b360b19070b6"},
@@ -1573,20 +1573,20 @@ pluggy = [
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
protobuf = [
- {file = "protobuf-4.21.6-cp310-abi3-win32.whl", hash = "sha256:49f88d56a9180dbb7f6199c920f5bb5c1dd0172f672983bb281298d57c2ac8eb"},
- {file = "protobuf-4.21.6-cp310-abi3-win_amd64.whl", hash = "sha256:7a6cc8842257265bdfd6b74d088b829e44bcac3cca234c5fdd6052730017b9ea"},
- {file = "protobuf-4.21.6-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:ba596b9ffb85c909fcfe1b1a23136224ed678af3faf9912d3fa483d5f9813c4e"},
- {file = "protobuf-4.21.6-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4143513c766db85b9d7c18dbf8339673c8a290131b2a0fe73855ab20770f72b0"},
- {file = "protobuf-4.21.6-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:b6cea204865595a92a7b240e4b65bcaaca3ad5d2ce25d9db3756eba06041138e"},
- {file = "protobuf-4.21.6-cp37-cp37m-win32.whl", hash = "sha256:9666da97129138585b26afcb63ad4887f602e169cafe754a8258541c553b8b5d"},
- {file = "protobuf-4.21.6-cp37-cp37m-win_amd64.whl", hash = "sha256:308173d3e5a3528787bb8c93abea81d5a950bdce62840d9760effc84127fb39c"},
- {file = "protobuf-4.21.6-cp38-cp38-win32.whl", hash = "sha256:aa29113ec901281f29d9d27b01193407a98aa9658b8a777b0325e6d97149f5ce"},
- {file = "protobuf-4.21.6-cp38-cp38-win_amd64.whl", hash = "sha256:8f9e60f7d44592c66e7b332b6a7b4b6e8d8b889393c79dbc3a91f815118f8eac"},
- {file = "protobuf-4.21.6-cp39-cp39-win32.whl", hash = "sha256:80e6540381080715fddac12690ee42d087d0d17395f8d0078dfd6f1181e7be4c"},
- {file = "protobuf-4.21.6-cp39-cp39-win_amd64.whl", hash = "sha256:77b355c8604fe285536155286b28b0c4cbc57cf81b08d8357bf34829ea982860"},
- {file = "protobuf-4.21.6-py2.py3-none-any.whl", hash = "sha256:07a0bb9cc6114f16a39c866dc28b6e3d96fa4ffb9cc1033057412547e6e75cb9"},
- {file = "protobuf-4.21.6-py3-none-any.whl", hash = "sha256:c7c864148a237f058c739ae7a05a2b403c0dfa4ce7d1f3e5213f352ad52d57c6"},
- {file = "protobuf-4.21.6.tar.gz", hash = "sha256:6b1040a5661cd5f6e610cbca9cfaa2a17d60e2bb545309bc1b278bb05be44bdd"},
+ {file = "protobuf-4.21.7-cp310-abi3-win32.whl", hash = "sha256:c7cb105d69a87416bd9023e64324e1c089593e6dae64d2536f06bcbe49cd97d8"},
+ {file = "protobuf-4.21.7-cp310-abi3-win_amd64.whl", hash = "sha256:3ec85328a35a16463c6f419dbce3c0fc42b3e904d966f17f48bae39597c7a543"},
+ {file = "protobuf-4.21.7-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:db9056b6a11cb5131036d734bcbf91ef3ef9235d6b681b2fc431cbfe5a7f2e56"},
+ {file = "protobuf-4.21.7-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:ca200645d6235ce0df3ccfdff1567acbab35c4db222a97357806e015f85b5744"},
+ {file = "protobuf-4.21.7-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:b019c79e23a80735cc8a71b95f76a49a262f579d6b84fd20a0b82279f40e2cc1"},
+ {file = "protobuf-4.21.7-cp37-cp37m-win32.whl", hash = "sha256:d3f89ccf7182293feba2de2739c8bf34fed1ed7c65a5cf987be00311acac57c1"},
+ {file = "protobuf-4.21.7-cp37-cp37m-win_amd64.whl", hash = "sha256:a74d96cd960b87b4b712797c741bb3ea3a913f5c2dc4b6cbe9c0f8360b75297d"},
+ {file = "protobuf-4.21.7-cp38-cp38-win32.whl", hash = "sha256:8e09d1916386eca1ef1353767b6efcebc0a6859ed7f73cb7fb974feba3184830"},
+ {file = "protobuf-4.21.7-cp38-cp38-win_amd64.whl", hash = "sha256:9e355f2a839d9930d83971b9f562395e13493f0e9211520f8913bd11efa53c02"},
+ {file = "protobuf-4.21.7-cp39-cp39-win32.whl", hash = "sha256:f370c0a71712f8965023dd5b13277444d3cdfecc96b2c778b0e19acbfd60df6e"},
+ {file = "protobuf-4.21.7-cp39-cp39-win_amd64.whl", hash = "sha256:9643684232b6b340b5e63bb69c9b4904cdd39e4303d498d1a92abddc7e895b7f"},
+ {file = "protobuf-4.21.7-py2.py3-none-any.whl", hash = "sha256:8066322588d4b499869bf9f665ebe448e793036b552f68c585a9b28f1e393f66"},
+ {file = "protobuf-4.21.7-py3-none-any.whl", hash = "sha256:58b81358ec6c0b5d50df761460ae2db58405c063fd415e1101209221a0a810e1"},
+ {file = "protobuf-4.21.7.tar.gz", hash = "sha256:71d9dba03ed3432c878a801e2ea51e034b0ea01cf3a4344fb60166cb5f6c8757"},
]
psycopg2-binary = [
{file = "psycopg2-binary-2.9.1.tar.gz", hash = "sha256:b0221ca5a9837e040ebf61f48899926b5783668b7807419e4adae8175a31f773"},
@@ -1696,8 +1696,8 @@ pyln-client = [
{file = "pyln_client-0.11.1-py3-none-any.whl", hash = "sha256:497db443406b80c98c0434e2938eb1b2a17e88fd9aa63b018124068198df6141"},
]
pyln-proto = [
- {file = "pyln-proto-0.12.0.tar.gz", hash = "sha256:3214d99d8385f2135a94937f0dc1da626a33b257e9ebc320841656edaefabbe5"},
- {file = "pyln_proto-0.12.0-py3-none-any.whl", hash = "sha256:dedef5d8e476a9ade5a0b2eb919ccc37e4a57f2a78fdc399f1c5e0de17e41604"},
+ {file = "pyln-proto-0.11.1.tar.gz", hash = "sha256:9bed240f41917c4fd526b767218a77d0fbe69242876eef72c35a856796f922d6"},
+ {file = "pyln_proto-0.11.1-py3-none-any.whl", hash = "sha256:27b2e04a81b894f69018279c0ce4aa2e7ccd03b86dd9783f96b9d8d1498c8393"},
]
pyparsing = [
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
@@ -1799,8 +1799,8 @@ secp256k1 = [
{file = "secp256k1-0.14.0.tar.gz", hash = "sha256:82c06712d69ef945220c8b53c1a0d424c2ff6a1f64aee609030df79ad8383397"},
]
setuptools = [
- {file = "setuptools-65.4.0-py3-none-any.whl", hash = "sha256:c2d2709550f15aab6c9110196ea312f468f41cd546bceb24127a1be6fdcaeeb1"},
- {file = "setuptools-65.4.0.tar.gz", hash = "sha256:a8f6e213b4b0661f590ccf40de95d28a177cd747d098624ad3f69c40287297e9"},
+ {file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"},
+ {file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"},
]
shortuuid = [
{file = "shortuuid-1.0.1-py3-none-any.whl", hash = "sha256:492c7402ff91beb1342a5898bd61ea953985bf24a41cd9f247409aa2e03c8f77"},
From 79319faa2146da17cd22467d9dd4762c42540f67 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Thu, 6 Oct 2022 10:17:21 +0200
Subject: [PATCH 28/57] make workflows only run on pull_request
---
.github/workflows/codeql.yml | 2 --
.github/workflows/formatting.yml | 2 --
.github/workflows/mypy.yml | 2 +-
.github/workflows/regtest.yml | 2 +-
.github/workflows/tests.yml | 2 +-
5 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 876c8b8af..bbe959837 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -1,8 +1,6 @@
name: codeql
on:
- push:
- branches: [main, ]
pull_request:
branches: [main]
schedule:
diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml
index e3d0fd35d..21c7fb38e 100644
--- a/.github/workflows/formatting.yml
+++ b/.github/workflows/formatting.yml
@@ -1,8 +1,6 @@
name: formatting
on:
- push:
- branches: [ main ]
pull_request:
branches: [ main ]
diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml
index d80da678a..96b574d2d 100644
--- a/.github/workflows/mypy.yml
+++ b/.github/workflows/mypy.yml
@@ -1,6 +1,6 @@
name: mypy
-on: [push, pull_request]
+on: [pull_request]
jobs:
check:
diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml
index 2d7aae6b3..f0adb3ac5 100644
--- a/.github/workflows/regtest.yml
+++ b/.github/workflows/regtest.yml
@@ -1,6 +1,6 @@
name: regtest
-on: [push, pull_request]
+on: [pull_request]
jobs:
LndRestWallet:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 5d368fbb1..c7b6e44bb 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -1,6 +1,6 @@
name: tests
-on: [push, pull_request]
+on: [pull_request]
jobs:
venv-sqlite:
From 63e4f7d59c0505654d47b8793738211966b9b776 Mon Sep 17 00:00:00 2001
From: HODLmeTight <35168804+TrezorHannes@users.noreply.github.com>
Date: Thu, 6 Oct 2022 14:25:14 +0200
Subject: [PATCH 29/57] changed comment places and small adjustments
- #comments in own lines
- cleaned up some ominous settings
- added --debug option for running the server
---
docs/guide/installation.md | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index 5e9fdb28a..37104fde2 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -18,21 +18,25 @@ If you have problems installing LNbits using these instructions, please have a l
git clone https://github.com/lnbits/lnbits-legend.git
cd lnbits-legend/
-# for making sure python 3.9 is installed, skip if installed
+# for making sure python 3.9 is installed, skip if installed. To check your installed version: python3 --version
sudo apt update
sudo apt install software-properties-common
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt install python3.9 python3.9-distutils
curl -sSL https://install.python-poetry.org | python3 -
-export PATH="/home/ubuntu/.local/bin:$PATH" # or whatever is suggested in the poetry install notes printed to terminal
-poetry env use python3.9 # you can exchange with python3.10 or newer versions. Identify your version with python3 --version and specify here
+# Once the above poetry install is completed, use the installation path printed to terminal and replace in the following command
+export PATH="/home/user/.local/bin:$PATH"
+# Next command, you can exchange with python3.10 or newer versions.
+# Identify your version with python3 --version and specify in the next line
+# command is only needed when your default python is not ^3.9 or ^3.10
+poetry env use python3.9
poetry install --only main
-poetry run python build.py
mkdir data
cp .env.example .env
-nano .env # set funding source
+# set funding source amongst other options
+nano .env
```
#### Running the server
@@ -40,6 +44,7 @@ nano .env # set funding source
```sh
poetry run lnbits
# To change port/host pass 'poetry run lnbits --port 9000 --host 0.0.0.0'
+# add --debug for slightly more verbose output
```
## Option 2: Nix
From 36b60ad318fffa87d8ec55753538525531d76dec Mon Sep 17 00:00:00 2001
From: HODLmeTight <35168804+TrezorHannes@users.noreply.github.com>
Date: Thu, 6 Oct 2022 14:50:33 +0200
Subject: [PATCH 30/57] added debug option for .env
---
docs/guide/installation.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index 37104fde2..6b95f93b1 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -44,7 +44,8 @@ nano .env
```sh
poetry run lnbits
# To change port/host pass 'poetry run lnbits --port 9000 --host 0.0.0.0'
-# add --debug for slightly more verbose output
+# adding --debug in the start-up command above to help your troubleshooting and generate a more verbose output
+# Note that you have to add the line DEBUG=true in your .env file, too.
```
## Option 2: Nix
From 27bae6a0c6cce731256095465905c92c1547c1e2 Mon Sep 17 00:00:00 2001
From: ben
Date: Thu, 6 Oct 2022 14:16:21 +0100
Subject: [PATCH 31/57] adds atm check, so withdraw isnt cancelled on false
call by wallets
---
lnbits/extensions/lnurldevice/lnurl.py | 29 +++++++++++++++-----------
1 file changed, 17 insertions(+), 12 deletions(-)
diff --git a/lnbits/extensions/lnurldevice/lnurl.py b/lnbits/extensions/lnurldevice/lnurl.py
index df0cd4b8f..ec7f3b486 100644
--- a/lnbits/extensions/lnurldevice/lnurl.py
+++ b/lnbits/extensions/lnurldevice/lnurl.py
@@ -184,22 +184,27 @@ async def lnurl_callback(
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="lnurldevice not found."
)
- if pr:
- if lnurldevicepayment.id != k1:
- return {"status": "ERROR", "reason": "Bad K1"}
- if lnurldevicepayment.payhash != "payment_hash":
- return {"status": "ERROR", "reason": f"Payment already claimed"}
+ if device.device == "atm":
+ if not pr:
+ raise HTTPException(
+ status_code=HTTPStatus.FORBIDDEN, detail="No payment request"
+ )
+ else:
+ if lnurldevicepayment.id != k1:
+ return {"status": "ERROR", "reason": "Bad K1"}
+ if lnurldevicepayment.payhash != "payment_hash":
+ return {"status": "ERROR", "reason": f"Payment already claimed"}
lnurldevicepayment = await update_lnurldevicepayment(
lnurldevicepayment_id=paymentid, payhash=lnurldevicepayment.payload
)
- await pay_invoice(
- wallet_id=device.wallet,
- payment_request=pr,
- max_sat=lnurldevicepayment.sats / 1000,
- extra={"tag": "withdraw"},
- )
- return {"status": "OK"}
+ await pay_invoice(
+ wallet_id=device.wallet,
+ payment_request=pr,
+ max_sat=lnurldevicepayment.sats / 1000,
+ extra={"tag": "withdraw"},
+ )
+ return {"status": "OK"}
payment_hash, payment_request = await create_invoice(
wallet_id=device.wallet,
From 8e8bf08ea587aa9013a966a807600812fee3c780 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 6 Oct 2022 15:21:09 +0100
Subject: [PATCH 32/57] fix issue with splitting to multiple wallets-queue
---
lnbits/extensions/splitpayments/tasks.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/lnbits/extensions/splitpayments/tasks.py b/lnbits/extensions/splitpayments/tasks.py
index b7cf1750c..cfc6c226f 100644
--- a/lnbits/extensions/splitpayments/tasks.py
+++ b/lnbits/extensions/splitpayments/tasks.py
@@ -28,6 +28,10 @@ async def on_invoice_paid(payment: Payment) -> None:
# now we make some special internal transfers (from no one to the receiver)
targets = await get_targets(payment.wallet_id)
+
+ if not targets:
+ return
+
transfers = [
(target.wallet, int(target.percent * payment.amount / 100))
for target in targets
@@ -41,9 +45,6 @@ async def on_invoice_paid(payment: Payment) -> None:
)
return
- if not targets:
- return
-
# mark the original payment with one extra key, "splitted"
# (this prevents us from doing this process again and it's informative)
# and reduce it by the amount we're going to send to the producer
@@ -76,5 +77,5 @@ async def on_invoice_paid(payment: Payment) -> None:
)
# manually send this for now
- await internal_invoice_queue.put(internal_checking_id)
+ await internal_invoice_queue.put(internal_checking_id)
return
From 11ec82e33962462880d08a9dc2ab6e828762edbd Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 6 Oct 2022 15:21:53 +0100
Subject: [PATCH 33/57] floor amount when not whole sat
---
lnbits/extensions/scrub/tasks.py | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/lnbits/extensions/scrub/tasks.py b/lnbits/extensions/scrub/tasks.py
index 320d34da0..62adc5e5e 100644
--- a/lnbits/extensions/scrub/tasks.py
+++ b/lnbits/extensions/scrub/tasks.py
@@ -1,6 +1,7 @@
import asyncio
import json
from http import HTTPStatus
+from math import floor
from urllib.parse import urlparse
import httpx
@@ -26,7 +27,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
# (avoid loops)
- if "scrubed" == payment.extra.get("tag"):
+ if payment.extra.get("tag") == "scrubed":
# already scrubbed
return
@@ -42,12 +43,13 @@ async def on_invoice_paid(payment: Payment) -> None:
# I REALLY HATE THIS DUPLICATION OF CODE!! CORE/VIEWS/API.PY, LINE 267
domain = urlparse(data["callback"]).netloc
-
+ rounded_amount = floor(payment.amount / 1000) * 1000
+
async with httpx.AsyncClient() as client:
try:
r = await client.get(
data["callback"],
- params={"amount": payment.amount},
+ params={"amount": rounded_amount},
timeout=40,
)
if r.is_error:
@@ -66,7 +68,8 @@ async def on_invoice_paid(payment: Payment) -> None:
)
invoice = bolt11.decode(params["pr"])
- if invoice.amount_msat != payment.amount:
+
+ if invoice.amount_msat != rounded_amount:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail=f"{domain} returned an invalid invoice. Expected {payment.amount} msat, got {invoice.amount_msat}.",
From 7316ef7dbb67a9327597e2e97aa7ef54f7a08ac0 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 6 Oct 2022 15:33:20 +0100
Subject: [PATCH 34/57] add disclaimer about whole (integer) sats
---
lnbits/extensions/scrub/README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lnbits/extensions/scrub/README.md b/lnbits/extensions/scrub/README.md
index 680c5e6db..3b8d0b2d7 100644
--- a/lnbits/extensions/scrub/README.md
+++ b/lnbits/extensions/scrub/README.md
@@ -4,6 +4,8 @@
SCRUB is a small but handy extension that allows a user to take advantage of all the functionalities inside **LNbits** and upon a payment received to your LNbits wallet, automatically forward it to your desired wallet via LNURL or LNAddress!
+Only whole values, integers, are Scrubbed, amounts will be rounded down (example: 6.3 will be 6)! The decimals, if existing, will be kept in your wallet!
+
[**Wallets supporting LNURL**](https://github.com/fiatjaf/awesome-lnurl#wallets)
## Usage
From 92a756b9bb92078bb937f4a22c9b35b033f44fed Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 6 Oct 2022 15:34:37 +0100
Subject: [PATCH 35/57] make format
---
lnbits/extensions/scrub/tasks.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/scrub/tasks.py b/lnbits/extensions/scrub/tasks.py
index 62adc5e5e..852f3860c 100644
--- a/lnbits/extensions/scrub/tasks.py
+++ b/lnbits/extensions/scrub/tasks.py
@@ -44,7 +44,7 @@ async def on_invoice_paid(payment: Payment) -> None:
# I REALLY HATE THIS DUPLICATION OF CODE!! CORE/VIEWS/API.PY, LINE 267
domain = urlparse(data["callback"]).netloc
rounded_amount = floor(payment.amount / 1000) * 1000
-
+
async with httpx.AsyncClient() as client:
try:
r = await client.get(
@@ -68,7 +68,7 @@ async def on_invoice_paid(payment: Payment) -> None:
)
invoice = bolt11.decode(params["pr"])
-
+
if invoice.amount_msat != rounded_amount:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
From 3222ae198d4f4bda826f1a63f861d5e90d0df4ac Mon Sep 17 00:00:00 2001
From: ben
Date: Thu, 6 Oct 2022 17:10:15 +0100
Subject: [PATCH 36/57] Adding bitcoinswitch to lnurldevices
---
lnbits/extensions/lnurldevice/__init__.py | 7 +++-
lnbits/extensions/lnurldevice/lnurl.py | 40 ++++++++++++++++--
lnbits/extensions/lnurldevice/migrations.py | 6 +++
lnbits/extensions/lnurldevice/models.py | 2 +
lnbits/extensions/lnurldevice/tasks.py | 39 ++++++++++++++++++
lnbits/extensions/lnurldevice/views.py | 45 +++++++++++++++++++++
6 files changed, 135 insertions(+), 4 deletions(-)
create mode 100644 lnbits/extensions/lnurldevice/tasks.py
diff --git a/lnbits/extensions/lnurldevice/__init__.py b/lnbits/extensions/lnurldevice/__init__.py
index 54849c957..dc4456b4d 100644
--- a/lnbits/extensions/lnurldevice/__init__.py
+++ b/lnbits/extensions/lnurldevice/__init__.py
@@ -2,6 +2,7 @@ from fastapi import APIRouter
from lnbits.db import Database
from lnbits.helpers import template_renderer
+from lnbits.tasks import catch_everything_and_restart
db = Database("ext_lnurldevice")
@@ -11,7 +12,11 @@ lnurldevice_ext: APIRouter = APIRouter(prefix="/lnurldevice", tags=["lnurldevice
def lnurldevice_renderer():
return template_renderer(["lnbits/extensions/lnurldevice/templates"])
-
+from .tasks import wait_for_paid_invoices
from .lnurl import * # noqa
from .views import * # noqa
from .views_api import * # noqa
+
+def lnurldevice_start():
+ loop = asyncio.get_event_loop()
+ loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))
diff --git a/lnbits/extensions/lnurldevice/lnurl.py b/lnbits/extensions/lnurldevice/lnurl.py
index ec7f3b486..a2bc0dd4f 100644
--- a/lnbits/extensions/lnurldevice/lnurl.py
+++ b/lnbits/extensions/lnurldevice/lnurl.py
@@ -102,7 +102,22 @@ async def lnurl_v1_params(
if device.device == "atm":
if paymentcheck:
return {"status": "ERROR", "reason": f"Payment already claimed"}
-
+ if device.device == "switch":
+ lnurldevicepayment = await create_lnurldevicepayment(
+ deviceid=device.id,
+ sats=device.profit,
+ )
+ if not lnurldevicepayment:
+ return {"status": "ERROR", "reason": "Could not create payment."}
+ return {
+ "tag": "payRequest",
+ "callback": request.url_for(
+ "lnurldevice.lnurl_callback", paymentid=lnurldevicepayment.id
+ ),
+ "minSendable": device.profit * 1000,
+ "maxSendable": device.profit * 1000,
+ "metadata": await device.lnurlpay_metadata(),
+ }
if len(p) % 4 > 0:
p += "=" * (4 - (len(p) % 4))
@@ -205,6 +220,27 @@ async def lnurl_callback(
extra={"tag": "withdraw"},
)
return {"status": "OK"}
+ if device.device == "switch":
+ payment_hash, payment_request = await create_invoice(
+ wallet_id=device.wallet,
+ amount=lnurldevicepayment.sats / 1000,
+ memo=device.title,
+ unhashed_description=(await device.lnurlpay_metadata()).encode("utf-8"),
+ extra={"tag": "Switch", "id": device.paymentid, "time": device.amount},
+ )
+ lnurldevicepayment = await update_lnurldevicepayment(
+ lnurldevicepayment_id=paymentid, payhash=payment_hash
+ )
+
+ return {
+ "pr": payment_request,
+ "successAction": {
+ "tag": "url",
+ "description": "Check the attached link",
+ "url": request.url_for("lnurldevice.displaypin", paymentid=paymentid),
+ },
+ "routes": [],
+ }
payment_hash, payment_request = await create_invoice(
wallet_id=device.wallet,
@@ -226,5 +262,3 @@ async def lnurl_callback(
},
"routes": [],
}
-
- return resp.dict()
diff --git a/lnbits/extensions/lnurldevice/migrations.py b/lnbits/extensions/lnurldevice/migrations.py
index c7899282c..65913b023 100644
--- a/lnbits/extensions/lnurldevice/migrations.py
+++ b/lnbits/extensions/lnurldevice/migrations.py
@@ -79,3 +79,9 @@ async def m002_redux(db):
)
except:
return
+
+async def m003_redux(db):
+ """
+ Add 'meta' for storing various metadata about the wallet
+ """
+ await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount INT DEFAULT 0;")
\ No newline at end of file
diff --git a/lnbits/extensions/lnurldevice/models.py b/lnbits/extensions/lnurldevice/models.py
index fef0aec1e..cf6171419 100644
--- a/lnbits/extensions/lnurldevice/models.py
+++ b/lnbits/extensions/lnurldevice/models.py
@@ -17,6 +17,7 @@ class createLnurldevice(BaseModel):
currency: str
device: str
profit: float
+ amount: int
class lnurldevices(BaseModel):
@@ -27,6 +28,7 @@ class lnurldevices(BaseModel):
currency: str
device: str
profit: float
+ amount: int
timestamp: str
def from_row(cls, row: Row) -> "lnurldevices":
diff --git a/lnbits/extensions/lnurldevice/tasks.py b/lnbits/extensions/lnurldevice/tasks.py
new file mode 100644
index 000000000..2337c0ad1
--- /dev/null
+++ b/lnbits/extensions/lnurldevice/tasks.py
@@ -0,0 +1,39 @@
+import asyncio
+import json
+from http import HTTPStatus
+from urllib.parse import urlparse
+
+import httpx
+from fastapi import HTTPException
+
+from lnbits import bolt11
+from lnbits.core.models import Payment
+from lnbits.core.services import pay_invoice
+from lnbits.helpers import get_current_extension_name
+from lnbits.tasks import register_invoice_listener
+
+from .crud import get_lnurldevice
+from .views import updater
+
+async def wait_for_paid_invoices():
+ invoice_queue = asyncio.Queue()
+ register_invoice_listener(invoice_queue, get_current_extension_name())
+
+ while True:
+ payment = await invoice_queue.get()
+ await on_invoice_paid(payment)
+
+
+async def on_invoice_paid(payment: Payment) -> None:
+ # (avoid loops)
+ if "switch" == payment.extra.get("tag"):
+ lnurldevicepayment = await get_lnurldevicepayment(payment.extra.get("id"))
+ if not lnurldevicepayment:
+ return
+ if lnurldevicepayment.payhash == "used":
+ return
+ lnurldevicepayment = await update_lnurldevicepayment(
+ lnurldevicepayment_id=paymentid, payhash="used"
+ )
+ return await updater(lnurldevicepayment.deviceid)
+ return
\ No newline at end of file
diff --git a/lnbits/extensions/lnurldevice/views.py b/lnbits/extensions/lnurldevice/views.py
index 3389e17c7..4a28a5c1a 100644
--- a/lnbits/extensions/lnurldevice/views.py
+++ b/lnbits/extensions/lnurldevice/views.py
@@ -51,3 +51,48 @@ async def displaypin(request: Request, paymentid: str = Query(None)):
"lnurldevice/error.html",
{"request": request, "pin": "filler", "not_paid": True},
)
+
+
+##################WEBSOCKET ROUTES########################
+
+
+class ConnectionManager:
+ def __init__(self):
+ self.active_connections: List[WebSocket] = []
+
+ async def connect(self, websocket: WebSocket, lnurldevice_id: str):
+ await websocket.accept()
+ websocket.id = lnurldevice_id
+ self.active_connections.append(websocket)
+
+ def disconnect(self, websocket: WebSocket):
+ self.active_connections.remove(websocket)
+
+ async def send_personal_message(self, message: str, lnurldevice_id: str):
+ for connection in self.active_connections:
+ if connection.id == lnurldevice_id:
+ await connection.send_text(message)
+
+ async def broadcast(self, message: str):
+ for connection in self.active_connections:
+ await connection.send_text(message)
+
+
+manager = ConnectionManager()
+
+
+@lnurldevice_ext.websocket("/ws/{lnurldevice_id}", name="lnurldevice.lnurldevice_by_id")
+async def websocket_endpoint(websocket: WebSocket, lnurldevice_id: str):
+ await manager.connect(websocket, lnurldevice_id)
+ try:
+ while True:
+ data = await websocket.receive_text()
+ except WebSocketDisconnect:
+ manager.disconnect(websocket)
+
+
+async def updater(lnurldevice_id):
+ lnurldevice = await get_lnurldevice(lnurldevice_id)
+ if not lnurldevice:
+ return
+ await manager.send_personal_message(f"{lnurldevice.amount}", lnurldevice_id)
\ No newline at end of file
From 05c8f02cb7cc714457e885c2c9002f6cc97c3074 Mon Sep 17 00:00:00 2001
From: ben
Date: Thu, 6 Oct 2022 17:41:06 +0100
Subject: [PATCH 37/57] Added code sample for websocket stuff
---
docs/devs/websockets.md | 87 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
create mode 100644 docs/devs/websockets.md
diff --git a/docs/devs/websockets.md b/docs/devs/websockets.md
new file mode 100644
index 000000000..34cf940de
--- /dev/null
+++ b/docs/devs/websockets.md
@@ -0,0 +1,87 @@
+---
+layout: default
+parent: For developers
+title: Websockets
+nav_order: 2
+---
+
+
+Websockets
+=================
+
+`websockets` are a great way to add a two way instant data channel between server and client. This example was taken from teh copilot extension, we create a websocket endpoint which can be restricted by `id`, then can feed it data to broadcast to any client on the socket using the `updater(extension_id, data)` function (`extension` has been used in place of an extension name, wreplace to your own extension):
+
+
+```sh
+from fastapi import Request, WebSocket, WebSocketDisconnect
+
+class ConnectionManager:
+ def __init__(self):
+ self.active_connections: List[WebSocket] = []
+
+ async def connect(self, websocket: WebSocket, extension_id: str):
+ await websocket.accept()
+ websocket.id = extension_id
+ self.active_connections.append(websocket)
+
+ def disconnect(self, websocket: WebSocket):
+ self.active_connections.remove(websocket)
+
+ async def send_personal_message(self, message: str, extension_id: str):
+ for connection in self.active_connections:
+ if connection.id == extension_id:
+ await connection.send_text(message)
+
+ async def broadcast(self, message: str):
+ for connection in self.active_connections:
+ await connection.send_text(message)
+
+
+manager = ConnectionManager()
+
+
+@extension_ext.websocket("/ws/{extension_id}", name="extension.websocket_by_id")
+async def websocket_endpoint(websocket: WebSocket, extension_id: str):
+ await manager.connect(websocket, extension_id)
+ try:
+ while True:
+ data = await websocket.receive_text()
+ except WebSocketDisconnect:
+ manager.disconnect(websocket)
+
+
+async def updater(extension_id, data):
+ extension = await get_extension(extension_id)
+ if not extension:
+ return
+ await manager.send_personal_message(f"{data}", extension_id)
+```
+
+Example vue-js function for listening to the websocket:
+
+```
+initWs: async function () {
+ if (location.protocol !== 'http:') {
+ localUrl =
+ 'wss://' +
+ document.domain +
+ ':' +
+ location.port +
+ '/extension/ws/' +
+ self.extension.id
+ } else {
+ localUrl =
+ 'ws://' +
+ document.domain +
+ ':' +
+ location.port +
+ '/extension/ws/' +
+ self.extension.id
+ }
+ this.ws = new WebSocket(localUrl)
+ this.ws.addEventListener('message', async ({data}) => {
+ const res = JSON.parse(data.toString())
+ console.log(res)
+ })
+},
+```
\ No newline at end of file
From 6d55445a1bb941ed2b13b290cb4d8994ef0a52ba Mon Sep 17 00:00:00 2001
From: Arc <33088785+arcbtc@users.noreply.github.com>
Date: Thu, 6 Oct 2022 17:44:04 +0100
Subject: [PATCH 38/57] Update websockets.md
---
docs/devs/websockets.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/devs/websockets.md b/docs/devs/websockets.md
index 34cf940de..0638e4f28 100644
--- a/docs/devs/websockets.md
+++ b/docs/devs/websockets.md
@@ -9,7 +9,7 @@ nav_order: 2
Websockets
=================
-`websockets` are a great way to add a two way instant data channel between server and client. This example was taken from teh copilot extension, we create a websocket endpoint which can be restricted by `id`, then can feed it data to broadcast to any client on the socket using the `updater(extension_id, data)` function (`extension` has been used in place of an extension name, wreplace to your own extension):
+`websockets` are a great way to add a two way instant data channel between server and client. This example was taken from the `copilot` extension, we create a websocket endpoint which can be restricted by `id`, then can feed it data to broadcast to any client on the socket using the `updater(extension_id, data)` function (`extension` has been used in place of an extension name, wreplace to your own extension):
```sh
@@ -84,4 +84,4 @@ initWs: async function () {
console.log(res)
})
},
-```
\ No newline at end of file
+```
From b46c06012d1e4e280eb9a8d3459276489a935b62 Mon Sep 17 00:00:00 2001
From: Arc <33088785+arcbtc@users.noreply.github.com>
Date: Thu, 6 Oct 2022 17:51:36 +0100
Subject: [PATCH 39/57] Revert "make gh workflows only run on pull_request"
---
.github/workflows/codeql.yml | 2 ++
.github/workflows/formatting.yml | 2 ++
.github/workflows/mypy.yml | 2 +-
.github/workflows/regtest.yml | 2 +-
.github/workflows/tests.yml | 2 +-
5 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index bbe959837..876c8b8af 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -1,6 +1,8 @@
name: codeql
on:
+ push:
+ branches: [main, ]
pull_request:
branches: [main]
schedule:
diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml
index 21c7fb38e..e3d0fd35d 100644
--- a/.github/workflows/formatting.yml
+++ b/.github/workflows/formatting.yml
@@ -1,6 +1,8 @@
name: formatting
on:
+ push:
+ branches: [ main ]
pull_request:
branches: [ main ]
diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml
index 96b574d2d..d80da678a 100644
--- a/.github/workflows/mypy.yml
+++ b/.github/workflows/mypy.yml
@@ -1,6 +1,6 @@
name: mypy
-on: [pull_request]
+on: [push, pull_request]
jobs:
check:
diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml
index f0adb3ac5..2d7aae6b3 100644
--- a/.github/workflows/regtest.yml
+++ b/.github/workflows/regtest.yml
@@ -1,6 +1,6 @@
name: regtest
-on: [pull_request]
+on: [push, pull_request]
jobs:
LndRestWallet:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index c7b6e44bb..5d368fbb1 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -1,6 +1,6 @@
name: tests
-on: [pull_request]
+on: [push, pull_request]
jobs:
venv-sqlite:
From ff98b87eca0ab2309ccc1b1be33969aaeb96b035 Mon Sep 17 00:00:00 2001
From: ben
Date: Thu, 6 Oct 2022 22:19:30 +0100
Subject: [PATCH 40/57] Added a switch
---
lnbits/extensions/lnurldevice/__init__.py | 1 +
lnbits/extensions/lnurldevice/models.py | 2 +-
.../templates/lnurldevice/_api_docs.html | 8 +--
.../templates/lnurldevice/index.html | 66 ++++++++++++++++++-
lnbits/extensions/lnurldevice/views.py | 32 ++++++++-
5 files changed, 102 insertions(+), 7 deletions(-)
diff --git a/lnbits/extensions/lnurldevice/__init__.py b/lnbits/extensions/lnurldevice/__init__.py
index dc4456b4d..5452e3562 100644
--- a/lnbits/extensions/lnurldevice/__init__.py
+++ b/lnbits/extensions/lnurldevice/__init__.py
@@ -1,3 +1,4 @@
+import asyncio
from fastapi import APIRouter
from lnbits.db import Database
diff --git a/lnbits/extensions/lnurldevice/models.py b/lnbits/extensions/lnurldevice/models.py
index cf6171419..0714c656d 100644
--- a/lnbits/extensions/lnurldevice/models.py
+++ b/lnbits/extensions/lnurldevice/models.py
@@ -36,7 +36,7 @@ class lnurldevices(BaseModel):
def lnurl(self, req: Request) -> Lnurl:
url = req.url_for(
- "lnurldevice.lnurl_response", device_id=self.id, _external=True
+ "lnurldevice.lnurl_v1_params", device_id=self.id
)
return lnurl_encode(url)
diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
index 7f9afa27b..7497714e1 100644
--- a/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
+++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
@@ -1,10 +1,10 @@
- Register LNURLDevice devices to receive payments in your LNbits wallet.
- Build your own here
- https://github.com/arcbtc/bitcoinpos
+ Such as the LNPoS
+ https://lnbits.github.io/lnpos
Created by, Ben Arc
+
LNURLDevice Settings
+
+
+ bitcoinSwitch embeddable QRCode
+
+
LNURLDevice device string
+
{% raw
+ %}{{wslocation}}/lnurldevice/ws/{{settingsDialog.data.id}}{% endraw
+ %} Click to copy URL
+
+ Click to copy URL
-
+
@@ -191,6 +221,7 @@
label="Type of device"
>
+
+
+
+
{
diff --git a/lnbits/extensions/lnurldevice/views.py b/lnbits/extensions/lnurldevice/views.py
index 4a28a5c1a..a64de84b8 100644
--- a/lnbits/extensions/lnurldevice/views.py
+++ b/lnbits/extensions/lnurldevice/views.py
@@ -1,4 +1,6 @@
from http import HTTPStatus
+import pyqrcode
+from io import BytesIO
from fastapi import Request
from fastapi.param_functions import Query
@@ -14,6 +16,9 @@ from lnbits.decorators import check_user_exists
from . import lnurldevice_ext, lnurldevice_renderer
from .crud import get_lnurldevice, get_lnurldevicepayment
+from fastapi import Request, WebSocket, WebSocketDisconnect
+
+from starlette.responses import HTMLResponse, StreamingResponse
templates = Jinja2Templates(directory="templates")
@@ -52,6 +57,30 @@ async def displaypin(request: Request, paymentid: str = Query(None)):
{"request": request, "pin": "filler", "not_paid": True},
)
+@lnurldevice_ext.get("/img/{lnurldevice_id}", response_class=StreamingResponse)
+async def img(request: Request, lnurldevice_id):
+ lnurldevice = await get_lnurldevice(lnurldevice_id)
+ if not lnurldevice:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="LNURLDevice does not exist."
+ )
+ qr = pyqrcode.create(lnurldevice.lnurl(request))
+ stream = BytesIO()
+ qr.svg(stream, scale=3)
+ stream.seek(0)
+
+ async def _generator(stream: BytesIO):
+ yield stream.getvalue()
+
+ return StreamingResponse(
+ _generator(stream),
+ headers={
+ "Content-Type": "image/svg+xml",
+ "Cache-Control": "no-cache, no-store, must-revalidate",
+ "Pragma": "no-cache",
+ "Expires": "0",
+ },
+ )
##################WEBSOCKET ROUTES########################
@@ -95,4 +124,5 @@ async def updater(lnurldevice_id):
lnurldevice = await get_lnurldevice(lnurldevice_id)
if not lnurldevice:
return
- await manager.send_personal_message(f"{lnurldevice.amount}", lnurldevice_id)
\ No newline at end of file
+ await manager.send_personal_message(f"{lnurldevice.amount}", lnurldevice_id)
+
From 3bf38884ed8cb30ef9b5c428fdaea1acbedf1020 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Fri, 7 Oct 2022 08:30:07 +0200
Subject: [PATCH 41/57] log successful connection to backend with
logger.success()
---
lnbits/app.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/app.py b/lnbits/app.py
index 514825380..8b9cf7985 100644
--- a/lnbits/app.py
+++ b/lnbits/app.py
@@ -126,7 +126,7 @@ def check_funding_source(app: FastAPI) -> None:
logger.info("Retrying connection to backend in 5 seconds...")
await asyncio.sleep(5)
signal.signal(signal.SIGINT, original_sigint_handler)
- logger.info(
+ logger.success(
f"✔️ Backend {WALLET.__class__.__name__} connected and with a balance of {balance} msat."
)
From 52dc528a8a7557da685da624e22058e737e49a16 Mon Sep 17 00:00:00 2001
From: ben
Date: Fri, 7 Oct 2022 11:29:06 +0100
Subject: [PATCH 42/57] Added qrdialogue
---
.../templates/lnurldevice/index.html | 61 +++++++++++++++----
lnbits/extensions/lnurldevice/views.py | 19 +-----
lnbits/extensions/lnurldevice/views_api.py | 13 +++-
3 files changed, 61 insertions(+), 32 deletions(-)
diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html
index 528e5afe6..b55c83f76 100644
--- a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html
+++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html
@@ -94,18 +94,17 @@
- bitcoinSwitch embeddable QRCode
-
+ v-if="props.row.device == 'switch'"
+ :disable="protocol == 'http:'"
+ flat
+ unelevated
+ dense
+ size="xs"
+ icon="visibility"
+ :color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
+ @click="openQrCodeDialog(props.row.id)"
+ > LNURLs only work over HTTPS view LNURL
+
+
+
+
+
+ {% raw %}
+
+
+ ID: {{ qrCodeDialog.data.id }}
+
+
+ {% endraw %}
+
+ Copy LNURL
+ Close
+
+
+
{% endblock %} {% block scripts %} {{ window_vars(user) }}
@@ -306,6 +333,7 @@
mixins: [windowMixin],
data: function () {
return {
+ protocol: window.location.protocol,
location: window.location.hostname,
wslocation: window.location.hostname,
filter: '',
@@ -404,6 +432,14 @@
}
},
methods: {
+ openQrCodeDialog: function (lnurldevice_id) {
+ var lnurldevice = _.findWhere(this.lnurldeviceLinks, {id: lnurldevice_id})
+ console.log(lnurldevice)
+ this.qrCodeDialog.data = _.clone(lnurldevice)
+ this.qrCodeDialog.data.url =
+ window.location.protocol + '//' + window.location.host
+ this.qrCodeDialog.show = true
+ },
cancellnurldevice: function (data) {
var self = this
self.formDialoglnurldevice.show = false
@@ -460,6 +496,7 @@
.then(function (response) {
if (response.data) {
self.lnurldeviceLinks = response.data.map(maplnurldevice)
+ console.log(response.data)
}
})
.catch(function (error) {
diff --git a/lnbits/extensions/lnurldevice/views.py b/lnbits/extensions/lnurldevice/views.py
index a64de84b8..26f581d36 100644
--- a/lnbits/extensions/lnurldevice/views.py
+++ b/lnbits/extensions/lnurldevice/views.py
@@ -64,23 +64,8 @@ async def img(request: Request, lnurldevice_id):
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="LNURLDevice does not exist."
)
- qr = pyqrcode.create(lnurldevice.lnurl(request))
- stream = BytesIO()
- qr.svg(stream, scale=3)
- stream.seek(0)
-
- async def _generator(stream: BytesIO):
- yield stream.getvalue()
-
- return StreamingResponse(
- _generator(stream),
- headers={
- "Content-Type": "image/svg+xml",
- "Cache-Control": "no-cache, no-store, must-revalidate",
- "Pragma": "no-cache",
- "Expires": "0",
- },
- )
+ return lnurldevice.lnurl(request)
+
##################WEBSOCKET ROUTES########################
diff --git a/lnbits/extensions/lnurldevice/views_api.py b/lnbits/extensions/lnurldevice/views_api.py
index d152d210c..4a4c2b905 100644
--- a/lnbits/extensions/lnurldevice/views_api.py
+++ b/lnbits/extensions/lnurldevice/views_api.py
@@ -45,14 +45,21 @@ async def api_lnurldevice_create_or_update(
@lnurldevice_ext.get("/api/v1/lnurlpos")
-async def api_lnurldevices_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_lnurldevices_retrieve(req: Request, wallet: WalletTypeInfo = Depends(get_key_type)):
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
try:
return [
- {**lnurldevice.dict()} for lnurldevice in await get_lnurldevices(wallet_ids)
+ {**lnurldevice.dict(), **{"lnurl": lnurldevice.lnurl(req)}}
+ for lnurldevice in await get_lnurldevices(wallet_ids)
]
except:
- return ""
+ try:
+ return [
+ {**lnurldevice.dict()}
+ for lnurldevice in await get_lnurldevices(wallet_ids)
+ ]
+ except:
+ return ""
@lnurldevice_ext.get("/api/v1/lnurlpos/{lnurldevice_id}")
From 7711387d6472171539493c2f830869edef1e49fd Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 7 Oct 2022 15:23:57 +0300
Subject: [PATCH 43/57] fix: use bigint for amounts in postgres (#1030)
---
lnbits/db.py | 6 ++++++
lnbits/extensions/boltcards/migrations.py | 8 ++++----
lnbits/extensions/boltz/migrations.py | 12 ++++++------
lnbits/extensions/invoices/migrations.py | 2 +-
lnbits/extensions/lnurldevice/migrations.py | 2 +-
lnbits/extensions/lnurlpayout/migrations.py | 4 ++--
lnbits/extensions/streamalerts/migrations.py | 2 +-
lnbits/extensions/tipjar/migrations.py | 4 ++--
8 files changed, 23 insertions(+), 17 deletions(-)
diff --git a/lnbits/db.py b/lnbits/db.py
index 669817842..f52b03914 100644
--- a/lnbits/db.py
+++ b/lnbits/db.py
@@ -52,6 +52,12 @@ class Compat:
return ""
return ""
+ @property
+ def big_int(self) -> str:
+ if self.type in {POSTGRES}:
+ return "BIGINT"
+ return "INT"
+
class Connection(Compat):
def __init__(self, conn: AsyncConnection, txn, typ, name, schema):
diff --git a/lnbits/extensions/boltcards/migrations.py b/lnbits/extensions/boltcards/migrations.py
index 081260139..9609e0c37 100644
--- a/lnbits/extensions/boltcards/migrations.py
+++ b/lnbits/extensions/boltcards/migrations.py
@@ -29,7 +29,7 @@ async def m001_initial(db):
)
await db.execute(
- """
+ f"""
CREATE TABLE boltcards.hits (
id TEXT PRIMARY KEY UNIQUE,
card_id TEXT NOT NULL,
@@ -38,7 +38,7 @@ async def m001_initial(db):
useragent TEXT,
old_ctr INT NOT NULL DEFAULT 0,
new_ctr INT NOT NULL DEFAULT 0,
- amount INT NOT NULL,
+ amount {db.big_int} NOT NULL,
time TIMESTAMP NOT NULL DEFAULT """
+ db.timestamp_now
+ """
@@ -47,11 +47,11 @@ async def m001_initial(db):
)
await db.execute(
- """
+ f"""
CREATE TABLE boltcards.refunds (
id TEXT PRIMARY KEY UNIQUE,
hit_id TEXT NOT NULL,
- refund_amount INT NOT NULL,
+ refund_amount {db.big_int} NOT NULL,
time TIMESTAMP NOT NULL DEFAULT """
+ db.timestamp_now
+ """
diff --git a/lnbits/extensions/boltz/migrations.py b/lnbits/extensions/boltz/migrations.py
index e4026dd04..925322ecd 100644
--- a/lnbits/extensions/boltz/migrations.py
+++ b/lnbits/extensions/boltz/migrations.py
@@ -1,16 +1,16 @@
async def m001_initial(db):
await db.execute(
- """
+ f"""
CREATE TABLE boltz.submarineswap (
id TEXT PRIMARY KEY,
wallet TEXT NOT NULL,
payment_hash TEXT NOT NULL,
- amount INT NOT NULL,
+ amount {db.big_int} NOT NULL,
status TEXT NOT NULL,
boltz_id TEXT NOT NULL,
refund_address TEXT NOT NULL,
refund_privkey TEXT NOT NULL,
- expected_amount INT NOT NULL,
+ expected_amount {db.big_int} NOT NULL,
timeout_block_height INT NOT NULL,
address TEXT NOT NULL,
bip21 TEXT NOT NULL,
@@ -22,12 +22,12 @@ async def m001_initial(db):
"""
)
await db.execute(
- """
+ f"""
CREATE TABLE boltz.reverse_submarineswap (
id TEXT PRIMARY KEY,
wallet TEXT NOT NULL,
onchain_address TEXT NOT NULL,
- amount INT NOT NULL,
+ amount {db.big_int} NOT NULL,
instant_settlement BOOLEAN NOT NULL,
status TEXT NOT NULL,
boltz_id TEXT NOT NULL,
@@ -37,7 +37,7 @@ async def m001_initial(db):
claim_privkey TEXT NOT NULL,
lockup_address TEXT NOT NULL,
invoice TEXT NOT NULL,
- onchain_amount INT NOT NULL,
+ onchain_amount {db.big_int} NOT NULL,
time TIMESTAMP NOT NULL DEFAULT """
+ db.timestamp_now
+ """
diff --git a/lnbits/extensions/invoices/migrations.py b/lnbits/extensions/invoices/migrations.py
index c47a954ae..74a0fdbad 100644
--- a/lnbits/extensions/invoices/migrations.py
+++ b/lnbits/extensions/invoices/migrations.py
@@ -45,7 +45,7 @@ async def m001_initial_invoices(db):
id TEXT PRIMARY KEY,
invoice_id TEXT NOT NULL,
- amount INT NOT NULL,
+ amount {db.big_int} NOT NULL,
time TIMESTAMP NOT NULL DEFAULT {db.timestamp_now},
diff --git a/lnbits/extensions/lnurldevice/migrations.py b/lnbits/extensions/lnurldevice/migrations.py
index c7899282c..da62cb7e6 100644
--- a/lnbits/extensions/lnurldevice/migrations.py
+++ b/lnbits/extensions/lnurldevice/migrations.py
@@ -29,7 +29,7 @@ async def m001_initial(db):
payhash TEXT,
payload TEXT NOT NULL,
pin INT,
- sats INT,
+ sats {db.big_int},
timestamp TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
);
"""
diff --git a/lnbits/extensions/lnurlpayout/migrations.py b/lnbits/extensions/lnurlpayout/migrations.py
index 6af047910..7a45e4953 100644
--- a/lnbits/extensions/lnurlpayout/migrations.py
+++ b/lnbits/extensions/lnurlpayout/migrations.py
@@ -3,14 +3,14 @@ async def m001_initial(db):
Initial lnurlpayouts table.
"""
await db.execute(
- """
+ f"""
CREATE TABLE lnurlpayout.lnurlpayouts (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
wallet TEXT NOT NULL,
admin_key TEXT NOT NULL,
lnurlpay TEXT NOT NULL,
- threshold INT NOT NULL
+ threshold {db.big_int} NOT NULL
);
"""
)
diff --git a/lnbits/extensions/streamalerts/migrations.py b/lnbits/extensions/streamalerts/migrations.py
index 1b0cea375..7d50e8f11 100644
--- a/lnbits/extensions/streamalerts/migrations.py
+++ b/lnbits/extensions/streamalerts/migrations.py
@@ -25,7 +25,7 @@ async def m001_initial(db):
name TEXT NOT NULL,
message TEXT NOT NULL,
cur_code TEXT NOT NULL,
- sats INT NOT NULL,
+ sats {db.big_int} NOT NULL,
amount FLOAT NOT NULL,
service INTEGER NOT NULL,
posted BOOLEAN NOT NULL,
diff --git a/lnbits/extensions/tipjar/migrations.py b/lnbits/extensions/tipjar/migrations.py
index 6b58fbca2..d8f6da3f0 100644
--- a/lnbits/extensions/tipjar/migrations.py
+++ b/lnbits/extensions/tipjar/migrations.py
@@ -19,8 +19,8 @@ async def m001_initial(db):
wallet TEXT NOT NULL,
name TEXT NOT NULL,
message TEXT NOT NULL,
- sats INT NOT NULL,
- tipjar INT NOT NULL,
+ sats {db.big_int} NOT NULL,
+ tipjar {db.big_int} NOT NULL,
FOREIGN KEY(tipjar) REFERENCES {db.references_schema}TipJars(id)
);
"""
From 8d4337679f93161966310ccf56d30eb4fd576317 Mon Sep 17 00:00:00 2001
From: ben
Date: Fri, 7 Oct 2022 13:46:40 +0100
Subject: [PATCH 44/57] Websocket/listener working
---
lnbits/extensions/lnurldevice/crud.py | 6 +++--
lnbits/extensions/lnurldevice/lnurl.py | 26 +++++++++++--------
lnbits/extensions/lnurldevice/tasks.py | 8 +++---
.../templates/lnurldevice/index.html | 3 ++-
lnbits/extensions/lnurldevice/views_api.py | 9 ++++---
5 files changed, 30 insertions(+), 22 deletions(-)
diff --git a/lnbits/extensions/lnurldevice/crud.py b/lnbits/extensions/lnurldevice/crud.py
index 451665212..4c25e4cb4 100644
--- a/lnbits/extensions/lnurldevice/crud.py
+++ b/lnbits/extensions/lnurldevice/crud.py
@@ -22,9 +22,10 @@ async def create_lnurldevice(
wallet,
currency,
device,
- profit
+ profit,
+ amount
)
- VALUES (?, ?, ?, ?, ?, ?, ?)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""",
(
lnurldevice_id,
@@ -34,6 +35,7 @@ async def create_lnurldevice(
data.currency,
data.device,
data.profit,
+ data.amount,
),
)
return await get_lnurldevice(lnurldevice_id)
diff --git a/lnbits/extensions/lnurldevice/lnurl.py b/lnbits/extensions/lnurldevice/lnurl.py
index a2bc0dd4f..3823005ea 100644
--- a/lnbits/extensions/lnurldevice/lnurl.py
+++ b/lnbits/extensions/lnurldevice/lnurl.py
@@ -103,9 +103,19 @@ async def lnurl_v1_params(
if paymentcheck:
return {"status": "ERROR", "reason": f"Payment already claimed"}
if device.device == "switch":
+
+ price_msat = (
+ await fiat_amount_as_satoshis(float(device.profit), device.currency)
+ if device.currency != "sat"
+ else amount_in_cent
+ ) * 1000
+
lnurldevicepayment = await create_lnurldevicepayment(
deviceid=device.id,
- sats=device.profit,
+ payload="bla",
+ sats=price_msat,
+ pin=1,
+ payhash="bla",
)
if not lnurldevicepayment:
return {"status": "ERROR", "reason": "Could not create payment."}
@@ -114,8 +124,8 @@ async def lnurl_v1_params(
"callback": request.url_for(
"lnurldevice.lnurl_callback", paymentid=lnurldevicepayment.id
),
- "minSendable": device.profit * 1000,
- "maxSendable": device.profit * 1000,
+ "minSendable": price_msat,
+ "maxSendable": price_msat,
"metadata": await device.lnurlpay_metadata(),
}
if len(p) % 4 > 0:
@@ -224,21 +234,15 @@ async def lnurl_callback(
payment_hash, payment_request = await create_invoice(
wallet_id=device.wallet,
amount=lnurldevicepayment.sats / 1000,
- memo=device.title,
+ memo=device.title + "-" + lnurldevicepayment.id,
unhashed_description=(await device.lnurlpay_metadata()).encode("utf-8"),
- extra={"tag": "Switch", "id": device.paymentid, "time": device.amount},
+ extra={"tag": "Switch", "id": paymentid, "time": device.amount},
)
lnurldevicepayment = await update_lnurldevicepayment(
lnurldevicepayment_id=paymentid, payhash=payment_hash
)
-
return {
"pr": payment_request,
- "successAction": {
- "tag": "url",
- "description": "Check the attached link",
- "url": request.url_for("lnurldevice.displaypin", paymentid=paymentid),
- },
"routes": [],
}
diff --git a/lnbits/extensions/lnurldevice/tasks.py b/lnbits/extensions/lnurldevice/tasks.py
index 2337c0ad1..4bf0d3b90 100644
--- a/lnbits/extensions/lnurldevice/tasks.py
+++ b/lnbits/extensions/lnurldevice/tasks.py
@@ -12,8 +12,9 @@ from lnbits.core.services import pay_invoice
from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
-from .crud import get_lnurldevice
+from .crud import get_lnurldevice, get_lnurldevicepayment, update_lnurldevicepayment
from .views import updater
+from loguru import logger
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
@@ -23,17 +24,16 @@ async def wait_for_paid_invoices():
payment = await invoice_queue.get()
await on_invoice_paid(payment)
-
async def on_invoice_paid(payment: Payment) -> None:
# (avoid loops)
- if "switch" == payment.extra.get("tag"):
+ if "Switch" == payment.extra.get("tag"):
lnurldevicepayment = await get_lnurldevicepayment(payment.extra.get("id"))
if not lnurldevicepayment:
return
if lnurldevicepayment.payhash == "used":
return
lnurldevicepayment = await update_lnurldevicepayment(
- lnurldevicepayment_id=paymentid, payhash="used"
+ lnurldevicepayment_id=payment.extra.get("id"), payhash="used"
)
return await updater(lnurldevicepayment.deviceid)
return
\ No newline at end of file
diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html
index b55c83f76..ccb0a6e94 100644
--- a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html
+++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html
@@ -240,6 +240,7 @@
fill-mask="0"
reverse-fill-mask
:step="'0.01'"
+ value="0.00"
>
Date: Fri, 7 Oct 2022 23:18:57 +0100
Subject: [PATCH 45/57] Black
---
lnbits/extensions/lnurldevice/__init__.py | 2 ++
lnbits/extensions/lnurldevice/lnurl.py | 2 +-
lnbits/extensions/lnurldevice/migrations.py | 5 ++++-
lnbits/extensions/lnurldevice/models.py | 4 +---
lnbits/extensions/lnurldevice/tasks.py | 10 ++++++----
lnbits/extensions/lnurldevice/views.py | 4 ++--
lnbits/extensions/lnurldevice/views_api.py | 12 +++++++-----
7 files changed, 23 insertions(+), 16 deletions(-)
diff --git a/lnbits/extensions/lnurldevice/__init__.py b/lnbits/extensions/lnurldevice/__init__.py
index 5452e3562..d987e6cc1 100644
--- a/lnbits/extensions/lnurldevice/__init__.py
+++ b/lnbits/extensions/lnurldevice/__init__.py
@@ -13,11 +13,13 @@ lnurldevice_ext: APIRouter = APIRouter(prefix="/lnurldevice", tags=["lnurldevice
def lnurldevice_renderer():
return template_renderer(["lnbits/extensions/lnurldevice/templates"])
+
from .tasks import wait_for_paid_invoices
from .lnurl import * # noqa
from .views import * # noqa
from .views_api import * # noqa
+
def lnurldevice_start():
loop = asyncio.get_event_loop()
loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))
diff --git a/lnbits/extensions/lnurldevice/lnurl.py b/lnbits/extensions/lnurldevice/lnurl.py
index 3823005ea..79892b78a 100644
--- a/lnbits/extensions/lnurldevice/lnurl.py
+++ b/lnbits/extensions/lnurldevice/lnurl.py
@@ -122,7 +122,7 @@ async def lnurl_v1_params(
return {
"tag": "payRequest",
"callback": request.url_for(
- "lnurldevice.lnurl_callback", paymentid=lnurldevicepayment.id
+ "lnurldevice.lnurl_callback", paymentid=lnurldevicepayment.id
),
"minSendable": price_msat,
"maxSendable": price_msat,
diff --git a/lnbits/extensions/lnurldevice/migrations.py b/lnbits/extensions/lnurldevice/migrations.py
index 65913b023..fa56b3d13 100644
--- a/lnbits/extensions/lnurldevice/migrations.py
+++ b/lnbits/extensions/lnurldevice/migrations.py
@@ -80,8 +80,11 @@ async def m002_redux(db):
except:
return
+
async def m003_redux(db):
"""
Add 'meta' for storing various metadata about the wallet
"""
- await db.execute("ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount INT DEFAULT 0;")
\ No newline at end of file
+ await db.execute(
+ "ALTER TABLE lnurldevice.lnurldevices ADD COLUMN amount INT DEFAULT 0;"
+ )
diff --git a/lnbits/extensions/lnurldevice/models.py b/lnbits/extensions/lnurldevice/models.py
index 0714c656d..01bcc2ba6 100644
--- a/lnbits/extensions/lnurldevice/models.py
+++ b/lnbits/extensions/lnurldevice/models.py
@@ -35,9 +35,7 @@ class lnurldevices(BaseModel):
return cls(**dict(row))
def lnurl(self, req: Request) -> Lnurl:
- url = req.url_for(
- "lnurldevice.lnurl_v1_params", device_id=self.id
- )
+ url = req.url_for("lnurldevice.lnurl_v1_params", device_id=self.id)
return lnurl_encode(url)
async def lnurlpay_metadata(self) -> LnurlPayMetadata:
diff --git a/lnbits/extensions/lnurldevice/tasks.py b/lnbits/extensions/lnurldevice/tasks.py
index 4bf0d3b90..2f048138a 100644
--- a/lnbits/extensions/lnurldevice/tasks.py
+++ b/lnbits/extensions/lnurldevice/tasks.py
@@ -16,6 +16,7 @@ from .crud import get_lnurldevice, get_lnurldevicepayment, update_lnurldevicepay
from .views import updater
from loguru import logger
+
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
register_invoice_listener(invoice_queue, get_current_extension_name())
@@ -23,7 +24,8 @@ async def wait_for_paid_invoices():
while True:
payment = await invoice_queue.get()
await on_invoice_paid(payment)
-
+
+
async def on_invoice_paid(payment: Payment) -> None:
# (avoid loops)
if "Switch" == payment.extra.get("tag"):
@@ -33,7 +35,7 @@ async def on_invoice_paid(payment: Payment) -> None:
if lnurldevicepayment.payhash == "used":
return
lnurldevicepayment = await update_lnurldevicepayment(
- lnurldevicepayment_id=payment.extra.get("id"), payhash="used"
- )
+ lnurldevicepayment_id=payment.extra.get("id"), payhash="used"
+ )
return await updater(lnurldevicepayment.deviceid)
- return
\ No newline at end of file
+ return
diff --git a/lnbits/extensions/lnurldevice/views.py b/lnbits/extensions/lnurldevice/views.py
index 26f581d36..752d2c81b 100644
--- a/lnbits/extensions/lnurldevice/views.py
+++ b/lnbits/extensions/lnurldevice/views.py
@@ -57,6 +57,7 @@ async def displaypin(request: Request, paymentid: str = Query(None)):
{"request": request, "pin": "filler", "not_paid": True},
)
+
@lnurldevice_ext.get("/img/{lnurldevice_id}", response_class=StreamingResponse)
async def img(request: Request, lnurldevice_id):
lnurldevice = await get_lnurldevice(lnurldevice_id)
@@ -65,7 +66,7 @@ async def img(request: Request, lnurldevice_id):
status_code=HTTPStatus.NOT_FOUND, detail="LNURLDevice does not exist."
)
return lnurldevice.lnurl(request)
-
+
##################WEBSOCKET ROUTES########################
@@ -110,4 +111,3 @@ async def updater(lnurldevice_id):
if not lnurldevice:
return
await manager.send_personal_message(f"{lnurldevice.amount}", lnurldevice_id)
-
diff --git a/lnbits/extensions/lnurldevice/views_api.py b/lnbits/extensions/lnurldevice/views_api.py
index 65625cbb9..c034f66ed 100644
--- a/lnbits/extensions/lnurldevice/views_api.py
+++ b/lnbits/extensions/lnurldevice/views_api.py
@@ -46,18 +46,20 @@ async def api_lnurldevice_create_or_update(
@lnurldevice_ext.get("/api/v1/lnurlpos")
-async def api_lnurldevices_retrieve(req: Request, wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_lnurldevices_retrieve(
+ req: Request, wallet: WalletTypeInfo = Depends(get_key_type)
+):
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
try:
return [
- {**lnurldevice.dict(), **{"lnurl": lnurldevice.lnurl(req)}}
- for lnurldevice in await get_lnurldevices(wallet_ids)
+ {**lnurldevice.dict(), **{"lnurl": lnurldevice.lnurl(req)}}
+ for lnurldevice in await get_lnurldevices(wallet_ids)
]
except:
try:
return [
- {**lnurldevice.dict()}
- for lnurldevice in await get_lnurldevices(wallet_ids)
+ {**lnurldevice.dict()}
+ for lnurldevice in await get_lnurldevices(wallet_ids)
]
except:
return ""
From d4a5fe27768a8d05417e7ed8d6340b2420291a5f Mon Sep 17 00:00:00 2001
From: ben
Date: Fri, 7 Oct 2022 23:48:42 +0100
Subject: [PATCH 46/57] isort
---
lnbits/extensions/lnurldevice/tasks.py | 4 ++--
lnbits/extensions/lnurldevice/views.py | 9 +++------
2 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/lnbits/extensions/lnurldevice/tasks.py b/lnbits/extensions/lnurldevice/tasks.py
index 2f048138a..db8d87f03 100644
--- a/lnbits/extensions/lnurldevice/tasks.py
+++ b/lnbits/extensions/lnurldevice/tasks.py
@@ -12,9 +12,9 @@ from lnbits.core.services import pay_invoice
from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
-from .crud import get_lnurldevice, get_lnurldevicepayment, update_lnurldevicepayment
+from .crud import (get_lnurldevice, get_lnurldevicepayment,
+ update_lnurldevicepayment)
from .views import updater
-from loguru import logger
async def wait_for_paid_invoices():
diff --git a/lnbits/extensions/lnurldevice/views.py b/lnbits/extensions/lnurldevice/views.py
index 752d2c81b..5c6eba24b 100644
--- a/lnbits/extensions/lnurldevice/views.py
+++ b/lnbits/extensions/lnurldevice/views.py
@@ -1,13 +1,13 @@
from http import HTTPStatus
-import pyqrcode
from io import BytesIO
-from fastapi import Request
+import pyqrcode
+from fastapi import Request, WebSocket, WebSocketDisconnect
from fastapi.param_functions import Query
from fastapi.params import Depends
from fastapi.templating import Jinja2Templates
from starlette.exceptions import HTTPException
-from starlette.responses import HTMLResponse
+from starlette.responses import HTMLResponse, StreamingResponse
from lnbits.core.crud import update_payment_status
from lnbits.core.models import User
@@ -16,9 +16,6 @@ from lnbits.decorators import check_user_exists
from . import lnurldevice_ext, lnurldevice_renderer
from .crud import get_lnurldevice, get_lnurldevicepayment
-from fastapi import Request, WebSocket, WebSocketDisconnect
-
-from starlette.responses import HTMLResponse, StreamingResponse
templates = Jinja2Templates(directory="templates")
From e3bd38fc6b11cbc9c1382fbac5fdfc005a0142db Mon Sep 17 00:00:00 2001
From: ben
Date: Fri, 7 Oct 2022 23:51:28 +0100
Subject: [PATCH 47/57] back
---
lnbits/extensions/lnurldevice/tasks.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lnbits/extensions/lnurldevice/tasks.py b/lnbits/extensions/lnurldevice/tasks.py
index db8d87f03..c8f3db04f 100644
--- a/lnbits/extensions/lnurldevice/tasks.py
+++ b/lnbits/extensions/lnurldevice/tasks.py
@@ -12,8 +12,7 @@ from lnbits.core.services import pay_invoice
from lnbits.helpers import get_current_extension_name
from lnbits.tasks import register_invoice_listener
-from .crud import (get_lnurldevice, get_lnurldevicepayment,
- update_lnurldevicepayment)
+from .crud import get_lnurldevice, get_lnurldevicepayment, update_lnurldevicepayment
from .views import updater
From 34effe3ae5690ad3b68d0a1af34ccc2d9d9b6e21 Mon Sep 17 00:00:00 2001
From: ben
Date: Fri, 7 Oct 2022 23:57:28 +0100
Subject: [PATCH 48/57] isort
---
lnbits/extensions/lnurldevice/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/lnurldevice/__init__.py b/lnbits/extensions/lnurldevice/__init__.py
index d987e6cc1..c27c9e17d 100644
--- a/lnbits/extensions/lnurldevice/__init__.py
+++ b/lnbits/extensions/lnurldevice/__init__.py
@@ -2,8 +2,8 @@ import asyncio
from fastapi import APIRouter
from lnbits.db import Database
-from lnbits.helpers import template_renderer
from lnbits.tasks import catch_everything_and_restart
+from lnbits.helpers import template_renderer
db = Database("ext_lnurldevice")
From fbc20b3579169b44227cc8ba8e20cc78be1b65f5 Mon Sep 17 00:00:00 2001
From: ben
Date: Sat, 8 Oct 2022 01:28:46 +0100
Subject: [PATCH 49/57] added credits
---
.../templates/lnurldevice/_api_docs.html | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
index 7497714e1..b239e9812 100644
--- a/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
+++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
@@ -2,12 +2,21 @@
For LNURL based Points of Sale, ATMs, and relay devices
- Such as the LNPoS
+ Use with:
+ LNPoS
https://lnbits.github.io/lnpos
+ bitcoinSwitch
+ https://github.com/lnbits/bitcoinSwitch
+ FOSSA
+ https://github.com/lnbits/fossa
- Created by, Ben ArcBen Arc
, BC, Vlad Stan
From 3bf3c91007ae4f589f06176a405061b92878e5ff Mon Sep 17 00:00:00 2001
From: calle <93376500+callebtc@users.noreply.github.com>
Date: Mon, 10 Oct 2022 16:12:06 +0200
Subject: [PATCH 50/57] Add backend: ln.tips (LightningTipBot) wallet (#921)
* add ln.tips
* add wallet
* revert lnbits
* make format
* .env.example
* listener fix
* reconnect fixed
* make format
* make format
---
.env.example | 7 +-
lnbits/extensions/lnurldevice/__init__.py | 5 +-
.../templates/lnurldevice/_api_docs.html | 22 +--
.../templates/lnurldevice/index.html | 142 ++++++++-------
lnbits/wallets/__init__.py | 2 +
lnbits/wallets/lntips.py | 170 ++++++++++++++++++
6 files changed, 263 insertions(+), 85 deletions(-)
create mode 100644 lnbits/wallets/lntips.py
diff --git a/.env.example b/.env.example
index 7d6de35f4..987c6ca69 100644
--- a/.env.example
+++ b/.env.example
@@ -41,7 +41,7 @@ LNBITS_SITE_DESCRIPTION="Some description about your service, will display if ti
LNBITS_THEME_OPTIONS="classic, bitcoin, flamingo, freedom, mint, autumn, monochrome, salvador"
# LNBITS_CUSTOM_LOGO="https://lnbits.com/assets/images/logo/logo.svg"
-# Choose from LNPayWallet, OpenNodeWallet, LntxbotWallet, ClicheWallet
+# Choose from LNPayWallet, OpenNodeWallet, LntxbotWallet, ClicheWallet, LnTipsWallet
# LndRestWallet, CoreLightningWallet, LNbitsWallet, SparkWallet, FakeWallet, EclairWallet
LNBITS_BACKEND_WALLET_CLASS=VoidWallet
# VoidWallet is just a fallback that works without any actual Lightning capabilities,
@@ -92,3 +92,8 @@ LNBITS_DENOMINATION=sats
# EclairWallet
ECLAIR_URL=http://127.0.0.1:8283
ECLAIR_PASS=eclairpw
+
+# LnTipsWallet
+# Enter /api in LightningTipBot to get your key
+LNTIPS_API_KEY=LNTIPS_ADMIN_KEY
+LNTIPS_API_ENDPOINT=https://ln.tips
diff --git a/lnbits/extensions/lnurldevice/__init__.py b/lnbits/extensions/lnurldevice/__init__.py
index c27c9e17d..d2010c449 100644
--- a/lnbits/extensions/lnurldevice/__init__.py
+++ b/lnbits/extensions/lnurldevice/__init__.py
@@ -1,9 +1,10 @@
import asyncio
+
from fastapi import APIRouter
from lnbits.db import Database
-from lnbits.tasks import catch_everything_and_restart
from lnbits.helpers import template_renderer
+from lnbits.tasks import catch_everything_and_restart
db = Database("ext_lnurldevice")
@@ -14,8 +15,8 @@ def lnurldevice_renderer():
return template_renderer(["lnbits/extensions/lnurldevice/templates"])
-from .tasks import wait_for_paid_invoices
from .lnurl import * # noqa
+from .tasks import wait_for_paid_invoices
from .views import * # noqa
from .views_api import * # noqa
diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
index b239e9812..f93d44d80 100644
--- a/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
+++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/_api_docs.html
@@ -3,20 +3,22 @@
For LNURL based Points of Sale, ATMs, and relay devices
Use with:
- LNPoS
- https://lnbits.github.io/lnpos
+ https://lnbits.github.io/lnpos
- bitcoinSwitch
- https://github.com/lnbits/bitcoinSwitch
+ https://github.com/lnbits/bitcoinSwitch
- FOSSA
- https://github.com/lnbits/fossa
+ https://github.com/lnbits/fossa
- Created by, Ben Arc, BC, Vlad StanBen Arc,
+ BC,
+ Vlad Stan
diff --git a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html
index ccb0a6e94..028dd94b4 100644
--- a/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html
+++ b/lnbits/extensions/lnurldevice/templates/lnurldevice/index.html
@@ -94,17 +94,19 @@
LNURLs only work over HTTPS view LNURL
+ v-if="props.row.device == 'switch'"
+ :disable="protocol == 'http:'"
+ flat
+ unelevated
+ dense
+ size="xs"
+ icon="visibility"
+ :color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
+ @click="openQrCodeDialog(props.row.id)"
+ >
+ LNURLs only work over HTTPS view LNURL
LNURLDevice device string
- {% raw
- %}{{wslocation}}/lnurldevice/ws/{{settingsDialog.data.id}}{% endraw
- %} Click to copy URL
-
- {% raw %}{{wslocation}}/lnurldevice/ws/{{settingsDialog.data.id}}{%
+ endraw %} Click to copy URL
+
+ {% raw
- %}{{location}}/lnurldevice/api/v1/lnurl/{{settingsDialog.data.id}},
- {{settingsDialog.data.key}}, {{settingsDialog.data.currency}}{% endraw
- %} Click to copy URL
-
-
+ >{% raw
+ %}{{location}}/lnurldevice/api/v1/lnurl/{{settingsDialog.data.id}},
+ {{settingsDialog.data.key}}, {{settingsDialog.data.currency}}{% endraw
+ %} Click to copy URL
+
+
@@ -229,29 +230,28 @@
label="Profit margin (% added to invoices/deducted from faucets)"
>
-
-
-
+
+
+
ID: {{ qrCodeDialog.data.id }}
-
{% endraw %}
@@ -434,13 +433,15 @@
},
methods: {
openQrCodeDialog: function (lnurldevice_id) {
- var lnurldevice = _.findWhere(this.lnurldeviceLinks, {id: lnurldevice_id})
- console.log(lnurldevice)
- this.qrCodeDialog.data = _.clone(lnurldevice)
- this.qrCodeDialog.data.url =
- window.location.protocol + '//' + window.location.host
- this.qrCodeDialog.show = true
- },
+ var lnurldevice = _.findWhere(this.lnurldeviceLinks, {
+ id: lnurldevice_id
+ })
+ console.log(lnurldevice)
+ this.qrCodeDialog.data = _.clone(lnurldevice)
+ this.qrCodeDialog.data.url =
+ window.location.protocol + '//' + window.location.host
+ this.qrCodeDialog.show = true
+ },
cancellnurldevice: function (data) {
var self = this
self.formDialoglnurldevice.show = false
@@ -617,10 +618,7 @@
'//',
window.location.host
].join('')
- self.wslocation = [
- 'ws://',
- window.location.host
- ].join('')
+ self.wslocation = ['ws://', window.location.host].join('')
LNbits.api
.request('GET', '/api/v1/currencies')
.then(response => {
diff --git a/lnbits/wallets/__init__.py b/lnbits/wallets/__init__.py
index 41949652d..fa533566f 100644
--- a/lnbits/wallets/__init__.py
+++ b/lnbits/wallets/__init__.py
@@ -1,5 +1,6 @@
# flake8: noqa
+
from .cliche import ClicheWallet
from .cln import CoreLightningWallet # legacy .env support
from .cln import CoreLightningWallet as CLightningWallet
@@ -9,6 +10,7 @@ from .lnbits import LNbitsWallet
from .lndgrpc import LndWallet
from .lndrest import LndRestWallet
from .lnpay import LNPayWallet
+from .lntips import LnTipsWallet
from .lntxbot import LntxbotWallet
from .opennode import OpenNodeWallet
from .spark import SparkWallet
diff --git a/lnbits/wallets/lntips.py b/lnbits/wallets/lntips.py
new file mode 100644
index 000000000..54220c85a
--- /dev/null
+++ b/lnbits/wallets/lntips.py
@@ -0,0 +1,170 @@
+import asyncio
+import hashlib
+import json
+import time
+from os import getenv
+from typing import AsyncGenerator, Dict, Optional
+
+import httpx
+from loguru import logger
+
+from .base import (
+ InvoiceResponse,
+ PaymentResponse,
+ PaymentStatus,
+ StatusResponse,
+ Wallet,
+)
+
+
+class LnTipsWallet(Wallet):
+ def __init__(self):
+ endpoint = getenv("LNTIPS_API_ENDPOINT")
+ self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
+
+ key = (
+ getenv("LNTIPS_API_KEY")
+ or getenv("LNTIPS_ADMIN_KEY")
+ or getenv("LNTIPS_INVOICE_KEY")
+ )
+ self.auth = {"Authorization": f"Basic {key}"}
+
+ async def status(self) -> StatusResponse:
+ async with httpx.AsyncClient() as client:
+ r = await client.get(
+ f"{self.endpoint}/api/v1/balance", headers=self.auth, timeout=40
+ )
+ try:
+ data = r.json()
+ except:
+ return StatusResponse(
+ f"Failed to connect to {self.endpoint}, got: '{r.text[:200]}...'", 0
+ )
+
+ if data.get("error"):
+ return StatusResponse(data["error"], 0)
+
+ return StatusResponse(None, data["balance"] * 1000)
+
+ async def create_invoice(
+ self,
+ amount: int,
+ memo: Optional[str] = None,
+ description_hash: Optional[bytes] = None,
+ unhashed_description: Optional[bytes] = None,
+ **kwargs,
+ ) -> InvoiceResponse:
+ data: Dict = {"amount": amount}
+ if description_hash:
+ data["description_hash"] = description_hash.hex()
+ elif unhashed_description:
+ data["description_hash"] = hashlib.sha256(unhashed_description).hexdigest()
+ else:
+ data["memo"] = memo or ""
+
+ async with httpx.AsyncClient() as client:
+ r = await client.post(
+ f"{self.endpoint}/api/v1/createinvoice",
+ headers=self.auth,
+ json=data,
+ timeout=40,
+ )
+
+ if r.is_error:
+ try:
+ data = r.json()
+ error_message = data["message"]
+ except:
+ error_message = r.text
+ pass
+
+ return InvoiceResponse(False, None, None, error_message)
+
+ data = r.json()
+ return InvoiceResponse(
+ True, data["payment_hash"], data["payment_request"], None
+ )
+
+ async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
+ async with httpx.AsyncClient() as client:
+ r = await client.post(
+ f"{self.endpoint}/api/v1/payinvoice",
+ headers=self.auth,
+ json={"pay_req": bolt11},
+ timeout=None,
+ )
+ if r.is_error:
+ return PaymentResponse(False, None, 0, None, r.text)
+
+ if "error" in r.json():
+ try:
+ data = r.json()
+ error_message = data["error"]
+ except:
+ error_message = r.text
+ pass
+ return PaymentResponse(False, None, 0, None, error_message)
+
+ data = r.json()["details"]
+ checking_id = data["payment_hash"]
+ fee_msat = -data["fee"]
+ preimage = data["preimage"]
+ return PaymentResponse(True, checking_id, fee_msat, preimage, None)
+
+ async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
+ async with httpx.AsyncClient() as client:
+ r = await client.post(
+ f"{self.endpoint}/api/v1/invoicestatus/{checking_id}",
+ headers=self.auth,
+ )
+
+ if r.is_error or len(r.text) == 0:
+ return PaymentStatus(None)
+
+ data = r.json()
+ return PaymentStatus(data["paid"])
+
+ async def get_payment_status(self, checking_id: str) -> PaymentStatus:
+ async with httpx.AsyncClient() as client:
+ r = await client.post(
+ url=f"{self.endpoint}/api/v1/paymentstatus/{checking_id}",
+ headers=self.auth,
+ )
+
+ if r.is_error:
+ return PaymentStatus(None)
+ data = r.json()
+
+ paid_to_status = {False: None, True: True}
+ return PaymentStatus(paid_to_status[data.get("paid")])
+
+ async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
+ last_connected = None
+ while True:
+ url = f"{self.endpoint}/api/v1/invoicestream"
+ try:
+ async with httpx.AsyncClient(timeout=None, headers=self.auth) as client:
+ last_connected = time.time()
+ async with client.stream("GET", url) as r:
+ async for line in r.aiter_lines():
+ try:
+ prefix = "data: "
+ if not line.startswith(prefix):
+ continue
+ data = line[len(prefix) :] # sse parsing
+ inv = json.loads(data)
+ if not inv.get("payment_hash"):
+ continue
+ except:
+ continue
+ yield inv["payment_hash"]
+ except Exception as e:
+ pass
+
+ # do not sleep if the connection was active for more than 10s
+ # since the backend is expected to drop the connection after 90s
+ if last_connected is None or time.time() - last_connected < 10:
+ logger.error(
+ f"lost connection to {self.endpoint}/api/v1/invoicestream, retrying in 5 seconds"
+ )
+ await asyncio.sleep(5)
From d1302e4868f0fe12cf6bdf7d25e82045981077c2 Mon Sep 17 00:00:00 2001
From: calle <93376500+callebtc@users.noreply.github.com>
Date: Tue, 11 Oct 2022 08:52:39 +0200
Subject: [PATCH 51/57] show progress (#987)
---
lnbits/core/crud.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py
index cbed62922..bb1ca0c1c 100644
--- a/lnbits/core/crud.py
+++ b/lnbits/core/crud.py
@@ -333,7 +333,7 @@ async def delete_expired_invoices(
"""
)
logger.debug(f"Checking expiry of {len(rows)} invoices")
- for (payment_request,) in rows:
+ for i, (payment_request,) in enumerate(rows):
try:
invoice = bolt11.decode(payment_request)
except:
@@ -343,7 +343,7 @@ async def delete_expired_invoices(
if expiration_date > datetime.datetime.utcnow():
continue
logger.debug(
- f"Deleting expired invoice: {invoice.payment_hash} (expired: {expiration_date})"
+ f"Deleting expired invoice {i}/{len(rows)}: {invoice.payment_hash} (expired: {expiration_date})"
)
await (conn or db).execute(
"""
From 42704eaf1bcf0a0ec6fc4309ea63f692f0721286 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Tue, 18 Oct 2022 08:49:27 +0200
Subject: [PATCH 52/57] change boltz logs from warn to debug (#1057)
---
lnbits/extensions/boltz/tasks.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lnbits/extensions/boltz/tasks.py b/lnbits/extensions/boltz/tasks.py
index ace94557b..d1ace04b8 100644
--- a/lnbits/extensions/boltz/tasks.py
+++ b/lnbits/extensions/boltz/tasks.py
@@ -57,7 +57,7 @@ async def check_for_pending_swaps():
swap_status = get_swap_status(swap)
# should only happen while development when regtest is reset
if swap_status.exists is False:
- logger.warning(f"Boltz - swap: {swap.boltz_id} does not exist.")
+ logger.debug(f"Boltz - swap: {swap.boltz_id} does not exist.")
await update_swap_status(swap.id, "failed")
continue
@@ -73,7 +73,7 @@ async def check_for_pending_swaps():
else:
if swap_status.hit_timeout:
if not swap_status.has_lockup:
- logger.warning(
+ logger.debug(
f"Boltz - swap: {swap.id} hit timeout, but no lockup tx..."
)
await update_swap_status(swap.id, "timeout")
From 9de38f908bfa603c1cfb98cfd078e0136e815320 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Thu, 20 Oct 2022 17:40:23 +0300
Subject: [PATCH 53/57] fix: wallet not found error (#1063)
---
lnbits/extensions/satspay/crud.py | 9 ++++++++-
lnbits/extensions/satspay/helpers.py | 17 +++++++++++++++++
lnbits/extensions/satspay/models.py | 1 -
.../satspay/templates/satspay/display.html | 15 +++------------
lnbits/extensions/satspay/views.py | 5 ++---
lnbits/extensions/satspay/views_api.py | 17 +++--------------
6 files changed, 33 insertions(+), 31 deletions(-)
create mode 100644 lnbits/extensions/satspay/helpers.py
diff --git a/lnbits/extensions/satspay/crud.py b/lnbits/extensions/satspay/crud.py
index 47d7a4a8d..23d391b7b 100644
--- a/lnbits/extensions/satspay/crud.py
+++ b/lnbits/extensions/satspay/crud.py
@@ -102,7 +102,7 @@ async def check_address_balance(charge_id: str) -> List[Charges]:
charge = await get_charge(charge_id)
if not charge.paid:
if charge.onchainaddress:
- config = await get_config(charge.user)
+ config = await get_charge_config(charge_id)
try:
async with httpx.AsyncClient() as client:
r = await client.get(
@@ -122,3 +122,10 @@ async def check_address_balance(charge_id: str) -> List[Charges]:
return await update_charge(charge_id=charge_id, balance=charge.amount)
row = await db.fetchone("SELECT * FROM satspay.charges WHERE id = ?", (charge_id,))
return Charges.from_row(row) if row else None
+
+
+async def get_charge_config(charge_id: str):
+ row = await db.fetchone(
+ """SELECT "user" FROM satspay.charges WHERE id = ?""", (charge_id,)
+ )
+ return await get_config(row.user)
diff --git a/lnbits/extensions/satspay/helpers.py b/lnbits/extensions/satspay/helpers.py
new file mode 100644
index 000000000..2d15b5578
--- /dev/null
+++ b/lnbits/extensions/satspay/helpers.py
@@ -0,0 +1,17 @@
+from .models import Charges
+
+
+def compact_charge(charge: Charges):
+ return {
+ "id": charge.id,
+ "description": charge.description,
+ "onchainaddress": charge.onchainaddress,
+ "payment_request": charge.payment_request,
+ "payment_hash": charge.payment_hash,
+ "time": charge.time,
+ "amount": charge.amount,
+ "balance": charge.balance,
+ "paid": charge.paid,
+ "timestamp": charge.timestamp,
+ "completelink": charge.completelink, # should be secret?
+ }
diff --git a/lnbits/extensions/satspay/models.py b/lnbits/extensions/satspay/models.py
index e8638d5e2..daf63f429 100644
--- a/lnbits/extensions/satspay/models.py
+++ b/lnbits/extensions/satspay/models.py
@@ -19,7 +19,6 @@ class CreateCharge(BaseModel):
class Charges(BaseModel):
id: str
- user: str
description: Optional[str]
onchainwallet: Optional[str]
onchainaddress: Optional[str]
diff --git a/lnbits/extensions/satspay/templates/satspay/display.html b/lnbits/extensions/satspay/templates/satspay/display.html
index f34ac5095..12288c809 100644
--- a/lnbits/extensions/satspay/templates/satspay/display.html
+++ b/lnbits/extensions/satspay/templates/satspay/display.html
@@ -328,7 +328,7 @@
)
},
checkBalances: async function () {
- if (!this.charge.hasStaleBalance) await this.refreshCharge()
+ if (this.charge.hasStaleBalance) return
try {
const {data} = await LNbits.api.request(
'GET',
@@ -339,18 +339,9 @@
LNbits.utils.notifyApiError(error)
}
},
- refreshCharge: async function () {
- try {
- const {data} = await LNbits.api.request(
- 'GET',
- `/satspay/api/v1/charge/${this.charge.id}`
- )
- this.charge = mapCharge(data, this.charge)
- } catch (error) {
- LNbits.utils.notifyApiError(error)
- }
- },
checkPendingOnchain: async function () {
+ if (!this.charge.onchainaddress) return
+
const {
bitcoin: {addresses: addressesAPI}
} = mempoolJS({
diff --git a/lnbits/extensions/satspay/views.py b/lnbits/extensions/satspay/views.py
index 69d81dadf..b789bf8fe 100644
--- a/lnbits/extensions/satspay/views.py
+++ b/lnbits/extensions/satspay/views.py
@@ -9,10 +9,9 @@ from starlette.responses import HTMLResponse
from lnbits.core.crud import get_wallet
from lnbits.core.models import User
from lnbits.decorators import check_user_exists
-from lnbits.extensions.watchonly.crud import get_config
from . import satspay_ext, satspay_renderer
-from .crud import get_charge
+from .crud import get_charge, get_charge_config
templates = Jinja2Templates(directory="templates")
@@ -32,7 +31,7 @@ async def display(request: Request, charge_id: str):
status_code=HTTPStatus.NOT_FOUND, detail="Charge link does not exist."
)
wallet = await get_wallet(charge.lnbitswallet)
- onchainwallet_config = await get_config(charge.user)
+ onchainwallet_config = await get_charge_config(charge_id)
inkey = wallet.inkey if wallet else None
mempool_endpoint = (
onchainwallet_config.mempool_endpoint if onchainwallet_config else None
diff --git a/lnbits/extensions/satspay/views_api.py b/lnbits/extensions/satspay/views_api.py
index f94b970af..73c87e7c2 100644
--- a/lnbits/extensions/satspay/views_api.py
+++ b/lnbits/extensions/satspay/views_api.py
@@ -20,6 +20,7 @@ from .crud import (
get_charges,
update_charge,
)
+from .helpers import compact_charge
from .models import CreateCharge
#############################CHARGES##########################
@@ -123,25 +124,13 @@ async def api_charge_balance(charge_id):
try:
r = await client.post(
charge.webhook,
- json={
- "id": charge.id,
- "description": charge.description,
- "onchainaddress": charge.onchainaddress,
- "payment_request": charge.payment_request,
- "payment_hash": charge.payment_hash,
- "time": charge.time,
- "amount": charge.amount,
- "balance": charge.balance,
- "paid": charge.paid,
- "timestamp": charge.timestamp,
- "completelink": charge.completelink,
- },
+ json=compact_charge(charge),
timeout=40,
)
except AssertionError:
charge.webhook = None
return {
- **charge.dict(),
+ **compact_charge(charge),
**{"time_elapsed": charge.time_elapsed},
**{"time_left": charge.time_left},
**{"paid": charge.paid},
From daf5768d52c39cbcf24279d2c8604a2fefa1a197 Mon Sep 17 00:00:00 2001
From: Vlad Stan
Date: Fri, 21 Oct 2022 15:49:17 +0300
Subject: [PATCH 54/57] fix: remove user from model (#1067)
---
lnbits/extensions/watchonly/crud.py | 4 ++--
lnbits/extensions/watchonly/models.py | 1 -
lnbits/extensions/watchonly/views_api.py | 3 +--
3 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/lnbits/extensions/watchonly/crud.py b/lnbits/extensions/watchonly/crud.py
index de338b91e..c4a1df728 100644
--- a/lnbits/extensions/watchonly/crud.py
+++ b/lnbits/extensions/watchonly/crud.py
@@ -10,7 +10,7 @@ from .models import Address, Config, WalletAccount
##########################WALLETS####################
-async def create_watch_wallet(w: WalletAccount) -> WalletAccount:
+async def create_watch_wallet(user: str, w: WalletAccount) -> WalletAccount:
wallet_id = urlsafe_short_hash()
await db.execute(
"""
@@ -30,7 +30,7 @@ async def create_watch_wallet(w: WalletAccount) -> WalletAccount:
""",
(
wallet_id,
- w.user,
+ user,
w.masterpub,
w.fingerprint,
w.title,
diff --git a/lnbits/extensions/watchonly/models.py b/lnbits/extensions/watchonly/models.py
index 622f5ec80..d8c278ff8 100644
--- a/lnbits/extensions/watchonly/models.py
+++ b/lnbits/extensions/watchonly/models.py
@@ -14,7 +14,6 @@ class CreateWallet(BaseModel):
class WalletAccount(BaseModel):
id: str
- user: str
masterpub: str
fingerprint: str
title: str
diff --git a/lnbits/extensions/watchonly/views_api.py b/lnbits/extensions/watchonly/views_api.py
index 750d46c98..9030b9c3d 100644
--- a/lnbits/extensions/watchonly/views_api.py
+++ b/lnbits/extensions/watchonly/views_api.py
@@ -86,7 +86,6 @@ async def api_wallet_create_or_update(
new_wallet = WalletAccount(
id="none",
- user=w.wallet.user,
masterpub=data.masterpub,
fingerprint=descriptor.keys[0].fingerprint.hex(),
type=descriptor.scriptpubkey_type(),
@@ -115,7 +114,7 @@ async def api_wallet_create_or_update(
)
)
- wallet = await create_watch_wallet(new_wallet)
+ wallet = await create_watch_wallet(w.wallet.user, new_wallet)
await api_get_addresses(wallet.id, w)
except Exception as e:
From 2d61a8854f38a61507289af3520e52ff54ccb1a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Fri, 21 Oct 2022 17:35:58 +0200
Subject: [PATCH 55/57] improving on split payment handling
---
lnbits/extensions/splitpayments/models.py | 2 +-
lnbits/extensions/splitpayments/tasks.py | 72 ++++++++---------------
2 files changed, 24 insertions(+), 50 deletions(-)
diff --git a/lnbits/extensions/splitpayments/models.py b/lnbits/extensions/splitpayments/models.py
index 4b95ed18b..6338d97f4 100644
--- a/lnbits/extensions/splitpayments/models.py
+++ b/lnbits/extensions/splitpayments/models.py
@@ -14,7 +14,7 @@ class Target(BaseModel):
class TargetPutList(BaseModel):
wallet: str = Query(...)
alias: str = Query("")
- percent: float = Query(..., ge=0.01)
+ percent: float = Query(..., ge=0.01, lt=100)
class TargetPut(BaseModel):
diff --git a/lnbits/extensions/splitpayments/tasks.py b/lnbits/extensions/splitpayments/tasks.py
index cfc6c226f..53378b205 100644
--- a/lnbits/extensions/splitpayments/tasks.py
+++ b/lnbits/extensions/splitpayments/tasks.py
@@ -1,13 +1,11 @@
import asyncio
-import json
from loguru import logger
-from lnbits.core import db as core_db
-from lnbits.core.crud import create_payment
from lnbits.core.models import Payment
-from lnbits.helpers import get_current_extension_name, urlsafe_short_hash
-from lnbits.tasks import internal_invoice_queue, register_invoice_listener
+from lnbits.core.services import create_invoice, pay_invoice
+from lnbits.helpers import get_current_extension_name
+from lnbits.tasks import register_invoice_listener
from .crud import get_targets
@@ -22,60 +20,36 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
- if payment.extra.get("tag") == "splitpayments" or payment.extra.get("splitted"):
- # already splitted, ignore
+ if payment.extra.get("tag") == "splitpayments":
+ # already a splitted payment, ignore
return
- # now we make some special internal transfers (from no one to the receiver)
targets = await get_targets(payment.wallet_id)
if not targets:
return
- transfers = [
- (target.wallet, int(target.percent * payment.amount / 100))
- for target in targets
- ]
- transfers = [(wallet, amount) for wallet, amount in transfers if amount > 0]
- amount_left = payment.amount - sum([amount for _, amount in transfers])
+ total_percent = sum([target.percent for target in targets])
- if amount_left < 0:
- logger.error(
- "splitpayments failure: amount_left is negative.", payment.payment_hash
- )
+ if total_percent > 100:
+ logger.error("splitpayment failure: total percent adds up to more than 100%")
return
- # mark the original payment with one extra key, "splitted"
- # (this prevents us from doing this process again and it's informative)
- # and reduce it by the amount we're going to send to the producer
- await core_db.execute(
- """
- UPDATE apipayments
- SET extra = ?, amount = ?
- WHERE hash = ?
- AND checking_id NOT LIKE 'internal_%'
- """,
- (
- json.dumps(dict(**payment.extra, splitted=True)),
- amount_left,
- payment.payment_hash,
- ),
- )
-
- # perform the internal transfer using the same payment_hash
- for wallet, amount in transfers:
- internal_checking_id = f"internal_{urlsafe_short_hash()}"
- await create_payment(
- wallet_id=wallet,
- checking_id=internal_checking_id,
- payment_request="",
- payment_hash=payment.payment_hash,
- amount=amount,
- memo=payment.memo,
- pending=False,
+ logger.debug(f"performing split payments to {len(targets)} targets")
+ for target in targets:
+ amount = int(payment.amount * target.percent / 100) # msats
+ payment_hash, payment_request = await create_invoice(
+ wallet_id=target.wallet,
+ amount=int(amount / 1000), # sats
+ internal=True,
+ memo=f"split payment: {target.percent}% for {target.alias or target.wallet}",
extra={"tag": "splitpayments"},
)
+ logger.debug(f"created split invoice: {payment_hash}")
- # manually send this for now
- await internal_invoice_queue.put(internal_checking_id)
- return
+ checking_id = await pay_invoice(
+ payment_request=payment_request,
+ wallet_id=payment.wallet_id,
+ extra={"tag": "splitpayments"},
+ )
+ logger.debug(f"paid split invoice: {checking_id}")
From fe879ad9b964766c25f8b59a0323d009d332166d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Fri, 21 Oct 2022 18:15:27 +0200
Subject: [PATCH 56/57] add tpos to pr
---
lnbits/extensions/tpos/tasks.py | 58 ++++++++++-----------------------
1 file changed, 17 insertions(+), 41 deletions(-)
diff --git a/lnbits/extensions/tpos/tasks.py b/lnbits/extensions/tpos/tasks.py
index f18d16892..6369bbc7f 100644
--- a/lnbits/extensions/tpos/tasks.py
+++ b/lnbits/extensions/tpos/tasks.py
@@ -1,11 +1,11 @@
import asyncio
-import json
-from lnbits.core import db as core_db
-from lnbits.core.crud import create_payment
+from loguru import logger
+
from lnbits.core.models import Payment
-from lnbits.helpers import get_current_extension_name, urlsafe_short_hash
-from lnbits.tasks import internal_invoice_queue, register_invoice_listener
+from lnbits.core.services import create_invoice, pay_invoice
+from lnbits.helpers import get_current_extension_name
+from lnbits.tasks import register_invoice_listener
from .crud import get_tpos
@@ -20,11 +20,9 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
- if payment.extra.get("tag") == "tpos" and payment.extra.get("tipSplitted"):
- # already splitted, ignore
+ if payment.extra.get("tag") != "tpos":
return
- # now we make some special internal transfers (from no one to the receiver)
tpos = await get_tpos(payment.extra.get("tposId"))
tipAmount = payment.extra.get("tipAmount")
@@ -32,39 +30,17 @@ async def on_invoice_paid(payment: Payment) -> None:
# no tip amount
return
- tipAmount = tipAmount * 1000
- amount = payment.amount - tipAmount
-
- # mark the original payment with one extra key, "splitted"
- # (this prevents us from doing this process again and it's informative)
- # and reduce it by the amount we're going to send to the producer
- await core_db.execute(
- """
- UPDATE apipayments
- SET extra = ?, amount = ?
- WHERE hash = ?
- AND checking_id NOT LIKE 'internal_%'
- """,
- (
- json.dumps(dict(**payment.extra, tipSplitted=True)),
- amount,
- payment.payment_hash,
- ),
- )
-
- # perform the internal transfer using the same payment_hash
- internal_checking_id = f"internal_{urlsafe_short_hash()}"
- await create_payment(
+ payment_hash, payment_request = await create_invoice(
wallet_id=tpos.tip_wallet,
- checking_id=internal_checking_id,
- payment_request="",
- payment_hash=payment.payment_hash,
- amount=tipAmount,
- memo=f"Tip for {payment.memo}",
- pending=False,
- extra={"tipSplitted": True},
+ amount=int(tipAmount), # sats
+ internal=True,
+ memo=f"tpos tip",
)
+ logger.debug(f"tpos: tip invoice created: {payment_hash}")
- # manually send this for now
- await internal_invoice_queue.put(internal_checking_id)
- return
+ checking_id = await pay_invoice(
+ payment_request=payment_request,
+ wallet_id=payment.wallet_id,
+ extra={"tag": "tpos"},
+ )
+ logger.debug(f"tpos: tip invoice paid: {checking_id}")
From e1fbb8c3cd112d56bbd147565cc649f007d42287 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?dni=20=E2=9A=A1?=
Date: Fri, 21 Oct 2022 18:28:39 +0200
Subject: [PATCH 57/57] fix livestream extension
---
lnbits/extensions/livestream/tasks.py | 52 ++++++++-------------------
1 file changed, 14 insertions(+), 38 deletions(-)
diff --git a/lnbits/extensions/livestream/tasks.py b/lnbits/extensions/livestream/tasks.py
index 626c698ca..d081332f5 100644
--- a/lnbits/extensions/livestream/tasks.py
+++ b/lnbits/extensions/livestream/tasks.py
@@ -4,10 +4,10 @@ import json
from loguru import logger
from lnbits.core import db as core_db
-from lnbits.core.crud import create_payment
from lnbits.core.models import Payment
-from lnbits.helpers import get_current_extension_name, urlsafe_short_hash
-from lnbits.tasks import internal_invoice_listener, register_invoice_listener
+from lnbits.core.services import create_invoice, pay_invoice
+from lnbits.helpers import get_current_extension_name
+from lnbits.tasks import register_invoice_listener
from .crud import get_livestream_by_track, get_producer, get_track
@@ -44,44 +44,20 @@ async def on_invoice_paid(payment: Payment) -> None:
# now we make a special kind of internal transfer
amount = int(payment.amount * (100 - ls.fee_pct) / 100)
- # mark the original payment with two extra keys, "shared_with" and "received"
- # (this prevents us from doing this process again and it's informative)
- # and reduce it by the amount we're going to send to the producer
- await core_db.execute(
- """
- UPDATE apipayments
- SET extra = ?, amount = ?
- WHERE hash = ?
- AND checking_id NOT LIKE 'internal_%'
- """,
- (
- json.dumps(
- dict(
- **payment.extra,
- shared_with=[producer.name, producer.id],
- received=payment.amount,
- )
- ),
- payment.amount - amount,
- payment.payment_hash,
- ),
- )
-
- # perform an internal transfer using the same payment_hash to the producer wallet
- internal_checking_id = f"internal_{urlsafe_short_hash()}"
- await create_payment(
- wallet_id=producer.wallet,
- checking_id=internal_checking_id,
- payment_request="",
- payment_hash=payment.payment_hash,
- amount=amount,
+ payment_hash, payment_request = await create_invoice(
+ wallet_id=tpos.tip_wallet,
+ amount=amount, # sats
+ internal=True,
memo=f"Revenue from '{track.name}'.",
- pending=False,
)
+ logger.debug(f"livestream: producer invoice created: {payment_hash}")
- # manually send this for now
- # await internal_invoice_paid.send(internal_checking_id)
- await internal_invoice_listener.put(internal_checking_id)
+ checking_id = await pay_invoice(
+ payment_request=payment_request,
+ wallet_id=payment.wallet_id,
+ extra={"tag": "livestream"},
+ )
+ logger.debug(f"livestream: producer invoice paid: {checking_id}")
# so the flow is the following:
# - we receive, say, 1000 satoshis