From e0981ecede4706956e9fb814c6087759f89cfb20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Fri, 23 Sep 2022 09:58:19 +0200 Subject: [PATCH 01/96] change python version of lnd wallet (#996) --- .github/workflows/regtest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/regtest.yml b/.github/workflows/regtest.yml index 250a66c7..0029eed4 100644 --- a/.github/workflows/regtest.yml +++ b/.github/workflows/regtest.yml @@ -46,7 +46,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.8] + python-version: [3.9] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} From a98c5f9a8548e2b5f977864838dc078a4f1fd325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Fri, 23 Sep 2022 10:00:45 +0200 Subject: [PATCH 02/96] fixes issue #977 (#999) * fixes issue #977 * fix prettier formatting, when pre-commit --- lnbits/core/static/js/wallet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/static/js/wallet.js b/lnbits/core/static/js/wallet.js index e62c1a6d..76d82ad4 100644 --- a/lnbits/core/static/js/wallet.js +++ b/lnbits/core/static/js/wallet.js @@ -675,7 +675,7 @@ new Vue({ // status is important for export but it is not in paymentsTable // because it is manually added with payment detail link and icons // and would cause duplication in the list - let columns = this.paymentsTable.columns + let columns = structuredClone(this.paymentsTable.columns) columns.unshift({ name: 'pending', align: 'left', From d64feec2a54437d70bf43b8cd9f544bcf143c8c9 Mon Sep 17 00:00:00 2001 From: Jesse de Wit Date: Fri, 23 Sep 2022 10:01:14 +0200 Subject: [PATCH 03/96] strip environment variable list items (#993) --- lnbits/settings.py | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/lnbits/settings.py b/lnbits/settings.py index 25e43eec..3f4e31cc 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -24,18 +24,21 @@ LNBITS_DATA_FOLDER = env.str( ) LNBITS_DATABASE_URL = env.str("LNBITS_DATABASE_URL", default=None) -LNBITS_ALLOWED_USERS: List[str] = env.list( - "LNBITS_ALLOWED_USERS", default=[], subcast=str -) -LNBITS_ADMIN_USERS: List[str] = env.list("LNBITS_ADMIN_USERS", default=[], subcast=str) -LNBITS_ADMIN_EXTENSIONS: List[str] = env.list( - "LNBITS_ADMIN_EXTENSIONS", default=[], subcast=str -) -LNBITS_DISABLED_EXTENSIONS: List[str] = env.list( - "LNBITS_DISABLED_EXTENSIONS", default=[], subcast=str -) +LNBITS_ALLOWED_USERS: List[str] = [ + x.strip(" ") for x in env.list("LNBITS_ALLOWED_USERS", default=[], subcast=str) +] +LNBITS_ADMIN_USERS: List[str] = [ + x.strip(" ") for x in env.list("LNBITS_ADMIN_USERS", default=[], subcast=str) +] +LNBITS_ADMIN_EXTENSIONS: List[str] = [ + x.strip(" ") for x in env.list("LNBITS_ADMIN_EXTENSIONS", default=[], subcast=str) +] +LNBITS_DISABLED_EXTENSIONS: List[str] = [ + x.strip(" ") + for x in env.list("LNBITS_DISABLED_EXTENSIONS", default=[], subcast=str) +] -LNBITS_AD_SPACE = env.list("LNBITS_AD_SPACE", default=[]) +LNBITS_AD_SPACE = [x.strip(" ") for x in env.list("LNBITS_AD_SPACE", default=[])] LNBITS_HIDE_API = env.bool("LNBITS_HIDE_API", default=False) LNBITS_SITE_TITLE = env.str("LNBITS_SITE_TITLE", default="LNbits") LNBITS_DENOMINATION = env.str("LNBITS_DENOMINATION", default="sats") @@ -43,11 +46,14 @@ LNBITS_SITE_TAGLINE = env.str( "LNBITS_SITE_TAGLINE", default="free and open-source lightning wallet" ) LNBITS_SITE_DESCRIPTION = env.str("LNBITS_SITE_DESCRIPTION", default="") -LNBITS_THEME_OPTIONS: List[str] = env.list( - "LNBITS_THEME_OPTIONS", - default="classic, flamingo, mint, salvador, monochrome, autumn", - subcast=str, -) +LNBITS_THEME_OPTIONS: List[str] = [ + x.strip(" ") + for x in env.list( + "LNBITS_THEME_OPTIONS", + default="classic, flamingo, mint, salvador, monochrome, autumn", + subcast=str, + ) +] LNBITS_CUSTOM_LOGO = env.str("LNBITS_CUSTOM_LOGO", default="") WALLET = wallet_class() From b47685f8aa481453fc7e113b31812c8994807dfb Mon Sep 17 00:00:00 2001 From: fusion44 Date: Fri, 23 Sep 2022 10:02:23 +0200 Subject: [PATCH 04/96] Updates and fixes (#912) * feat: add support for python 3.10 * fix: move all in-line comments to their own line On some systems the environs package was uable to parse values as it tried read the comments as code... --- .env.example | 13 +++-- poetry.lock | 131 ++++++++++++++++++++++++++++++++----------------- pyproject.toml | 4 +- 3 files changed, 96 insertions(+), 52 deletions(-) diff --git a/.env.example b/.env.example index 97105bc3..93b82325 100644 --- a/.env.example +++ b/.env.example @@ -9,8 +9,11 @@ LNBITS_ADMIN_USERS="" LNBITS_ADMIN_EXTENSIONS="ngrok" LNBITS_DEFAULT_WALLET_NAME="LNbits wallet" -LNBITS_AD_SPACE="" # csv ad image filepaths or urls, extensions can choose to honor -LNBITS_HIDE_API=false # Hides wallet api, extensions can choose to honor +# csv ad image filepaths or urls, extensions can choose to honor +LNBITS_AD_SPACE="" + +# Hides wallet api, extensions can choose to honor +LNBITS_HIDE_API=false # Disable extensions for all users, use "all" to disable all extensions LNBITS_DISABLED_EXTENSIONS="amilk" @@ -25,8 +28,10 @@ LNBITS_DATA_FOLDER="./data" LNBITS_FORCE_HTTPS=true LNBITS_SERVICE_FEE="0.0" -LNBITS_RESERVE_FEE_MIN=2000 # value in millisats -LNBITS_RESERVE_FEE_PERCENT=1.0 # value in percent +# value in millisats +LNBITS_RESERVE_FEE_MIN=2000 +# value in percent +LNBITS_RESERVE_FEE_PERCENT=1.0 # Change theme LNBITS_SITE_TITLE="LNbits" diff --git a/poetry.lock b/poetry.lock index 975c62b2..38f08575 100644 --- a/poetry.lock +++ b/poetry.lock @@ -159,7 +159,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "coverage" -version = "6.4.2" +version = "6.4.4" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -194,6 +194,14 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "enum34" +version = "1.1.10" +description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "environs" version = "9.3.3" @@ -374,7 +382,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} [package.extras] -dev = ["isort (>=5.1.1)", "black (>=19.10b0)", "sphinx-rtd-theme (>=0.4.3)", "sphinx-autobuild (>=0.7.1)", "Sphinx (>=2.2.1)", "pytest-cov (>=2.7.1)", "pytest (>=4.6.2)", "tox-travis (>=0.12)", "tox (>=3.9.0)", "flake8 (>=3.7.7)", "colorama (>=0.3.4)", "codecov (>=2.0.15)"] +dev = ["codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "tox (>=3.9.0)", "tox-travis (>=0.12)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "Sphinx (>=2.2.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "black (>=19.10b0)", "isort (>=5.1.1)"] [[package]] name = "markupsafe" @@ -463,6 +471,17 @@ python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" +[[package]] +name = "pathlib2" +version = "2.3.7.post1" +description = "Object-oriented filesystem paths" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +six = "*" + [[package]] name = "pathspec" version = "0.9.0" @@ -577,13 +596,15 @@ PNG = ["pypng (>=0.0.13)"] [[package]] name = "pyscss" -version = "1.3.7" +version = "1.4.0" description = "pyScss, a Scss compiler for Python" category = "main" optional = false python-versions = "*" [package.dependencies] +enum34 = "*" +pathlib2 = "*" six = "*" [[package]] @@ -897,7 +918,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "^3.9 | ^3.8 | ^3.7" -content-hash = "cadb8f2e46f0c083e91956f4f0f70b53b6c106f1c0b47972b57132dfee357367" +content-hash = "ac8c4117d537aaf8853d35038f2821ea4bc04b29c7971b91fd46329365008b95" [metadata.files] aiofiles = [ @@ -1025,47 +1046,56 @@ colorama = [ {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, ] coverage = [ - {file = "coverage-6.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e"}, - {file = "coverage-6.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c"}, - {file = "coverage-6.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8"}, - {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39"}, - {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0"}, - {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee"}, - {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d"}, - {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc"}, - {file = "coverage-6.4.2-cp310-cp310-win32.whl", hash = "sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386"}, - {file = "coverage-6.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0"}, - {file = "coverage-6.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46"}, - {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07"}, - {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039"}, - {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996"}, - {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f"}, - {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e"}, - {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083"}, - {file = "coverage-6.4.2-cp37-cp37m-win32.whl", hash = "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7"}, - {file = "coverage-6.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120"}, - {file = "coverage-6.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"}, - {file = "coverage-6.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32"}, - {file = "coverage-6.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae"}, - {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8"}, - {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1"}, - {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63"}, - {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933"}, - {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de"}, - {file = "coverage-6.4.2-cp38-cp38-win32.whl", hash = "sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783"}, - {file = "coverage-6.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6"}, - {file = "coverage-6.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f"}, - {file = "coverage-6.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f"}, - {file = "coverage-6.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe"}, - {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29"}, - {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55"}, - {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b"}, - {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978"}, - {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c"}, - {file = "coverage-6.4.2-cp39-cp39-win32.whl", hash = "sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd"}, - {file = "coverage-6.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf"}, - {file = "coverage-6.4.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97"}, - {file = "coverage-6.4.2.tar.gz", hash = "sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe"}, + {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"}, ] ecdsa = [ {file = "ecdsa-0.17.0-py2.py3-none-any.whl", hash = "sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676"}, @@ -1074,6 +1104,11 @@ ecdsa = [ embit = [ {file = "embit-0.4.9.tar.gz", hash = "sha256:992332bd89af6e2d027e26fe437eb14aa33997db08c882c49064d49c3e6f4ab9"}, ] +enum34 = [ + {file = "enum34-1.1.10-py2-none-any.whl", hash = "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53"}, + {file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"}, + {file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"}, +] environs = [ {file = "environs-9.3.3-py2.py3-none-any.whl", hash = "sha256:ee5466156b50fe03aa9fec6e720feea577b5bf515d7f21b2c46608272557ba26"}, {file = "environs-9.3.3.tar.gz", hash = "sha256:72b867ff7b553076cdd90f3ee01ecc1cf854987639c9c459f0ed0d3d44ae490c"}, @@ -1274,6 +1309,10 @@ packaging = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, ] +pathlib2 = [ + {file = "pathlib2-2.3.7.post1-py2.py3-none-any.whl", hash = "sha256:5266a0fd000452f1b3467d782f079a4343c63aaa119221fbdc4e39577489ca5b"}, + {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"}, @@ -1397,7 +1436,7 @@ pyqrcode = [ {file = "PyQRCode-1.2.1.zip", hash = "sha256:1b2812775fa6ff5c527977c4cd2ccb07051ca7d0bc0aecf937a43864abe5eff6"}, ] pyscss = [ - {file = "pyScss-1.3.7.tar.gz", hash = "sha256:f1df571569021a23941a538eb154405dde80bed35dc1ea7c5f3e18e0144746bf"}, + {file = "pyScss-1.4.0.tar.gz", hash = "sha256:8f35521ffe36afa8b34c7d6f3195088a7057c185c2b8f15ee459ab19748669ff"}, ] pytest = [ {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, diff --git a/pyproject.toml b/pyproject.toml index 1ae8c1fe..fc4bb198 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ generate-setup-file = false script = "build.py" [tool.poetry.dependencies] -python = "^3.9 | ^3.8 | ^3.7" +python = "^3.10 | ^3.9 | ^3.8 | ^3.7" aiofiles = "0.8.0" asgiref = "3.4.1" attrs = "21.2.0" @@ -39,7 +39,7 @@ pycryptodomex = "3.14.1" pydantic = "1.8.2" pypng = "0.0.21" pyqrcode = "1.2.1" -pyscss = "1.3.7" +pyScss = "1.4.0" python-dotenv = "0.19.0" pyyaml = "5.4.1" represent = "1.6.0.post0" From 8305ebe6e8dd82ee2bab5c7d56090661c7e2660a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Fri, 23 Sep 2022 10:02:44 +0200 Subject: [PATCH 05/96] fix scss error on poetry install (#989) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fc4bb198..e95c6a2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,7 +72,7 @@ mypy = "^0.971" types-protobuf = "^3.19.22" [build-system] -requires = ["poetry-core>=1.0.0"] +requires = ["poetry-core>=1.0.0", "pyScss"] build-backend = "poetry.core.masonry.api" [tool.poetry.scripts] From d8acad42823721edbd0f6aa7741f500e8e280ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Fri, 23 Sep 2022 10:35:18 +0200 Subject: [PATCH 06/96] make sure tests not spamming boltz live servers (#1000) * make sure tests not spamming boltz live servers * fixing tests to no run on live servers --- Makefile | 8 +++++ tests/extensions/boltz/test_api.py | 53 +++--------------------------- 2 files changed, 13 insertions(+), 48 deletions(-) diff --git a/Makefile b/Makefile index 6b2fdeb7..4f99f1da 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,10 @@ checkisort: poetry run isort --check-only . test: + BOLTZ_NETWORK="regtest" \ + BOLTZ_URL="http://127.0.0.1:9001" \ + BOLTZ_MEMPOOL_SPACE_URL="http://127.0.0.1:8080" \ + BOLTZ_MEMPOOL_SPACE_URL_WS="ws://127.0.0.1:8080" \ LNBITS_BACKEND_WALLET_CLASS="FakeWallet" \ FAKE_WALLET_SECRET="ToTheMoon1" \ LNBITS_DATA_FOLDER="./tests/data" \ @@ -46,6 +50,10 @@ test-real-wallet: poetry run pytest test-venv: + BOLTZ_NETWORK="regtest" \ + BOLTZ_URL="http://127.0.0.1:9001" \ + BOLTZ_MEMPOOL_SPACE_URL="http://127.0.0.1:8080" \ + BOLTZ_MEMPOOL_SPACE_URL_WS="ws://127.0.0.1:8080" \ LNBITS_BACKEND_WALLET_CLASS="FakeWallet" \ FAKE_WALLET_SECRET="ToTheMoon1" \ LNBITS_DATA_FOLDER="./tests/data" \ diff --git a/tests/extensions/boltz/test_api.py b/tests/extensions/boltz/test_api.py index 20b6e5a4..90ce6ec1 100644 --- a/tests/extensions/boltz/test_api.py +++ b/tests/extensions/boltz/test_api.py @@ -5,18 +5,21 @@ from tests.helpers import is_fake, is_regtest @pytest.mark.asyncio +@pytest.mark.skipif(is_fake, reason="this test is only passes with regtest") async def test_mempool_url(client): response = await client.get("/boltz/api/v1/swap/mempool") assert response.status_code == 200 @pytest.mark.asyncio +@pytest.mark.skipif(is_fake, reason="this test is only passes with regtest") async def test_boltz_config(client): response = await client.get("/boltz/api/v1/swap/boltz") assert response.status_code == 200 @pytest.mark.asyncio +@pytest.mark.skipif(is_fake, reason="this test is only passes with regtest") async def test_endpoints_unauthenticated(client): response = await client.get("/boltz/api/v1/swap?all_wallets=true") assert response.status_code == 401 @@ -33,6 +36,7 @@ async def test_endpoints_unauthenticated(client): @pytest.mark.asyncio +@pytest.mark.skipif(is_fake, reason="this test is only passes with regtest") async def test_endpoints_inkey(client, inkey_headers_to): response = await client.get( "/boltz/api/v1/swap?all_wallets=true", headers=inkey_headers_to @@ -56,6 +60,7 @@ async def test_endpoints_inkey(client, inkey_headers_to): @pytest.mark.asyncio +@pytest.mark.skipif(is_fake, reason="this test is only passes with regtest") async def test_endpoints_adminkey_nocontent(client, adminkey_headers_to): response = await client.post("/boltz/api/v1/swap", headers=adminkey_headers_to) assert response.status_code == 204 @@ -73,54 +78,6 @@ async def test_endpoints_adminkey_nocontent(client, adminkey_headers_to): assert response.status_code == 204 -@pytest.mark.asyncio -@pytest.mark.skipif(is_regtest, reason="this test is only passes with fakewallet") -async def test_endpoints_adminkey_fakewallet(client, from_wallet, adminkey_headers_to): - response = await client.post( - "/boltz/api/v1/swap/check", headers=adminkey_headers_to - ) - assert response.status_code == 200 - swap = { - "wallet": from_wallet.id, - "refund_address": "bcrt1q3cwq33y435h52gq3qqsdtczh38ltlnf69zvypm", - "amount": 50_000, - } - response = await client.post( - "/boltz/api/v1/swap", json=swap, headers=adminkey_headers_to - ) - assert response.status_code == 405 - reverse_swap = { - "wallet": from_wallet.id, - "instant_settlement": True, - "onchain_address": "bcrt1q4vfyszl4p8cuvqh07fyhtxve5fxq8e2ux5gx43", - "amount": 50_000, - } - response = await client.post( - "/boltz/api/v1/swap/reverse", json=reverse_swap, headers=adminkey_headers_to - ) - assert response.status_code == 201 - reverse_swap = response.json() - assert reverse_swap["id"] is not None - response = await client.post( - "/boltz/api/v1/swap/status", - params={"swap_id": reverse_swap["id"]}, - headers=adminkey_headers_to, - ) - assert response.status_code == 200 - response = await client.post( - "/boltz/api/v1/swap/status", - params={"swap_id": "wrong"}, - headers=adminkey_headers_to, - ) - assert response.status_code == 404 - response = await client.post( - "/boltz/api/v1/swap/refund", - params={"swap_id": "wrong"}, - headers=adminkey_headers_to, - ) - assert response.status_code == 404 - - @pytest.mark.asyncio @pytest.mark.skipif(is_fake, reason="this test is only passes with regtest") async def test_endpoints_adminkey_regtest(client, from_wallet, adminkey_headers_to): From e80daf27957b315676f51f39059e4f3488029e03 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Fri, 23 Sep 2022 10:25:57 +0100 Subject: [PATCH 07/96] rename stores to stalls --- .../templates/diagonalley/index.html | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/index.html b/lnbits/extensions/diagonalley/templates/diagonalley/index.html index c951fb0a..eb290a92 100644 --- a/lnbits/extensions/diagonalley/templates/diagonalley/index.html +++ b/lnbits/extensions/diagonalley/templates/diagonalley/index.html @@ -314,7 +314,7 @@ unelevated color="primary" type="submit" - >Update StoreUpdate Stall Create StoreCreate Stall + Store - Create a stall to list products on + Stall + + Create a market stall to list products on + + Store - Create a store to list products on + Stall + + Create a market stall to list products on + Launch frontend shop (not Nostr) + >Create Market - Makes a simple frontend shop for your stalls @@ -596,11 +600,11 @@ - +
-
Stores
+
Market Stalls
Date: Fri, 23 Sep 2022 15:33:51 +0200 Subject: [PATCH 08/96] API auditor (#1002) * just quick auditor * fix for none balance * handle node down case Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> Co-authored-by: calle <93376500+callebtc@users.noreply.github.com> --- lnbits/core/crud.py | 5 +++++ lnbits/core/views/api.py | 27 ++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index ecc27a9c..cbed6292 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -177,6 +177,11 @@ async def get_wallet_for_key( return Wallet(**row) +async def get_total_balance(conn: Optional[Connection] = None): + row = await (conn or db).fetchone("SELECT SUM(balance) FROM balances") + return 0 if row[0] is None else row[0] + + # wallet payments # --------------- diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index af453f03..7a2bbbe6 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -2,6 +2,7 @@ import asyncio import binascii import hashlib import json +import time from http import HTTPStatus from io import BytesIO from typing import Dict, List, Optional, Tuple, Union @@ -27,7 +28,7 @@ from lnbits.decorators import ( require_invoice_key, ) from lnbits.helpers import url_for, urlsafe_short_hash -from lnbits.settings import LNBITS_ADMIN_USERS, LNBITS_SITE_TITLE +from lnbits.settings import LNBITS_ADMIN_USERS, LNBITS_SITE_TITLE, WALLET from lnbits.utils.exchange_rates import ( currencies, fiat_amount_as_satoshis, @@ -39,6 +40,7 @@ from ..crud import ( create_payment, get_payments, get_standalone_payment, + get_total_balance, get_wallet, get_wallet_for_key, save_balance_check, @@ -657,3 +659,26 @@ async def img(request: Request, data): "Expires": "0", }, ) + + +@core_app.get("/api/v1/audit/") +async def api_auditor(wallet: WalletTypeInfo = Depends(get_key_type)): + if wallet.wallet.user not in LNBITS_ADMIN_USERS: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, detail="Not an admin user" + ) + + total_balance = await get_total_balance() + error_message, node_balance = await WALLET.status() + + if not error_message: + delta = node_balance - total_balance + else: + node_balance, delta = None, None + + return { + "node_balance_msats": node_balance, + "lnbits_balance_msats": total_balance, + "delta_msats": delta, + "timestamp": int(time.time()), + } From f6d12c9d24551ffd9f401e8cb5047d113211183b Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Fri, 23 Sep 2022 14:40:20 +0100 Subject: [PATCH 09/96] prevent ios autozoom on inputs (#945) Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com> --- lnbits/templates/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/templates/base.html b/lnbits/templates/base.html index acca92e7..67241bb5 100644 --- a/lnbits/templates/base.html +++ b/lnbits/templates/base.html @@ -12,7 +12,7 @@ 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 10/96] 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 0029eed4..6572ccdb 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 592c29ef..80fb54c0 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 38f08575..ea83e25e 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 e95c6a2e..7f833aa5 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 11/96] 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 fed097d2..6259fe7b 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 12/96] 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 cedaa210..622f5ec8 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 f2cccbba..9f38df1d 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 263f6d92..91577cd2 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 77d28fee..750d46c9 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 13/96] 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 33d07d94..33261280 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 14/96] 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 ba52c4fa..e40ca81f 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 15/96] 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 2ed2d49c..a95a1906 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 33261280..b27f2fa2 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 16/96] 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 b27f2fa2..67268eeb 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 17/96] 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 91577cd2..67f89810 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 18/96] 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 67268eeb..8d3c1fec 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 19/96] 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 e106ace3..e3d0fd35 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 90006d2a..c280ad7d 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 61601731..d80da678 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 6572ccdb..2d7aae6b 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 298d7ff0..5d368fbb 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 20/96] 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 cde65ca2..6e1d94c7 100644 --- a/lnbits/extensions/watchonly/static/components/payment/payment.html +++ b/lnbits/extensions/watchonly/static/components/payment/payment.html @@ -5,7 +5,7 @@ Date: Wed, 28 Sep 2022 11:11:52 +0100 Subject: [PATCH 21/96] WIP added keys to merchant --- .../templates/diagonalley/index.html | 50 ++++++++++++++----- lnbits/extensions/diagonalley/views_api.py | 13 +++++ 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/index.html b/lnbits/extensions/diagonalley/templates/diagonalley/index.html index eb290a92..cd03e6f3 100644 --- a/lnbits/extensions/diagonalley/templates/diagonalley/index.html +++ b/lnbits/extensions/diagonalley/templates/diagonalley/index.html @@ -253,28 +253,32 @@ > - + > --> {% endblock %} {% block scripts %} {{ window_vars(user) }} + + +{% endblock %} From e0b93fbd49df489bc8dcea3ec1cab41650b2a49f Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Wed, 28 Sep 2022 11:12:34 +0100 Subject: [PATCH 23/96] chat and WS initial commit --- lnbits/extensions/diagonalley/views.py | 49 ++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/diagonalley/views.py b/lnbits/extensions/diagonalley/views.py index d0fee249..d6e701a0 100644 --- a/lnbits/extensions/diagonalley/views.py +++ b/lnbits/extensions/diagonalley/views.py @@ -1,6 +1,7 @@ from http import HTTPStatus +from typing import List -from fastapi import Request +from fastapi import Query, Request, WebSocket, WebSocketDisconnect from fastapi.params import Depends from fastapi.templating import Jinja2Templates from loguru import logger @@ -11,10 +12,10 @@ from lnbits.core.models import User from lnbits.decorators import check_user_exists # type: ignore from lnbits.extensions.diagonalley import diagonalley_ext, diagonalley_renderer -from ...core.crud import get_wallet from .crud import ( get_diagonalley_market, get_diagonalley_market_stalls, + get_diagonalley_order_details, get_diagonalley_products, get_diagonalley_stall, get_diagonalley_zone, @@ -31,6 +32,22 @@ async def index(request: Request, user: User = Depends(check_user_exists)): "diagonalley/index.html", {"request": request, "user": user.dict()} ) +@diagonalley_ext.get("/chat", response_class=HTMLResponse) +async def chat_page(request: Request, merch: str = Query(...), order: str = Query(...)): + stall = await get_diagonalley_stall(merch) + orders = await get_diagonalley_order_details(order) + + logger.debug(f"ORDER: {orders}") + + return diagonalley_renderer().TemplateResponse( + "diagonalley/chat.html", + { + "request": request, + "stall": {"id": stall.id, "name": stall.name, "publickey": stall.publickey, "wallet": stall.wallet }, + "orders": [details.dict() for details in orders] + }, + ) + @diagonalley_ext.get("/{stall_id}", response_class=HTMLResponse) async def display(request: Request, stall_id): @@ -85,3 +102,31 @@ async def display(request: Request, market_id): "products": products, }, ) + +##################WEBSOCKET ROUTES######################## + +class ConnectionManager: + def __init__(self): + self.active_connections: List[WebSocket] = [] + + async def connect(self, websocket: WebSocket, order_id: str): + await websocket.accept() + websocket.id = order_id + self.active_connections.append(websocket) + + def disconnect(self, websocket: WebSocket): + self.active_connections.remove(websocket) + + async def send_personal_message(self, message: str, order_id: str): + for connection in self.active_connections: + if connection.id == order_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() + + From b9371805e2eb0038b439a76589d26beca6c507ef Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Wed, 28 Sep 2022 17:02:15 +0100 Subject: [PATCH 24/96] websockets working localstorage for review --- lnbits/extensions/diagonalley/notifier.py | 83 ++++++++++ .../templates/diagonalley/chat.html | 145 ++++++++++++++---- .../templates/diagonalley/index.html | 2 +- lnbits/extensions/diagonalley/views.py | 85 +++++----- lnbits/extensions/diagonalley/views_api.py | 5 +- 5 files changed, 254 insertions(+), 66 deletions(-) create mode 100644 lnbits/extensions/diagonalley/notifier.py diff --git a/lnbits/extensions/diagonalley/notifier.py b/lnbits/extensions/diagonalley/notifier.py new file mode 100644 index 00000000..58a9f2bb --- /dev/null +++ b/lnbits/extensions/diagonalley/notifier.py @@ -0,0 +1,83 @@ +## adapted from https://github.com/Sentymental/chat-fastapi-websocket +""" +Create a class Notifier that will handle messages +and delivery to the specific person +""" + +from collections import defaultdict + +from fastapi import WebSocket +from loguru import logger + + +class Notifier: + """ + Manages chatrooms, sessions and members. + + Methods: + - get_notification_generator(self): async generator with notification messages + - get_members(self, room_name: str): get members in room + - push(message: str, room_name: str): push message + - connect(websocket: WebSocket, room_name: str): connect to room + - remove(websocket: WebSocket, room_name: str): remove + - _notify(message: str, room_name: str): notifier + """ + + def __init__(self): + # Create sessions as a dict: + self.sessions: dict = defaultdict(dict) + + # Create notification generator: + self.generator = self.get_notification_generator() + + async def get_notification_generator(self): + """Notification Generator""" + + while True: + message = yield + msg = message["message"] + room_name = message["room_name"] + await self._notify(msg, room_name) + + def get_members(self, room_name: str): + """Get all members in a room""" + + try: + logger.info(f"Looking for members in room: {room_name}") + return self.sessions[room_name] + + except Exception: + logger.exception(f"There is no member in room: {room_name}") + return None + + async def push(self, message: str, room_name: str = None): + """Push a message""" + + message_body = {"message": message, "room_name": room_name} + await self.generator.asend(message_body) + + async def connect(self, websocket: WebSocket, room_name: str): + """Connect to room""" + + await websocket.accept() + if self.sessions[room_name] == {} or len(self.sessions[room_name]) == 0: + self.sessions[room_name] = [] + + self.sessions[room_name].append(websocket) + print(f"Connections ...: {self.sessions[room_name]}") + + def remove(self, websocket: WebSocket, room_name: str): + """Remove websocket from room""" + + self.sessions[room_name].remove(websocket) + print(f"Connection removed...\nOpen connections...: {self.sessions[room_name]}") + + async def _notify(self, message: str, room_name: str): + """Notifier""" + + remaining_sessions = [] + while len(self.sessions[room_name]) > 0: + websocket = self.sessions[room_name].pop() + await websocket.send_text(message) + remaining_sessions.append(websocket) + self.sessions[room_name] = remaining_sessions diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/chat.html b/lnbits/extensions/diagonalley/templates/diagonalley/chat.html index bb3e607f..1713c9e2 100644 --- a/lnbits/extensions/diagonalley/templates/diagonalley/chat.html +++ b/lnbits/extensions/diagonalley/templates/diagonalley/chat.html @@ -45,13 +45,15 @@ {% raw %}
{{ stall.name }}
-

+

Public Key: {{ sliceKey(stall.publickey) }} - {{ stall.publickey }} + Click to copy

{% endraw %} +
+ - -
+
-->
@@ -79,16 +81,26 @@ Lorem ipsum dolor, sit amet consectetur adipisicing elit. Dolore, quasi.

-
- - - - - +
+
+
+ {% raw %} + + + {{ user.keys[type] }} + +

{{ type == 'publickey' ? 'Public Key' : 'Private Key' }}

+ {% endraw %} +
+

Lorem ipsum dolor, sit amet consectetur adipisicing elit. Dolore, @@ -123,7 +135,9 @@ showMessages: false, messages: {}, stall: null, + selectedOrder: null, orders: [], + user: null, // Mock data model: null, mockMerch: ['Google', 'Facebook', 'Twitter', 'Apple', 'Oracle'], @@ -136,29 +150,108 @@ this.newMessage = '' this.$refs.newMessage.focus() }, - sendMessage() { + sendMessage(e) { let message = { key: Date.now(), text: this.newMessage, from: 'me' } - this.$set(this.messages, message.key, message) + ws.send(JSON.stringify(message)) this.clearMessage() + e.preventDefault() }, sliceKey(key) { if (!key) return '' return `${key.slice(0, 4)}...${key.slice(-4)}` + }, + async generateKeys() { + await LNbits.api + .request('GET', '/diagonalley/api/v1/keys', null) + .then(response => { + if (response.data) { + let data = { + keys: { + privatekey: response.data.privkey, + publickey: response.data.pubkey + } + } + this.user = data + console.log('Keys done', this.user) + return + } + }) + .catch(function (error) { + LNbits.utils.notifyApiError(error) + }) + }, + changeOrder() { + console.log(this.selectedOrder) + }, + startChat(room_name) { + if (location.protocol == 'https:') { + ws_scheme = 'wss://' + } else { + ws_scheme = 'ws://' + } + ws = new WebSocket( + ws_scheme + location.host + '/diagonalley/ws/' + room_name + ) + + function checkWebSocket(event) { + if (ws.readyState === WebSocket.CLOSED) { + console.log('WebSocket CLOSED: Reopening') + ws = new WebSocket( + ws_scheme + location.host + '/diagonalley/ws/' + room_name + ) + } + } + + ws.onmessage = event => { + let event_data = JSON.parse(event.data) + + this.$set(this.messages, event_data.key, event_data) + this.$q.localStorage.set() + } + + this.ws = ws } }, - created() { - let stall = JSON.parse('{{ stall | tojson }}') - let orders = JSON.parse('{{ orders | tojson }}') + async created() { + this.stall = JSON.parse('{{ stall | tojson }}') - this.stall = stall - this.orders = orders - console.log(stall) - console.log(orders) + let order_details = JSON.parse('{{ order | tojson }}') + let order_id = order_details[0].order_id + + let data = this.$q.localStorage.getItem(`lnbits.diagonalley.data`) + + try { + if (data) { + this.user = data + //add chat key (merchant pubkey) if not set + if (!this.user.chats[`${this.stall.publickey}`]) { + this.$set(this.user.chats, this.stall.publickey, []) + } + //this.$q.localStorage.set(`lnbits.diagonalley.data`, this.user) + } else { + // generate keys + await this.generateKeys() + // populate user data + this.user.chats = { + [`${this.stall.publickey}`]: [] + } + this.user.orders = [] + } + + this.order_details = order_details + this.user.orders = [...new Set([...this.user.orders, order_id])] + this.selectedOrder = order_id + + this.$q.localStorage.set(`lnbits.diagonalley.data`, this.user) + this.startChat(order_id) + } catch (e) { + console.error(e) + } } }) diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/index.html b/lnbits/extensions/diagonalley/templates/diagonalley/index.html index cd03e6f3..5ad6f6a5 100644 --- a/lnbits/extensions/diagonalley/templates/diagonalley/index.html +++ b/lnbits/extensions/diagonalley/templates/diagonalley/index.html @@ -644,7 +644,7 @@ icon="storefront" :color="($q.dark.isActive) ? 'grey-7' : 'grey-5'" type="a" - :href="'/diagonalley/' + props.row.id" + :href="'/diagonalley/stalls/' + props.row.id" target="_blank" > Link to pass to stall relay diff --git a/lnbits/extensions/diagonalley/views.py b/lnbits/extensions/diagonalley/views.py index d6e701a0..a378658a 100644 --- a/lnbits/extensions/diagonalley/views.py +++ b/lnbits/extensions/diagonalley/views.py @@ -1,7 +1,8 @@ +import json from http import HTTPStatus from typing import List -from fastapi import Query, Request, WebSocket, WebSocketDisconnect +from fastapi import BackgroundTasks, Query, Request, WebSocket, WebSocketDisconnect from fastapi.params import Depends from fastapi.templating import Jinja2Templates from loguru import logger @@ -11,6 +12,7 @@ from starlette.responses import HTMLResponse from lnbits.core.models import User from lnbits.decorators import check_user_exists # type: ignore from lnbits.extensions.diagonalley import diagonalley_ext, diagonalley_renderer +from lnbits.extensions.diagonalley.notifier import Notifier from .crud import ( get_diagonalley_market, @@ -32,24 +34,8 @@ async def index(request: Request, user: User = Depends(check_user_exists)): "diagonalley/index.html", {"request": request, "user": user.dict()} ) -@diagonalley_ext.get("/chat", response_class=HTMLResponse) -async def chat_page(request: Request, merch: str = Query(...), order: str = Query(...)): - stall = await get_diagonalley_stall(merch) - orders = await get_diagonalley_order_details(order) - logger.debug(f"ORDER: {orders}") - - return diagonalley_renderer().TemplateResponse( - "diagonalley/chat.html", - { - "request": request, - "stall": {"id": stall.id, "name": stall.name, "publickey": stall.publickey, "wallet": stall.wallet }, - "orders": [details.dict() for details in orders] - }, - ) - - -@diagonalley_ext.get("/{stall_id}", response_class=HTMLResponse) +@diagonalley_ext.get("/stalls/{stall_id}", response_class=HTMLResponse) async def display(request: Request, stall_id): stall = await get_diagonalley_stall(stall_id) products = await get_diagonalley_products(stall_id) @@ -103,30 +89,55 @@ async def display(request: Request, market_id): }, ) + +@diagonalley_ext.get("/chat", response_class=HTMLResponse) +async def chat_page(request: Request, merch: str = Query(...), order: str = Query(...)): + stall = await get_diagonalley_stall(merch) + _order = await get_diagonalley_order_details(order) + + return diagonalley_renderer().TemplateResponse( + "diagonalley/chat.html", + { + "request": request, + "stall": { + "id": stall.id, + "name": stall.name, + "publickey": stall.publickey, + "wallet": stall.wallet, + }, + "order": [details.dict() for details in _order], + }, + ) + + ##################WEBSOCKET ROUTES######################## -class ConnectionManager: - def __init__(self): - self.active_connections: List[WebSocket] = [] +# Initialize Notifier: +notifier = Notifier() - async def connect(self, websocket: WebSocket, order_id: str): - await websocket.accept() - websocket.id = order_id - self.active_connections.append(websocket) +@diagonalley_ext.websocket("/ws/{room_name}") +async def websocket_endpoint( + websocket: WebSocket, room_name: str, background_tasks: BackgroundTasks +): + await notifier.connect(websocket, room_name) + try: + while True: + data = await websocket.receive_text() + d = json.loads(data) + d["room_name"] = room_name - def disconnect(self, websocket: WebSocket): - self.active_connections.remove(websocket) + room_members = ( + notifier.get_members(room_name) + if notifier.get_members(room_name) is not None + else [] + ) - async def send_personal_message(self, message: str, order_id: str): - for connection in self.active_connections: - if connection.id == order_id: - await connection.send_text(message) + if websocket not in room_members: + print("Sender not in room member: Reconnecting...") + await notifier.connect(websocket, room_name) - async def broadcast(self, message: str): - for connection in self.active_connections: - await connection.send_text(message) - - -manager = ConnectionManager() + await notifier._notify(f"{data}", room_name) + except WebSocketDisconnect: + notifier.remove(websocket, room_name) diff --git a/lnbits/extensions/diagonalley/views_api.py b/lnbits/extensions/diagonalley/views_api.py index c3615c90..aa8d338d 100644 --- a/lnbits/extensions/diagonalley/views_api.py +++ b/lnbits/extensions/diagonalley/views_api.py @@ -469,14 +469,15 @@ async def api_diagonalley_stall_create( return market.dict() + ## KEYS + @diagonalley_ext.get("/api/v1/keys") -async def api_diagonalley_generate_keys(wallet: WalletTypeInfo = Depends(require_admin_key)): +async def api_diagonalley_generate_keys(): private_key = PrivateKey() public_key = private_key.pubkey.serialize().hex() while not public_key.startswith("02"): private_key = PrivateKey() public_key = private_key.pubkey.serialize().hex() return {"privkey": private_key.serialize(), "pubkey": public_key[2:]} - From 8ab9590594fc55bfc0a4bf9d3a7a799554c2ad83 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Wed, 28 Sep 2022 17:03:09 +0100 Subject: [PATCH 25/96] format --- lnbits/extensions/diagonalley/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/extensions/diagonalley/views.py b/lnbits/extensions/diagonalley/views.py index a378658a..7b242aed 100644 --- a/lnbits/extensions/diagonalley/views.py +++ b/lnbits/extensions/diagonalley/views.py @@ -115,6 +115,7 @@ async def chat_page(request: Request, merch: str = Query(...), order: str = Quer # Initialize Notifier: notifier = Notifier() + @diagonalley_ext.websocket("/ws/{room_name}") async def websocket_endpoint( websocket: WebSocket, room_name: str, background_tasks: BackgroundTasks @@ -140,4 +141,3 @@ async def websocket_endpoint( except WebSocketDisconnect: notifier.remove(websocket, room_name) - From 22b0ed049622aad934d8cdb8ae9bbb507744d317 Mon Sep 17 00:00:00 2001 From: ben Date: Thu, 29 Sep 2022 10:31:10 +0100 Subject: [PATCH 26/96] 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 2b058754..e77e301d 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 27/96] 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 e77e301d..21ee307a 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 28/96] 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 21ee307a..d79363af 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 29/96] 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 d79363af..7f2aefdc 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 30/96] 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 7f2aefdc..87679ed5 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 31/96] 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 041b9f32..98ae497e 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 32/96] 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 f769b44f..1319fa1f 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 33/96] 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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 delta 3684 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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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 Date: Fri, 30 Sep 2022 10:16:34 +0100 Subject: [PATCH 34/96] chat messages stored in db --- lnbits/extensions/diagonalley/crud.py | 15 +++++++++++ lnbits/extensions/diagonalley/migrations.py | 24 +++++++++++++++++ lnbits/extensions/diagonalley/models.py | 13 +++++++++ lnbits/extensions/diagonalley/notifier.py | 10 +++++++ .../templates/diagonalley/chat.html | 27 ++++++++++--------- lnbits/extensions/diagonalley/views.py | 6 +++-- 6 files changed, 80 insertions(+), 15 deletions(-) diff --git a/lnbits/extensions/diagonalley/crud.py b/lnbits/extensions/diagonalley/crud.py index 8f053de1..9e3f2013 100644 --- a/lnbits/extensions/diagonalley/crud.py +++ b/lnbits/extensions/diagonalley/crud.py @@ -9,6 +9,7 @@ from lnbits.settings import WALLET from . import db from .models import ( + CreateChatMessage, CreateMarket, CreateMarketStalls, Market, @@ -405,3 +406,17 @@ async def create_diagonalley_market_stalls( async def update_diagonalley_market(market_id): pass + +async def create_chat_message(data: CreateChatMessage): + print("DATA", data) + await db.execute( + """ + INSERT INTO diagonalley.messages (msg, pubkey, id_conversation) + VALUES (?, ?, ?) + """, + ( + data.msg, + data.pubkey, + data.room_name, + ), + ) diff --git a/lnbits/extensions/diagonalley/migrations.py b/lnbits/extensions/diagonalley/migrations.py index 6e1510a7..3bcddcb0 100644 --- a/lnbits/extensions/diagonalley/migrations.py +++ b/lnbits/extensions/diagonalley/migrations.py @@ -113,3 +113,27 @@ async def m001_initial(db): ); """ ) + +async def m002_add_chat_messages(db): + """ + Initial chat messages table. + """ + await db.execute( + f""" + CREATE TABLE diagonalley.messages ( + id {db.serial_primary_key}, + msg TEXT NOT NULL, + pubkey TEXT NOT NULL, + id_conversation TEXT NOT NULL, + timestamp TIMESTAMP NOT NULL DEFAULT """ + + db.timestamp_now + + """ + ); + """ + ) + + """ + Create indexes for message fetching + """ + await db.execute("CREATE INDEX idx_messages_timestamp ON diagonalley.messages (timestamp DESC)") + await db.execute("CREATE INDEX idx_messages_conversations ON diagonalley.messages (id_conversation)") diff --git a/lnbits/extensions/diagonalley/models.py b/lnbits/extensions/diagonalley/models.py index c3234bb6..f9fa1bca 100644 --- a/lnbits/extensions/diagonalley/models.py +++ b/lnbits/extensions/diagonalley/models.py @@ -107,3 +107,16 @@ class Market(BaseModel): class CreateMarketStalls(BaseModel): stallid: str + + +class ChatMessage(BaseModel): + id: str + msg: str + pubkey: str + id_conversation: str + timestamp: int + +class CreateChatMessage(BaseModel): + msg: str = Query(..., min_length=1) + pubkey: str = Query(...) + room_name: str = Query(...) diff --git a/lnbits/extensions/diagonalley/notifier.py b/lnbits/extensions/diagonalley/notifier.py index 58a9f2bb..e21be500 100644 --- a/lnbits/extensions/diagonalley/notifier.py +++ b/lnbits/extensions/diagonalley/notifier.py @@ -4,11 +4,15 @@ Create a class Notifier that will handle messages and delivery to the specific person """ +import json from collections import defaultdict from fastapi import WebSocket from loguru import logger +from lnbits.extensions.diagonalley.crud import create_chat_message +from lnbits.extensions.diagonalley.models import CreateChatMessage + class Notifier: """ @@ -75,6 +79,12 @@ class Notifier: async def _notify(self, message: str, room_name: str): """Notifier""" + d = json.loads(message) + d["room_name"] = room_name + db_msg = CreateChatMessage.parse_obj(d) + print("NOT:", db_msg) + await create_chat_message(data=db_msg) + remaining_sessions = [] while len(self.sessions[room_name]) > 0: websocket = self.sessions[room_name].pop() diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/chat.html b/lnbits/extensions/diagonalley/templates/diagonalley/chat.html index 1713c9e2..21f59361 100644 --- a/lnbits/extensions/diagonalley/templates/diagonalley/chat.html +++ b/lnbits/extensions/diagonalley/templates/diagonalley/chat.html @@ -7,12 +7,12 @@
@@ -125,6 +125,9 @@ {% endblock %} {% block scripts %} + +{% endblock %} From d5388ba7dee48554fd9ba1f09fa8e887f0477187 Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 4 Oct 2022 09:59:18 +0100 Subject: [PATCH 44/96] add publickey if stored --- .../templates/diagonalley/stall.html | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/stall.html b/lnbits/extensions/diagonalley/templates/diagonalley/stall.html index 9396d663..a3a04b1e 100644 --- a/lnbits/extensions/diagonalley/templates/diagonalley/stall.html +++ b/lnbits/extensions/diagonalley/templates/diagonalley/stall.html @@ -162,6 +162,17 @@ v-model.trim="checkoutDialog.data.username" label="Name *optional" > + + + Cancel Date: Tue, 4 Oct 2022 09:59:34 +0100 Subject: [PATCH 45/96] placeholder for product page WIP --- .../diagonalley/templates/diagonalley/product.html | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lnbits/extensions/diagonalley/templates/diagonalley/product.html diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/product.html b/lnbits/extensions/diagonalley/templates/diagonalley/product.html new file mode 100644 index 00000000..66f56691 --- /dev/null +++ b/lnbits/extensions/diagonalley/templates/diagonalley/product.html @@ -0,0 +1,14 @@ +{% extends "public.html" %} {% block page %} +

Product page

+{% endblock %} {% block scripts %} + +{% endblock %} From c59e9efabc70735c1d9825557b70b254cbb6f5fa Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 4 Oct 2022 10:00:42 +0100 Subject: [PATCH 46/96] add public key and refactor endpoint --- lnbits/extensions/diagonalley/crud.py | 1 + lnbits/extensions/diagonalley/models.py | 1 + .../templates/diagonalley/chat.html | 317 ------------------ lnbits/extensions/diagonalley/views.py | 13 +- 4 files changed, 11 insertions(+), 321 deletions(-) delete mode 100644 lnbits/extensions/diagonalley/templates/diagonalley/chat.html diff --git a/lnbits/extensions/diagonalley/crud.py b/lnbits/extensions/diagonalley/crud.py index 18207888..f093ba69 100644 --- a/lnbits/extensions/diagonalley/crud.py +++ b/lnbits/extensions/diagonalley/crud.py @@ -408,6 +408,7 @@ async def create_diagonalley_market_stalls( async def update_diagonalley_market(market_id): pass +### CHAT / MESSAGES async def create_chat_message(data: CreateChatMessage): print("DATA", data) diff --git a/lnbits/extensions/diagonalley/models.py b/lnbits/extensions/diagonalley/models.py index b1bfdc9c..e3ea2cc6 100644 --- a/lnbits/extensions/diagonalley/models.py +++ b/lnbits/extensions/diagonalley/models.py @@ -70,6 +70,7 @@ class createOrderDetails(BaseModel): class createOrder(BaseModel): wallet: str = Query(...) + username: str = Query(None) pubkey: str = Query(None) shippingzone: str = Query(...) address: str = Query(...) diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/chat.html b/lnbits/extensions/diagonalley/templates/diagonalley/chat.html deleted file mode 100644 index adbdb047..00000000 --- a/lnbits/extensions/diagonalley/templates/diagonalley/chat.html +++ /dev/null @@ -1,317 +0,0 @@ -{% extends "public.html" %} {% block page %} -
-
- -
-
- -
- -
-
- - - - - -
-
-
-
- - - {% raw %} -
{{ stall.name }}
-

- Public Key: {{ sliceKey(stall.publickey) }} - Click to copy -

- {% endraw %} -
- - - - - - - - -

- Lorem ipsum dolor, sit amet consectetur adipisicing elit. Dolore, - quasi. -

-
-
-
- {% raw %} - - - {{ user.keys[type] }} - -

{{ type == 'publickey' ? 'Public Key' : 'Private Key' }}

- {% endraw %} -
-
-
-

- Lorem ipsum dolor, sit amet consectetur adipisicing elit. Dolore, - quasi. -

-
- - -
- Backup keys - Download your keys - - Delete data - Delete all data from browser - -
-
-
-
-
-{% endblock %} {% block scripts %} - - -{% endblock %} diff --git a/lnbits/extensions/diagonalley/views.py b/lnbits/extensions/diagonalley/views.py index 1addb8a7..27875287 100644 --- a/lnbits/extensions/diagonalley/views.py +++ b/lnbits/extensions/diagonalley/views.py @@ -20,6 +20,7 @@ from .crud import ( get_diagonalley_market, get_diagonalley_market_stalls, get_diagonalley_order_details, + get_diagonalley_order_invoiceid, get_diagonalley_products, get_diagonalley_stall, get_diagonalley_zone, @@ -92,13 +93,15 @@ async def display(request: Request, market_id): ) -@diagonalley_ext.get("/chat", response_class=HTMLResponse) -async def chat_page(request: Request, merch: str = Query(...), order: str = Query(...)): +@diagonalley_ext.get("/order", response_class=HTMLResponse) +async def chat_page(request: Request, merch: str = Query(...), invoice_id: str = Query(...)): stall = await get_diagonalley_stall(merch) - _order = await get_diagonalley_order_details(order) + order = await get_diagonalley_order_invoiceid(invoice_id) + _order = await get_diagonalley_order_details(order.id) + products = await get_diagonalley_products(stall.id) return diagonalley_renderer().TemplateResponse( - "diagonalley/chat.html", + "diagonalley/order.html", { "request": request, "stall": { @@ -107,7 +110,9 @@ async def chat_page(request: Request, merch: str = Query(...), order: str = Quer "publickey": stall.publickey, "wallet": stall.wallet, }, + "order_id": order.id, "order": [details.dict() for details in _order], + "products": [product.dict() for product in products] }, ) 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 47/96] 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 87679ed5..5e9fdb28 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 48/96] fix poetry lock in main --- poetry.lock | 152 ++++++++++++++++++++++++++-------------------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/poetry.lock b/poetry.lock index 343fffbf..5b283d75 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 49/96] 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 876c8b8a..bbe95983 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 e3d0fd35..21c7fb38 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 d80da678..96b574d2 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 2d7aae6b..f0adb3ac 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 5d368fbb..c7b6e44b 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 50/96] 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 5e9fdb28..37104fde 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 51/96] 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 37104fde..6b95f93b 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 52/96] 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 df0cd4b8..ec7f3b48 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 53/96] 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 b7cf1750..cfc6c226 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 54/96] 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 320d34da..62adc5e5 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 55/96] 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 680c5e6d..3b8d0b2d 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 56/96] 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 62adc5e5..852f3860 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 57/96] 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 54849c95..dc4456b4 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 ec7f3b48..a2bc0dd4 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 c7899282..65913b02 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 fef0aec1..cf617141 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 00000000..2337c0ad --- /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 3389e17c..4a28a5c1 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 58/96] 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 00000000..34cf940d --- /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 59/96] 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 34cf940d..0638e4f2 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 60/96] 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 bbe95983..876c8b8a 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 21c7fb38..e3d0fd35 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 96b574d2..d80da678 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 f0adb3ac..2d7aae6b 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 c7b6e44b..5d368fbb 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 61/96] 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 dc4456b4..5452e356 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 cf617141..0714c656 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 7f9afa27..7497714e 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 4a28a5c1..a64de84b 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 62/96] 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 51482538..8b9cf798 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 63/96] 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 528e5afe..b55c83f7 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 a64de84b..26f581d3 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 d152d210..4a4c2b90 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 64/96] 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 66981784..f52b0391 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 08126013..9609e0c3 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 e4026dd0..925322ec 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 c47a954a..74a0fdba 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 c7899282..da62cb7e 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 6af04791..7a45e495 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 1b0cea37..7d50e8f1 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 6b58fbca..d8f6da3f 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 65/96] 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 45166521..4c25e4cb 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 a2bc0dd4..3823005e 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 2337c0ad..4bf0d3b9 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 b55c83f7..ccb0a6e9 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 66/96] 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 5452e356..d987e6cc 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 3823005e..79892b78 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 65913b02..fa56b3d1 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 0714c656..01bcc2ba 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 4bf0d3b9..2f048138 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 26f581d3..752d2c81 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 65625cbb..c034f66e 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 67/96] 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 2f048138..db8d87f0 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 752d2c81..5c6eba24 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 68/96] 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 db8d87f0..c8f3db04 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 69/96] 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 d987e6cc..c27c9e17 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 70/96] 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 7497714e..b239e981 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 71/96] 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 7d6de35f..987c6ca6 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 c27c9e17..d2010c44 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 b239e981..f93d44d8 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 ccb0a6e9..028dd94b 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 41949652..fa533566 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 00000000..54220c85 --- /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 72/96] 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 cbed6292..bb1ca0c1 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 73/96] 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 ace94557..d1ace04b 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 ebe8d8b0b983eb16257ca66b0d3c1720a0816980 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Wed, 19 Oct 2022 22:07:38 +0100 Subject: [PATCH 74/96] just for bragging... --- lnbits/extensions/diagonalley/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/extensions/diagonalley/config.json b/lnbits/extensions/diagonalley/config.json index 8ad41727..194949d0 100644 --- a/lnbits/extensions/diagonalley/config.json +++ b/lnbits/extensions/diagonalley/config.json @@ -2,5 +2,5 @@ "name": "Diagon Alley", "short_description": "Nostr shop system", "icon": "add_shopping_cart", - "contributors": ["benarc"] + "contributors": ["benarc", "talvasconcelos"] } From 532e12ec831b1a5908081edb5f32e95945e23023 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Wed, 19 Oct 2022 22:08:48 +0100 Subject: [PATCH 75/96] merchant chat functional almost out of WIP --- lnbits/extensions/diagonalley/crud.py | 13 + lnbits/extensions/diagonalley/notifier.py | 2 +- .../templates/diagonalley/index.html | 389 +++++++++++++++--- .../templates/diagonalley/order.html | 52 +-- .../templates/diagonalley/stall.html | 16 +- lnbits/extensions/diagonalley/views.py | 45 +- lnbits/extensions/diagonalley/views_api.py | 23 +- 7 files changed, 461 insertions(+), 79 deletions(-) diff --git a/lnbits/extensions/diagonalley/crud.py b/lnbits/extensions/diagonalley/crud.py index f093ba69..8dadb739 100644 --- a/lnbits/extensions/diagonalley/crud.py +++ b/lnbits/extensions/diagonalley/crud.py @@ -408,8 +408,10 @@ async def create_diagonalley_market_stalls( async def update_diagonalley_market(market_id): pass + ### CHAT / MESSAGES + async def create_chat_message(data: CreateChatMessage): print("DATA", data) await db.execute( @@ -441,3 +443,14 @@ async def get_diagonalley_chat_messages(room_name: str): ) return [ChatMessage(**row) for row in rows] + + +async def get_diagonalley_chat_by_merchant(ids: List[str]) -> List[ChatMessage]: + + q = ",".join(["?"] * len(ids)) + rows = await db.fetchall( + f"SELECT * FROM diagonalley.messages WHERE id_conversation IN ({q})", + (*ids,), + ) + print(ids, q, rows) + return [ChatMessage(**row) for row in rows] diff --git a/lnbits/extensions/diagonalley/notifier.py b/lnbits/extensions/diagonalley/notifier.py index e21be500..08badfc7 100644 --- a/lnbits/extensions/diagonalley/notifier.py +++ b/lnbits/extensions/diagonalley/notifier.py @@ -78,9 +78,9 @@ class Notifier: async def _notify(self, message: str, room_name: str): """Notifier""" - d = json.loads(message) d["room_name"] = room_name + print("hey", d) db_msg = CreateChatMessage.parse_obj(d) print("NOT:", db_msg) await create_chat_message(data=db_msg) diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/index.html b/lnbits/extensions/diagonalley/templates/diagonalley/index.html index 5ad6f6a5..f898fd40 100644 --- a/lnbits/extensions/diagonalley/templates/diagonalley/index.html +++ b/lnbits/extensions/diagonalley/templates/diagonalley/index.html @@ -345,20 +345,6 @@
- + Product List a product - + Product List a product + Shipping Zone Create a shipping zone @@ -382,7 +368,26 @@ Create a market stall to list products on - + Product List a product + + Product List a product + Create Market Makes a simple frontend shop for your stalls (not NOSTR) + {{ col.label }} @@ -434,6 +440,23 @@ :icon="props.expand ? 'remove' : 'add'" /> + + + + + {{ col.value }} @@ -824,22 +847,114 @@ {% include "diagonalley/_api_docs.html" %} +
Messages
- -
-
+ + + + +
+
+ +
+ +
+
+ + + + + +
+ +
- + {% endblock %} {% block scripts %} {{ window_vars(user) }} @@ -873,6 +987,15 @@ const pica = window.pica() + function imgSizeFit(img, maxWidth = 1024, maxHeight = 768) { + let ratio = Math.min( + 1, + maxWidth / img.naturalWidth, + maxHeight / img.naturalHeight + ) + return {width: img.naturalWidth * ratio, height: img.naturalHeight * ratio} + } + const mapStalls = obj => { obj._data = _.clone(obj) return obj @@ -891,6 +1014,7 @@ new Date(obj.time * 1000), 'YYYY-MM-DD HH:mm' ) + // obj.unread = false return obj } const mapKeys = obj => { @@ -933,8 +1057,12 @@ customerKeys: [], customerKey: '', customerMessages: {}, + messages: {}, + newMessage: '', + orderMessages: {}, shippedModel: false, shippingZoneOptions: [ + 'Free (digital)', 'Worldwide', 'Europe', 'Australia', @@ -999,17 +1127,17 @@ ordersTable: { columns: [ /*{ - name: 'product', - align: 'left', - label: 'Product', - field: 'product' - }, - { - name: 'quantity', - align: 'left', - label: 'Quantity', - field: 'quantity' - },*/ + name: 'product', + align: 'left', + label: 'Product', + field: 'product' + }, + { + name: 'quantity', + align: 'left', + label: 'Quantity', + field: 'quantity' + },*/ { name: 'id', align: 'left', @@ -1443,9 +1571,10 @@ let image = new Image() image.src = blobURL image.onload = async () => { + let fit = imgSizeFit(image) let canvas = document.createElement('canvas') - canvas.setAttribute('width', 760) - canvas.setAttribute('height', 490) + canvas.setAttribute('width', fit.width) + canvas.setAttribute('height', fit.height) await pica.resize(image, canvas, { quality: 0, alpha: true, @@ -1657,7 +1786,7 @@ .then(response => { if (response.data) { this.markets = response.data.map(mapMarkets) - console.log(this.markets) + // console.log(this.markets) } }) .catch(error => { @@ -1756,10 +1885,10 @@ //////////////////////////////////////// ////////////////ORDERS////////////////// //////////////////////////////////////// - getOrders: function () { + getOrders: async function () { var self = this - LNbits.api + await LNbits.api .request( 'GET', '/diagonalley/api/v1/orders?all_wallets=true', @@ -1768,7 +1897,6 @@ .then(function (response) { if (response.data) { self.orders = response.data.map(mapOrders) - console.log(self.orders) } }) .catch(function (error) { @@ -1839,21 +1967,190 @@ }, exportOrdersCSV: function () { LNbits.utils.exportCSV(this.ordersTable.columns, this.orders) + }, + /// CHAT + async getAllMessages() { + await LNbits.api + .request( + 'GET', + `/diagonalley/api/v1/chat/messages/merchant?orders=${this.orders + .map(o => o.invoiceid) + .toString()}`, + this.g.user.wallets[0].adminkey + ) + .then(res => { + this.messages = _.groupBy(res.data, 'id_conversation') + this.checkUnreadMessages() + console.log('Get new messages!') + }) + .catch(error => { + LNbits.utils.notifyApiError(error) + }) + }, + updateLastSeenMsg(id) { + let data = this.$q.localStorage.getItem( + `lnbits.diagonalley.${this.g.user.id}` + ) + let chat = { + ...data.chat, + [`${id}`]: { + timestamp: Object.keys(this.orderMessages)[ + Object.keys(this.orderMessages).length - 1 + ] + } + } + console.log({chat}) + this.$q.localStorage.set(`lnbits.diagonalley.${this.g.user.id}`, { + ...data, + chat + }) + this.checkUnreadMessages() + }, + checkUnreadMessages() { + let lastMsgs = this.$q.localStorage.getItem( + `lnbits.diagonalley.${this.g.user.id}` + ).chat + for (let key in this.messages) { + let idx = this.orders.findIndex(f => f.invoiceid == key) + if (!lastMsgs[key]) { + this.updateLastSeenMsg(key) + //this.orders[idx].unread = true + return + } + console.log( + 'Key', + key, + 'saved:', + lastMsgs[key].timestamp, + 'messages: ', + Math.max(...this.messages[key].map(c => c.timestamp)), + lastMsgs[key].timestamp < + Math.max(...this.messages[key].map(c => c.timestamp)) + ) + if ( + lastMsgs[key].timestamp < + Math.max(...this.messages[key].map(c => c.timestamp)) + ) { + this.$set(this.orders[idx], 'unread', true) + // this.orders[idx].unread = true + } else { + this.$set(this.orders[idx], 'unread', false) + // this.orders[idx].unread = false + } + console.log('Order:', this.orders[idx]) + } + }, + clearMessage() { + this.newMessage = '' + this.$refs.newMessage.focus() + }, + sendMessage() { + let message = { + msg: this.newMessage, + pubkey: this.keys.pubkey + } + this.ws.send(JSON.stringify(message)) + + this.clearMessage() + }, + chatRoom(id) { + this.startChat(id) + this.orderMessages = {} + this.messages[id].map(m => { + this.$set(this.orderMessages, m.timestamp, { + msg: m.msg, + pubkey: m.pubkey + }) + }) + this.$refs.chatCard.scrollIntoView({ + behavior: 'smooth', + inline: 'nearest' + }) + this.updateLastSeenMsg(id) + //"ea2fbf6c91aa228603681e2cc34bb06e34e6d1375fa4d6c35756182b2fa3307f" + //"c7435a04875c26e28db91a377bd6e991dbfefeefea8258415f3ae0c716ed2335" + }, + startChat(room_name) { + if (this.ws) { + this.ws.close() + } + if (location.protocol == 'https:') { + ws_scheme = 'wss://' + } else { + ws_scheme = 'ws://' + } + ws = new WebSocket( + ws_scheme + location.host + '/diagonalley/ws/' + room_name + ) + + function checkWebSocket(event) { + if (ws.readyState === WebSocket.CLOSED) { + console.log('WebSocket CLOSED: Reopening') + ws = new WebSocket( + ws_scheme + location.host + '/diagonalley/ws/' + room_name + ) + } + } + + ws.onmessage = event => { + let event_data = JSON.parse(event.data) + + this.$set(this.orderMessages, Date.now(), event_data) + this.updateLastSeenMsg(room_name) + } + + ws.onclose = event => { + this.updateLastSeenMsg(room_name) + } + + this.ws = ws } }, - created: function () { + async created() { if (this.g.user.wallets.length) { this.getStalls() this.getProducts() this.getZones() - this.getOrders() + await this.getOrders() this.getMarkets() - this.customerKeys = [ - 'cb4c0164fe03fcdadcbfb4f76611c71620790944c24f21a1cd119395cdedfe1b', - 'a9c17358a6dc4ceb3bb4d883eb87967a66b3453a0f3199f0b1c8eef8070c6a07' - ] + await this.getAllMessages() + let keys = this.$q.localStorage.getItem( + `lnbits.diagonalley.${this.g.user.id}` + ) + if (keys) { + this.keys = keys + } + setInterval(() => { + this.getAllMessages() + }, 300000) } } }) + {% endblock %} diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/order.html b/lnbits/extensions/diagonalley/templates/diagonalley/order.html index aa9d0de4..81cdfc36 100644 --- a/lnbits/extensions/diagonalley/templates/diagonalley/order.html +++ b/lnbits/extensions/diagonalley/templates/diagonalley/order.html @@ -68,7 +68,7 @@ dense emit-value v-model="selectedOrder" - :options="user.orders" + :options="Object.keys(user.orders)" label="Order" hint="Select an order from this merchant" @input="val => { changeOrder() }" @@ -187,7 +187,7 @@ msg: this.newMessage, pubkey: this.user.keys.publickey } - ws.send(JSON.stringify(message)) + this.ws.send(JSON.stringify(message)) this.clearMessage() }, @@ -236,10 +236,16 @@ LNbits.utils.notifyApiError(error) }) }, - changeOrder() { - console.log(this.selectedOrder) + async changeOrder() { + this.products = this.user.orders[this.selectedOrder] + this.messages = {} + await this.getMessages(this.selectedOrder) + this.startChat(this.selectedOrder) }, startChat(room_name) { + if (this.ws) { + this.ws.close() + } if (location.protocol == 'https:') { ws_scheme = 'wss://' } else { @@ -268,51 +274,51 @@ } }, async created() { - this.stall = JSON.parse('{{ stall | tojson }}') let order_details = JSON.parse('{{ order | tojson }}') let products = JSON.parse('{{ products | tojson }}') - let order_id = '{{ order_id }}' + this.stall = JSON.parse('{{ stall | tojson }}') + this.products = order_details.map(o => { + let product = products.find(p => p.id == o.product_id) + return { + quantity: o.quantity, + name: product.product, + image: product.image, + price: product.price + } + }) + let data = this.$q.localStorage.getItem(`lnbits.diagonalley.data`) try { if (data) { this.user = data //add chat key (merchant pubkey) if not set - if (!this.user.chats[`${order_id}`]) { - this.$set(this.user.chats, order_id, []) + if (!this.user.orders[`${order_id}`]) { + this.$set(this.user.orders, order_id, this.products) } //this.$q.localStorage.set(`lnbits.diagonalley.data`, this.user) } else { // generate keys await this.generateKeys() // populate user data - this.user.chats = { - [`${order_id}`]: [] + this.user.orders = { + [`${order_id}`]: this.products } - this.user.orders = [] + //this.user.orders = [] } - this.order_details = order_details - this.products = order_details.map(o => { - let product = products.find(p => p.id == o.product_id) - return { - quantity: o.quantity, - name: product.product, - image: product.image, - price: product.price - } - }) + //this.order_details = order_details - this.user.orders = [...new Set([...this.user.orders, order_id])] + //this.user.orders = [...new Set([...this.user.orders, order_id])] this.selectedOrder = order_id await this.getMessages(order_id) this.$q.localStorage.set(`lnbits.diagonalley.data`, this.user) this.startChat(order_id) - console.log(this.products) + console.log(this.messages) } catch (e) { console.error(e) } diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/stall.html b/lnbits/extensions/diagonalley/templates/diagonalley/stall.html index a3a04b1e..05163573 100644 --- a/lnbits/extensions/diagonalley/templates/diagonalley/stall.html +++ b/lnbits/extensions/diagonalley/templates/diagonalley/stall.html @@ -125,7 +125,7 @@ >
- {{cat}}
@@ -409,8 +409,18 @@ if (res.data.paid) { this.$q.notify({ type: 'positive', - message: 'Sats received, thanks!', - icon: 'thumb_up' + multiLine: true, + message: + "Sats received, thanks! You'l be redirected to the order page...", + icon: 'thumb_up', + actions: [ + { + label: 'See Order', + handler: () => { + window.location.href = `/diagonalley/order/?merch=${this.stall.id}&invoice_id=${this.qrCodeDialog.data.payment_hash}` + } + } + ] }) clearInterval(this.qrCodeDialog.paymentChecker) this.resetCart() diff --git a/lnbits/extensions/diagonalley/views.py b/lnbits/extensions/diagonalley/views.py index 27875287..2bf98211 100644 --- a/lnbits/extensions/diagonalley/views.py +++ b/lnbits/extensions/diagonalley/views.py @@ -94,7 +94,9 @@ async def display(request: Request, market_id): @diagonalley_ext.get("/order", response_class=HTMLResponse) -async def chat_page(request: Request, merch: str = Query(...), invoice_id: str = Query(...)): +async def chat_page( + request: Request, merch: str = Query(...), invoice_id: str = Query(...) +): stall = await get_diagonalley_stall(merch) order = await get_diagonalley_order_invoiceid(invoice_id) _order = await get_diagonalley_order_details(order.id) @@ -110,9 +112,9 @@ async def chat_page(request: Request, merch: str = Query(...), invoice_id: str = "publickey": stall.publickey, "wallet": stall.wallet, }, - "order_id": order.id, + "order_id": order.invoiceid, "order": [details.dict() for details in _order], - "products": [product.dict() for product in products] + "products": [product.dict() for product in products], }, ) @@ -123,6 +125,41 @@ async def chat_page(request: Request, merch: str = Query(...), invoice_id: str = notifier = Notifier() +# class ConnectionManager: +# def __init__(self): +# self.active_connections: List[WebSocket] = [] + +# async def connect(self, websocket: WebSocket, room_name: str): +# await websocket.accept() +# websocket.id = room_name +# self.active_connections.append(websocket) + +# def disconnect(self, websocket: WebSocket): +# self.active_connections.remove(websocket) + +# async def send_personal_message(self, message: str, room_name: str): +# for connection in self.active_connections: +# if connection.id == room_name: +# await connection.send_text(message) + +# async def broadcast(self, message: str): +# for connection in self.active_connections: +# await connection.send_text(message) + + +# manager = ConnectionManager() + + +# @diagonalley_ext.websocket("/ws/{room_name}") +# async def websocket_endpoint(websocket: WebSocket, room_name: str): +# await manager.connect(websocket, room_name) +# try: +# while True: +# data = await websocket.receive_text() +# except WebSocketDisconnect: +# manager.disconnect(websocket) + + @diagonalley_ext.websocket("/ws/{room_name}") async def websocket_endpoint( websocket: WebSocket, room_name: str, background_tasks: BackgroundTasks @@ -143,7 +180,7 @@ async def websocket_endpoint( if websocket not in room_members: print("Sender not in room member: Reconnecting...") await notifier.connect(websocket, room_name) - + print("ENDPOINT", data) await notifier._notify(data, room_name) except WebSocketDisconnect: diff --git a/lnbits/extensions/diagonalley/views_api.py b/lnbits/extensions/diagonalley/views_api.py index f84ea8e9..2f375357 100644 --- a/lnbits/extensions/diagonalley/views_api.py +++ b/lnbits/extensions/diagonalley/views_api.py @@ -1,10 +1,10 @@ from base64 import urlsafe_b64encode from http import HTTPStatus -from typing import List +from typing import List, Union from uuid import uuid4 from fastapi import Request -from fastapi.param_functions import Query +from fastapi.param_functions import Body, Query from fastapi.params import Depends from loguru import logger from secp256k1 import PrivateKey, PublicKey @@ -34,6 +34,7 @@ from .crud import ( delete_diagonalley_product, delete_diagonalley_stall, delete_diagonalley_zone, + get_diagonalley_chat_by_merchant, get_diagonalley_chat_messages, get_diagonalley_latest_chat_messages, get_diagonalley_market, @@ -255,6 +256,14 @@ async def api_diagonalley_orders( return {"message": "We could not retrieve the orders."} +@diagonalley_ext.get("/api/v1/orders/{order_id}") +async def api_diagonalley_order_by_id(order_id: str): + order = (await get_diagonalley_order(order_id)).dict() + order["details"] = await get_diagonalley_order_details(order_id) + + return order + + @diagonalley_ext.post("/api/v1/orders") async def api_diagonalley_order_create(data: createOrder): ref = urlsafe_short_hash() @@ -488,6 +497,16 @@ async def api_diagonalley_generate_keys(): ## MESSAGES/CHAT +@diagonalley_ext.get("/api/v1/chat/messages/merchant") +async def api_get_merchant_messages( + orders: str = Query(...), wallet: WalletTypeInfo = Depends(require_admin_key) +): + + return [ + msg.dict() for msg in await get_diagonalley_chat_by_merchant(orders.split(",")) + ] + + @diagonalley_ext.get("/api/v1/chat/messages/{room_name}") async def api_get_latest_chat_msg(room_name: str, all_messages: bool = Query(False)): if all_messages: From 9de38f908bfa603c1cfb98cfd078e0136e815320 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Thu, 20 Oct 2022 17:40:23 +0300 Subject: [PATCH 76/96] 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 47d7a4a8..23d391b7 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 00000000..2d15b557 --- /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 e8638d5e..daf63f42 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 f34ac509..12288c80 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 69d81dad..b789bf8f 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 f94b970a..73c87e7c 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 77/96] 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 de338b91..c4a1df72 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 622f5ec8..d8c278ff 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 750d46c9..9030b9c3 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 78/96] 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 4b95ed18..6338d97f 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 cfc6c226..53378b20 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 79/96] 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 f18d1689..6369bbc7 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 80/96] 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 626c698c..d081332f 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 From 3814989b3a142baaea1d010f7022882b72280f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Pha=CC=A3m-Bachelart?= Date: Mon, 24 Oct 2022 11:46:32 +0200 Subject: [PATCH 81/96] Add cliche wallet (#1071) --- docs/guide/wallets.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/guide/wallets.md b/docs/guide/wallets.md index 80fb54c0..10724f34 100644 --- a/docs/guide/wallets.md +++ b/docs/guide/wallets.md @@ -79,3 +79,8 @@ For the invoice to work you must have a publicly accessible URL in your LNbits. - `LNBITS_BACKEND_WALLET_CLASS`: **OpenNodeWallet** - `OPENNODE_API_ENDPOINT`: https://api.opennode.com/ - `OPENNODE_KEY`: opennodeAdminApiKey + + +### Cliche Wallet + +- `CLICHE_ENDPOINT`: ws://127.0.0.1:12000 From fc1a87f617a60de29552c8196a7f08050bb60ce5 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Mon, 24 Oct 2022 11:50:52 +0200 Subject: [PATCH 82/96] Remove unused proxy_fix (#1007) --- lnbits/app.py | 1 - lnbits/proxy_fix.py | 95 --------------------------------------------- 2 files changed, 96 deletions(-) delete mode 100644 lnbits/proxy_fix.py diff --git a/lnbits/app.py b/lnbits/app.py index 8b9cf798..075828ef 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -91,7 +91,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI: ) app.add_middleware(GZipMiddleware, minimum_size=1000) - # app.add_middleware(ASGIProxyFix) check_funding_source(app) register_assets(app) diff --git a/lnbits/proxy_fix.py b/lnbits/proxy_fix.py deleted file mode 100644 index 897835e0..00000000 --- a/lnbits/proxy_fix.py +++ /dev/null @@ -1,95 +0,0 @@ -from functools import partial -from typing import Callable, List, Optional -from urllib.parse import urlparse -from urllib.request import parse_http_list as _parse_list_header - -from quart import Request -from quart_trio.asgi import TrioASGIHTTPConnection -from werkzeug.datastructures import Headers - - -class ASGIProxyFix(TrioASGIHTTPConnection): - def _create_request_from_scope(self, send: Callable) -> Request: - headers = Headers() - headers["Remote-Addr"] = (self.scope.get("client") or [""])[0] - for name, value in self.scope["headers"]: - headers.add(name.decode("latin1").title(), value.decode("latin1")) - if self.scope["http_version"] < "1.1": - headers.setdefault("Host", self.app.config["SERVER_NAME"] or "") - - path = self.scope["path"] - path = path if path[0] == "/" else urlparse(path).path - - x_proto = self._get_real_value(1, headers.get("X-Forwarded-Proto")) - if x_proto: - self.scope["scheme"] = x_proto - - x_host = self._get_real_value(1, headers.get("X-Forwarded-Host")) - if x_host: - headers["host"] = x_host.lower() - - return self.app.request_class( - self.scope["method"], - self.scope["scheme"], - path, - self.scope["query_string"], - headers, - self.scope.get("root_path", ""), - self.scope["http_version"], - max_content_length=self.app.config["MAX_CONTENT_LENGTH"], - body_timeout=self.app.config["BODY_TIMEOUT"], - send_push_promise=partial(self._send_push_promise, send), - scope=self.scope, - ) - - def _get_real_value(self, trusted: int, value: Optional[str]) -> Optional[str]: - """Get the real value from a list header based on the configured - number of trusted proxies. - :param trusted: Number of values to trust in the header. - :param value: Comma separated list header value to parse. - :return: The real value, or ``None`` if there are fewer values - than the number of trusted proxies. - .. versionchanged:: 1.0 - Renamed from ``_get_trusted_comma``. - .. versionadded:: 0.15 - """ - if not (trusted and value): - return None - - values = self.parse_list_header(value) - if len(values) >= trusted: - return values[-trusted] - - return None - - def parse_list_header(self, value: str) -> List[str]: - result = [] - for item in _parse_list_header(value): - if item[:1] == item[-1:] == '"': - item = self.unquote_header_value(item[1:-1]) - result.append(item) - return result - - def unquote_header_value(self, value: str, is_filename: bool = False) -> str: - r"""Unquotes a header value. (Reversal of :func:`quote_header_value`). - This does not use the real unquoting but what browsers are actually - using for quoting. - .. versionadded:: 0.5 - :param value: the header value to unquote. - :param is_filename: The value represents a filename or path. - """ - if value and value[0] == value[-1] == '"': - # this is not the real unquoting, but fixing this so that the - # RFC is met will result in bugs with internet explorer and - # probably some other browsers as well. IE for example is - # uploading files with "C:\foo\bar.txt" as filename - value = value[1:-1] - - # if this is a filename and the starting characters look like - # a UNC path, then just return the value without quotes. Using the - # replace sequence below on a UNC path has the effect of turning - # the leading double slash into a single slash and then - # _fix_ie_filename() doesn't work correctly. See #458. - if not is_filename or value[:2] != "\\\\": - return value.replace("\\\\", "\\").replace('\\"', '"') - return value From 193d037ad6aa102152a824611c65a96a6ff876df Mon Sep 17 00:00:00 2001 From: Anton Kovalenko Date: Mon, 24 Oct 2022 13:23:11 +0300 Subject: [PATCH 83/96] Allow more than 2-nd level domain in incoming ln-addresses (#914) --- lnbits/core/views/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index c07df568..983d5a26 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -476,7 +476,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type except: # parse internet identifier (user@domain.com) name_domain = code.split("@") - if len(name_domain) == 2 and len(name_domain[1].split(".")) == 2: + if len(name_domain) == 2 and len(name_domain[1].split(".")) >= 2: name, domain = name_domain url = ( ("http://" if domain.endswith(".onion") else "https://") From 09cf654427d8f42baee484a5146a180b1261b1e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 24 Oct 2022 12:32:20 +0200 Subject: [PATCH 84/96] Proper Error Handling for qrcode-stream components (#1076) * proper errorhandling for camera * fix javascript for calle --- lnbits/core/static/js/wallet.js | 29 ++++++++++++++++++++++++++ lnbits/core/templates/core/wallet.html | 2 ++ 2 files changed, 31 insertions(+) diff --git a/lnbits/core/static/js/wallet.js b/lnbits/core/static/js/wallet.js index 76d82ad4..66801313 100644 --- a/lnbits/core/static/js/wallet.js +++ b/lnbits/core/static/js/wallet.js @@ -361,6 +361,35 @@ new Vue({ this.receive.status = 'pending' }) }, + onInitQR: async function (promise) { + try { + await promise + } catch (error) { + let mapping = { + NotAllowedError: 'ERROR: you need to grant camera access permission', + NotFoundError: 'ERROR: no camera on this device', + NotSupportedError: + 'ERROR: secure context required (HTTPS, localhost)', + NotReadableError: 'ERROR: is the camera already in use?', + OverconstrainedError: 'ERROR: installed cameras are not suitable', + StreamApiNotSupportedError: + 'ERROR: Stream API is not supported in this browser', + InsecureContextError: + 'ERROR: Camera access is only permitted in secure context. Use HTTPS or localhost rather than HTTP.' + } + let valid_error = Object.keys(mapping).filter(key => { + return error.name === key + }) + let camera_error = valid_error + ? mapping[valid_error] + : `ERROR: Camera error (${error.name})` + this.parse.camera.show = false + this.$q.notify({ + message: camera_error, + type: 'negative' + }) + } + }, decodeQR: function (res) { this.parse.data.request = res this.decodeRequest() diff --git a/lnbits/core/templates/core/wallet.html b/lnbits/core/templates/core/wallet.html index bccdc2b4..4bf6067c 100644 --- a/lnbits/core/templates/core/wallet.html +++ b/lnbits/core/templates/core/wallet.html @@ -653,6 +653,7 @@ @@ -671,6 +672,7 @@
From 09871bbabc27b69886d56770178b19f445b7c48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Mon, 24 Oct 2022 16:29:30 +0200 Subject: [PATCH 85/96] fix mypy for extensions (#873) * explicitly exclude all extensions from mypy * fix example extension mypy * fix subdomains extension mypy + 1 type error fixed * fix mypy discordbot * mypy check copilot extensnion * copilot black * add invoices ext to ignore * add boltz and boltcard * copilit id is necessary * was discordbot is ok Co-authored-by: dni --- lnbits/extensions/copilot/crud.py | 8 ++--- lnbits/extensions/copilot/tasks.py | 20 ++++++------ lnbits/extensions/copilot/views.py | 8 +++-- lnbits/extensions/copilot/views_api.py | 9 +++--- lnbits/extensions/discordbot/crud.py | 6 ++-- lnbits/extensions/discordbot/views.py | 4 ++- lnbits/extensions/discordbot/views_api.py | 39 +++++++++++++---------- lnbits/extensions/example/views.py | 5 ++- lnbits/extensions/subdomains/crud.py | 4 +-- lnbits/extensions/subdomains/models.py | 30 ++++++++--------- lnbits/extensions/subdomains/tasks.py | 4 +-- lnbits/extensions/subdomains/views.py | 4 ++- lnbits/extensions/subdomains/views_api.py | 25 +++++++++++---- pyproject.toml | 30 +++++++++++++++-- 14 files changed, 125 insertions(+), 71 deletions(-) diff --git a/lnbits/extensions/copilot/crud.py b/lnbits/extensions/copilot/crud.py index d0da044e..5ecb5cd4 100644 --- a/lnbits/extensions/copilot/crud.py +++ b/lnbits/extensions/copilot/crud.py @@ -10,7 +10,7 @@ from .models import Copilots, CreateCopilotData async def create_copilot( data: CreateCopilotData, inkey: Optional[str] = "" -) -> Copilots: +) -> Optional[Copilots]: copilot_id = urlsafe_short_hash() await db.execute( """ @@ -67,19 +67,19 @@ async def create_copilot( async def update_copilot( - data: CreateCopilotData, copilot_id: Optional[str] = "" + data: CreateCopilotData, copilot_id: str ) -> Optional[Copilots]: q = ", ".join([f"{field[0]} = ?" for field in data]) items = [f"{field[1]}" for field in data] items.append(copilot_id) - await db.execute(f"UPDATE copilot.newer_copilots SET {q} WHERE id = ?", (items)) + await db.execute(f"UPDATE copilot.newer_copilots SET {q} WHERE id = ?", (items,)) row = await db.fetchone( "SELECT * FROM copilot.newer_copilots WHERE id = ?", (copilot_id,) ) return Copilots(**row) if row else None -async def get_copilot(copilot_id: str) -> Copilots: +async def get_copilot(copilot_id: str) -> Optional[Copilots]: row = await db.fetchone( "SELECT * FROM copilot.newer_copilots WHERE id = ?", (copilot_id,) ) diff --git a/lnbits/extensions/copilot/tasks.py b/lnbits/extensions/copilot/tasks.py index c59ef4cc..48ad7813 100644 --- a/lnbits/extensions/copilot/tasks.py +++ b/lnbits/extensions/copilot/tasks.py @@ -26,7 +26,7 @@ async def wait_for_paid_invoices(): async def on_invoice_paid(payment: Payment) -> None: webhook = None data = None - if payment.extra.get("tag") != "copilot": + if not payment.extra or payment.extra.get("tag") != "copilot": # not an copilot invoice return @@ -71,12 +71,12 @@ async def on_invoice_paid(payment: Payment) -> None: async def mark_webhook_sent(payment: Payment, status: int) -> None: - payment.extra["wh_status"] = status - - await core_db.execute( - """ - UPDATE apipayments SET extra = ? - WHERE hash = ? - """, - (json.dumps(payment.extra), payment.payment_hash), - ) + if payment.extra: + payment.extra["wh_status"] = status + await core_db.execute( + """ + UPDATE apipayments SET extra = ? + WHERE hash = ? + """, + (json.dumps(payment.extra), payment.payment_hash), + ) diff --git a/lnbits/extensions/copilot/views.py b/lnbits/extensions/copilot/views.py index 7ee7f590..b4a2354a 100644 --- a/lnbits/extensions/copilot/views.py +++ b/lnbits/extensions/copilot/views.py @@ -15,7 +15,9 @@ templates = Jinja2Templates(directory="templates") @copilot_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): +async def index( + request: Request, user: User = Depends(check_user_exists) # type: ignore +): return copilot_renderer().TemplateResponse( "copilot/index.html", {"request": request, "user": user.dict()} ) @@ -44,7 +46,7 @@ class ConnectionManager: async def connect(self, websocket: WebSocket, copilot_id: str): await websocket.accept() - websocket.id = copilot_id + websocket.id = copilot_id # type: ignore self.active_connections.append(websocket) def disconnect(self, websocket: WebSocket): @@ -52,7 +54,7 @@ class ConnectionManager: async def send_personal_message(self, message: str, copilot_id: str): for connection in self.active_connections: - if connection.id == copilot_id: + if connection.id == copilot_id: # type: ignore await connection.send_text(message) async def broadcast(self, message: str): diff --git a/lnbits/extensions/copilot/views_api.py b/lnbits/extensions/copilot/views_api.py index 91b0572a..46611a2e 100644 --- a/lnbits/extensions/copilot/views_api.py +++ b/lnbits/extensions/copilot/views_api.py @@ -23,7 +23,7 @@ from .views import updater @copilot_ext.get("/api/v1/copilot") async def api_copilots_retrieve( - req: Request, wallet: WalletTypeInfo = Depends(get_key_type) + req: Request, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): wallet_user = wallet.wallet.user copilots = [copilot.dict() for copilot in await get_copilots(wallet_user)] @@ -37,7 +37,7 @@ async def api_copilots_retrieve( async def api_copilot_retrieve( req: Request, copilot_id: str = Query(None), - wallet: WalletTypeInfo = Depends(get_key_type), + wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore ): copilot = await get_copilot(copilot_id) if not copilot: @@ -54,7 +54,7 @@ async def api_copilot_retrieve( async def api_copilot_create_or_update( data: CreateCopilotData, copilot_id: str = Query(None), - wallet: WalletTypeInfo = Depends(require_admin_key), + wallet: WalletTypeInfo = Depends(require_admin_key), # type: ignore ): data.user = wallet.wallet.user data.wallet = wallet.wallet.id @@ -67,7 +67,8 @@ async def api_copilot_create_or_update( @copilot_ext.delete("/api/v1/copilot/{copilot_id}") async def api_copilot_delete( - copilot_id: str = Query(None), wallet: WalletTypeInfo = Depends(require_admin_key) + copilot_id: str = Query(None), + wallet: WalletTypeInfo = Depends(require_admin_key), # type: ignore ): copilot = await get_copilot(copilot_id) diff --git a/lnbits/extensions/discordbot/crud.py b/lnbits/extensions/discordbot/crud.py index 5661fcb4..629a5c00 100644 --- a/lnbits/extensions/discordbot/crud.py +++ b/lnbits/extensions/discordbot/crud.py @@ -98,21 +98,21 @@ async def get_discordbot_wallet(wallet_id: str) -> Optional[Wallets]: return Wallets(**row) if row else None -async def get_discordbot_wallets(admin_id: str) -> Optional[Wallets]: +async def get_discordbot_wallets(admin_id: str) -> List[Wallets]: rows = await db.fetchall( "SELECT * FROM discordbot.wallets WHERE admin = ?", (admin_id,) ) return [Wallets(**row) for row in rows] -async def get_discordbot_users_wallets(user_id: str) -> Optional[Wallets]: +async def get_discordbot_users_wallets(user_id: str) -> List[Wallets]: rows = await db.fetchall( """SELECT * FROM discordbot.wallets WHERE "user" = ?""", (user_id,) ) return [Wallets(**row) for row in rows] -async def get_discordbot_wallet_transactions(wallet_id: str) -> Optional[Payment]: +async def get_discordbot_wallet_transactions(wallet_id: str) -> List[Payment]: return await get_payments( wallet_id=wallet_id, complete=True, pending=False, outgoing=True, incoming=True ) diff --git a/lnbits/extensions/discordbot/views.py b/lnbits/extensions/discordbot/views.py index a5395e21..ec7d18cc 100644 --- a/lnbits/extensions/discordbot/views.py +++ b/lnbits/extensions/discordbot/views.py @@ -9,7 +9,9 @@ from . import discordbot_ext, discordbot_renderer @discordbot_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): +async def index( + request: Request, user: User = Depends(check_user_exists) # type: ignore +): return discordbot_renderer().TemplateResponse( "discordbot/index.html", {"request": request, "user": user.dict()} ) diff --git a/lnbits/extensions/discordbot/views_api.py b/lnbits/extensions/discordbot/views_api.py index 6f213a89..e6d004db 100644 --- a/lnbits/extensions/discordbot/views_api.py +++ b/lnbits/extensions/discordbot/views_api.py @@ -27,32 +27,37 @@ from .models import CreateUserData, CreateUserWallet @discordbot_ext.get("/api/v1/users", status_code=HTTPStatus.OK) -async def api_discordbot_users(wallet: WalletTypeInfo = Depends(get_key_type)): +async def api_discordbot_users( + wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore +): user_id = wallet.wallet.user return [user.dict() for user in await get_discordbot_users(user_id)] @discordbot_ext.get("/api/v1/users/{user_id}", status_code=HTTPStatus.OK) -async def api_discordbot_user(user_id, wallet: WalletTypeInfo = Depends(get_key_type)): +async def api_discordbot_user( + user_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore +): user = await get_discordbot_user(user_id) - return user.dict() + if user: + return user.dict() @discordbot_ext.post("/api/v1/users", status_code=HTTPStatus.CREATED) async def api_discordbot_users_create( - data: CreateUserData, wallet: WalletTypeInfo = Depends(get_key_type) + data: CreateUserData, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): user = await create_discordbot_user(data) full = user.dict() - full["wallets"] = [ - wallet.dict() for wallet in await get_discordbot_users_wallets(user.id) - ] + wallets = await get_discordbot_users_wallets(user.id) + if wallets: + full["wallets"] = [wallet for wallet in wallets] return full @discordbot_ext.delete("/api/v1/users/{user_id}") async def api_discordbot_users_delete( - user_id, wallet: WalletTypeInfo = Depends(get_key_type) + user_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): user = await get_discordbot_user(user_id) if not user: @@ -75,7 +80,7 @@ async def api_discordbot_activate_extension( raise HTTPException( status_code=HTTPStatus.NOT_FOUND, detail="User does not exist." ) - update_user_extension(user_id=userid, extension=extension, active=active) + await update_user_extension(user_id=userid, extension=extension, active=active) return {"extension": "updated"} @@ -84,7 +89,7 @@ async def api_discordbot_activate_extension( @discordbot_ext.post("/api/v1/wallets") async def api_discordbot_wallets_create( - data: CreateUserWallet, wallet: WalletTypeInfo = Depends(get_key_type) + data: CreateUserWallet, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): user = await create_discordbot_wallet( user_id=data.user_id, wallet_name=data.wallet_name, admin_id=data.admin_id @@ -93,28 +98,30 @@ async def api_discordbot_wallets_create( @discordbot_ext.get("/api/v1/wallets") -async def api_discordbot_wallets(wallet: WalletTypeInfo = Depends(get_key_type)): +async def api_discordbot_wallets( + wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore +): admin_id = wallet.wallet.user - return [wallet.dict() for wallet in await get_discordbot_wallets(admin_id)] + return await get_discordbot_wallets(admin_id) @discordbot_ext.get("/api/v1/transactions/{wallet_id}") async def api_discordbot_wallet_transactions( - wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) + wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): return await get_discordbot_wallet_transactions(wallet_id) @discordbot_ext.get("/api/v1/wallets/{user_id}") async def api_discordbot_users_wallets( - user_id, wallet: WalletTypeInfo = Depends(get_key_type) + user_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): - return [s_wallet.dict() for s_wallet in await get_discordbot_users_wallets(user_id)] + return await get_discordbot_users_wallets(user_id) @discordbot_ext.delete("/api/v1/wallets/{wallet_id}") async def api_discordbot_wallets_delete( - wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) + wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore ): get_wallet = await get_discordbot_wallet(wallet_id) if not get_wallet: diff --git a/lnbits/extensions/example/views.py b/lnbits/extensions/example/views.py index 252b4726..29b257f4 100644 --- a/lnbits/extensions/example/views.py +++ b/lnbits/extensions/example/views.py @@ -12,7 +12,10 @@ templates = Jinja2Templates(directory="templates") @example_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): +async def index( + request: Request, + user: User = Depends(check_user_exists), # type: ignore +): return example_renderer().TemplateResponse( "example/index.html", {"request": request, "user": user.dict()} ) diff --git a/lnbits/extensions/subdomains/crud.py b/lnbits/extensions/subdomains/crud.py index 207e2d1d..aa358d11 100644 --- a/lnbits/extensions/subdomains/crud.py +++ b/lnbits/extensions/subdomains/crud.py @@ -3,10 +3,10 @@ from typing import List, Optional, Union from lnbits.helpers import urlsafe_short_hash from . import db -from .models import CreateDomain, Domains, Subdomains +from .models import CreateDomain, CreateSubdomain, Domains, Subdomains -async def create_subdomain(payment_hash, wallet, data: CreateDomain) -> Subdomains: +async def create_subdomain(payment_hash, wallet, data: CreateSubdomain) -> Subdomains: await db.execute( """ INSERT INTO subdomains.subdomain (id, domain, email, subdomain, ip, wallet, sats, duration, paid, record_type) diff --git a/lnbits/extensions/subdomains/models.py b/lnbits/extensions/subdomains/models.py index 17004504..39e17615 100644 --- a/lnbits/extensions/subdomains/models.py +++ b/lnbits/extensions/subdomains/models.py @@ -3,24 +3,24 @@ from pydantic.main import BaseModel class CreateDomain(BaseModel): - wallet: str = Query(...) - domain: str = Query(...) - cf_token: str = Query(...) - cf_zone_id: str = Query(...) - webhook: str = Query("") - description: str = Query(..., min_length=0) - cost: int = Query(..., ge=0) - allowed_record_types: str = Query(...) + wallet: str = Query(...) # type: ignore + domain: str = Query(...) # type: ignore + cf_token: str = Query(...) # type: ignore + cf_zone_id: str = Query(...) # type: ignore + webhook: str = Query("") # type: ignore + description: str = Query(..., min_length=0) # type: ignore + cost: int = Query(..., ge=0) # type: ignore + allowed_record_types: str = Query(...) # type: ignore class CreateSubdomain(BaseModel): - domain: str = Query(...) - subdomain: str = Query(...) - email: str = Query(...) - ip: str = Query(...) - sats: int = Query(..., ge=0) - duration: int = Query(...) - record_type: str = Query(...) + domain: str = Query(...) # type: ignore + subdomain: str = Query(...) # type: ignore + email: str = Query(...) # type: ignore + ip: str = Query(...) # type: ignore + sats: int = Query(..., ge=0) # type: ignore + duration: int = Query(...) # type: ignore + record_type: str = Query(...) # type: ignore class Domains(BaseModel): diff --git a/lnbits/extensions/subdomains/tasks.py b/lnbits/extensions/subdomains/tasks.py index 04ee2dd4..c5a7f47b 100644 --- a/lnbits/extensions/subdomains/tasks.py +++ b/lnbits/extensions/subdomains/tasks.py @@ -20,7 +20,7 @@ async def wait_for_paid_invoices(): async def on_invoice_paid(payment: Payment) -> None: - if payment.extra.get("tag") != "lnsubdomain": + if not payment.extra or payment.extra.get("tag") != "lnsubdomain": # not an lnurlp invoice return @@ -37,7 +37,7 @@ async def on_invoice_paid(payment: Payment) -> None: ) ### Use webhook to notify about cloudflare registration - if domain.webhook: + if domain and domain.webhook: async with httpx.AsyncClient() as client: try: r = await client.post( diff --git a/lnbits/extensions/subdomains/views.py b/lnbits/extensions/subdomains/views.py index df387ba8..962f850d 100644 --- a/lnbits/extensions/subdomains/views.py +++ b/lnbits/extensions/subdomains/views.py @@ -16,7 +16,9 @@ templates = Jinja2Templates(directory="templates") @subdomains_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): +async def index( + request: Request, user: User = Depends(check_user_exists) # type:ignore +): return subdomains_renderer().TemplateResponse( "subdomains/index.html", {"request": request, "user": user.dict()} ) diff --git a/lnbits/extensions/subdomains/views_api.py b/lnbits/extensions/subdomains/views_api.py index b01e6ffb..34d8e75b 100644 --- a/lnbits/extensions/subdomains/views_api.py +++ b/lnbits/extensions/subdomains/views_api.py @@ -29,12 +29,15 @@ from .crud import ( @subdomains_ext.get("/api/v1/domains") async def api_domains( - g: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False) + g: WalletTypeInfo = Depends(get_key_type), # type: ignore + all_wallets: bool = Query(False), ): wallet_ids = [g.wallet.id] if all_wallets: - wallet_ids = (await get_user(g.wallet.user)).wallet_ids + user = await get_user(g.wallet.user) + if user is not None: + wallet_ids = user.wallet_ids return [domain.dict() for domain in await get_domains(wallet_ids)] @@ -42,7 +45,9 @@ async def api_domains( @subdomains_ext.post("/api/v1/domains") @subdomains_ext.put("/api/v1/domains/{domain_id}") async def api_domain_create( - data: CreateDomain, domain_id=None, g: WalletTypeInfo = Depends(get_key_type) + data: CreateDomain, + domain_id=None, + g: WalletTypeInfo = Depends(get_key_type), # type: ignore ): if domain_id: domain = await get_domain(domain_id) @@ -63,7 +68,9 @@ async def api_domain_create( @subdomains_ext.delete("/api/v1/domains/{domain_id}") -async def api_domain_delete(domain_id, g: WalletTypeInfo = Depends(get_key_type)): +async def api_domain_delete( + domain_id, g: WalletTypeInfo = Depends(get_key_type) # type: ignore +): domain = await get_domain(domain_id) if not domain: @@ -82,12 +89,14 @@ async def api_domain_delete(domain_id, g: WalletTypeInfo = Depends(get_key_type) @subdomains_ext.get("/api/v1/subdomains") async def api_subdomains( - all_wallets: bool = Query(False), g: WalletTypeInfo = Depends(get_key_type) + all_wallets: bool = Query(False), g: WalletTypeInfo = Depends(get_key_type) # type: ignore ): wallet_ids = [g.wallet.id] if all_wallets: - wallet_ids = (await get_user(g.wallet.user)).wallet_ids + user = await get_user(g.wallet.user) + if user is not None: + wallet_ids = user.wallet_ids return [domain.dict() for domain in await get_subdomains(wallet_ids)] @@ -173,7 +182,9 @@ async def api_subdomain_send_subdomain(payment_hash): @subdomains_ext.delete("/api/v1/subdomains/{subdomain_id}") -async def api_subdomain_delete(subdomain_id, g: WalletTypeInfo = Depends(get_key_type)): +async def api_subdomain_delete( + subdomain_id, g: WalletTypeInfo = Depends(get_key_type) # type: ignore +): subdomain = await get_subdomain(subdomain_id) if not subdomain: diff --git a/pyproject.toml b/pyproject.toml index 19dac860..7418de27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,8 +89,34 @@ profile = "black" ignore_missing_imports = "True" files = "lnbits" exclude = """(?x)( - ^lnbits/extensions. - | ^lnbits/wallets/lnd_grpc_files. + ^lnbits/extensions/bleskomat. + | ^lnbits/extensions/boltz. + | ^lnbits/extensions/boltcards. + | ^lnbits/extensions/events. + | ^lnbits/extensions/hivemind. + | ^lnbits/extensions/invoices. + | ^lnbits/extensions/jukebox. + | ^lnbits/extensions/livestream. + | ^lnbits/extensions/lnaddress. + | ^lnbits/extensions/lndhub. + | ^lnbits/extensions/lnticket. + | ^lnbits/extensions/lnurldevice. + | ^lnbits/extensions/lnurlp. + | ^lnbits/extensions/lnurlpayout. + | ^lnbits/extensions/ngrok. + | ^lnbits/extensions/offlineshop. + | ^lnbits/extensions/paywall. + | ^lnbits/extensions/satsdice. + | ^lnbits/extensions/satspay. + | ^lnbits/extensions/scrub. + | ^lnbits/extensions/splitpayments. + | ^lnbits/extensions/streamalerts. + | ^lnbits/extensions/tipjar. + | ^lnbits/extensions/tpos. + | ^lnbits/extensions/usermanager. + | ^lnbits/extensions/watchonly. + | ^lnbits/extensions/withdraw. + | ^lnbits/wallets/lnd_grpc_files. )""" [tool.pytest.ini_options] From 4dcb8d2ca047477f66b5a562735c85a2b1d52239 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Tue, 25 Oct 2022 11:55:55 +0100 Subject: [PATCH 86/96] redirect to order page --- lnbits/extensions/diagonalley/templates/diagonalley/stall.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/stall.html b/lnbits/extensions/diagonalley/templates/diagonalley/stall.html index 05163573..1299f694 100644 --- a/lnbits/extensions/diagonalley/templates/diagonalley/stall.html +++ b/lnbits/extensions/diagonalley/templates/diagonalley/stall.html @@ -425,6 +425,9 @@ clearInterval(this.qrCodeDialog.paymentChecker) this.resetCart() this.closeQrCodeDialog() + setTimeout(() => { + window.location.href = `/diagonalley/order/?merch=${this.stall.id}&invoice_id=${this.qrCodeDialog.data.payment_hash}` + }, 5000) } }) .catch(error => { From 227eaeae75f7228934127fc114fffa1bff4e301f Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Tue, 25 Oct 2022 11:57:01 +0100 Subject: [PATCH 87/96] some cleanup --- .../templates/diagonalley/index.html | 34 ++++++++----------- lnbits/extensions/diagonalley/views_api.py | 4 +-- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/index.html b/lnbits/extensions/diagonalley/templates/diagonalley/index.html index f898fd40..0882fd2f 100644 --- a/lnbits/extensions/diagonalley/templates/diagonalley/index.html +++ b/lnbits/extensions/diagonalley/templates/diagonalley/index.html @@ -580,6 +580,7 @@ - Link to pass to stall relay + Stall simple UI shopping cart {{ col.value }} @@ -1952,17 +1953,22 @@ }) }) }, - shipOrder: function (order_id) { - var self = this - + shipOrder(order_id){ LNbits.api .request( 'GET', '/diagonalley/api/v1/orders/shipped/' + order_id, this.g.user.wallets[0].inkey ) - .then(function (response) { - self.orders.push(mapOrders(response.data)) + .then((response) => { + console.log(response.data) + this.orders = _.reject(this.orders, (obj) => { + return obj.id == order_id + }) + this.orders.push(mapOrders(response.data)) + }) + .catch((error) => { + LNbits.utils.notifyApiError(error) }) }, exportOrdersCSV: function () { @@ -1999,7 +2005,7 @@ ] } } - console.log({chat}) + //console.log({chat}) this.$q.localStorage.set(`lnbits.diagonalley.${this.g.user.id}`, { ...data, chat @@ -2014,28 +2020,16 @@ let idx = this.orders.findIndex(f => f.invoiceid == key) if (!lastMsgs[key]) { this.updateLastSeenMsg(key) - //this.orders[idx].unread = true return } - console.log( - 'Key', - key, - 'saved:', - lastMsgs[key].timestamp, - 'messages: ', - Math.max(...this.messages[key].map(c => c.timestamp)), - lastMsgs[key].timestamp < - Math.max(...this.messages[key].map(c => c.timestamp)) - ) + if ( lastMsgs[key].timestamp < Math.max(...this.messages[key].map(c => c.timestamp)) ) { this.$set(this.orders[idx], 'unread', true) - // this.orders[idx].unread = true } else { this.$set(this.orders[idx], 'unread', false) - // this.orders[idx].unread = false } console.log('Order:', this.orders[idx]) } diff --git a/lnbits/extensions/diagonalley/views_api.py b/lnbits/extensions/diagonalley/views_api.py index 2f375357..e6a4c2de 100644 --- a/lnbits/extensions/diagonalley/views_api.py +++ b/lnbits/extensions/diagonalley/views_api.py @@ -286,8 +286,6 @@ async def api_diagonalley_order_create(data: createOrder): "payment_request": payment_request, "order_reference": ref, } - # order = await create_diagonalley_order(wallet_id=wallet.wallet.id, data=data) - # return order.dict() @diagonalley_ext.get("/api/v1/orders/payments/{payment_hash}") @@ -352,7 +350,7 @@ async def api_diagonalley_order_shipped( "SELECT * FROM diagonalley.orders WHERE id = ?", (order_id,) ) - return [order.dict() for order in get_diagonalley_orders(order["wallet"])] + return order ###List products based on stall id From c282f38726c2b62b4f4588cb665624aeb4fcd0ce Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Tue, 25 Oct 2022 12:30:15 +0100 Subject: [PATCH 88/96] cleanup and keys management --- .../templates/diagonalley/order.html | 104 ++++++++++++++++-- 1 file changed, 92 insertions(+), 12 deletions(-) diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/order.html b/lnbits/extensions/diagonalley/templates/diagonalley/order.html index 81cdfc36..fe5c7903 100644 --- a/lnbits/extensions/diagonalley/templates/diagonalley/order.html +++ b/lnbits/extensions/diagonalley/templates/diagonalley/order.html @@ -133,11 +133,15 @@
- Backup keys Download your keys - Restore keys + Restore keys + + Delete data Delete all data from browser @@ -145,6 +149,50 @@
+ + + + + + + + +
+ + +
+
+
+
{% endblock %} {% block scripts %}