From 3b70d45091e6d69aac04a207c59e1115eba8c76f Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 4 Oct 2022 09:56:15 +0200 Subject: [PATCH 01/34] .env variable for STARTUP_INVOICE_EXPIRY_CHECK --- .env.example | 2 ++ lnbits/settings.py | 1 + lnbits/tasks.py | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 93b823250..1c7d15297 100644 --- a/.env.example +++ b/.env.example @@ -32,6 +32,8 @@ LNBITS_SERVICE_FEE="0.0" LNBITS_RESERVE_FEE_MIN=2000 # value in percent LNBITS_RESERVE_FEE_PERCENT=1.0 +# check invoice expiry on every startup (can be slow on large instances) +STARTUP_INVOICE_EXPIRY_CHECK=True # Change theme LNBITS_SITE_TITLE="LNbits" diff --git a/lnbits/settings.py b/lnbits/settings.py index 3f4e31cc5..9fc3c1972 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -64,6 +64,7 @@ PREFER_SECURE_URLS = env.bool("LNBITS_FORCE_HTTPS", default=True) RESERVE_FEE_MIN = env.int("LNBITS_RESERVE_FEE_MIN", default=2000) RESERVE_FEE_PERCENT = env.float("LNBITS_RESERVE_FEE_PERCENT", default=1.0) SERVICE_FEE = env.float("LNBITS_SERVICE_FEE", default=0.0) +STARTUP_INVOICE_EXPIRY_CHECK = env.bool("STARTUP_INVOICE_EXPIRY_CHECK", default=True) try: LNBITS_COMMIT = ( diff --git a/lnbits/tasks.py b/lnbits/tasks.py index 94e43dcfd..267583034 100644 --- a/lnbits/tasks.py +++ b/lnbits/tasks.py @@ -15,7 +15,7 @@ from lnbits.core.crud import ( get_standalone_payment, ) from lnbits.core.services import redeem_lnurl_withdraw -from lnbits.settings import WALLET +from lnbits.settings import WALLET, STARTUP_INVOICE_EXPIRY_CHECK from .core import db @@ -144,7 +144,7 @@ async def check_pending_payments(): f"Task: pending check finished for {len(pending_payments)} payments (took {time.time() - start_time:0.3f} s)" ) # we delete expired invoices once upon the first pending check - if incoming: + if incoming and STARTUP_INVOICE_EXPIRY_CHECK: logger.debug("Task: deleting all expired invoices") start_time: float = time.time() await delete_expired_invoices(conn=conn) From efc024e1c0e3fcf5631dfcf8c98bc120f6dfbb6b Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Wed, 5 Oct 2022 14:17:23 +0200 Subject: [PATCH 02/34] check expiry upon payment creation --- lnbits/core/crud.py | 77 +++++++++++++++++++++++---------------- lnbits/core/migrations.py | 59 ++++++++++++++++++++++++++++++ poetry.lock | 20 +++++----- pyproject.toml | 1 + 4 files changed, 116 insertions(+), 41 deletions(-) diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index cbed62922..ae8de7ab9 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -321,37 +321,45 @@ async def delete_expired_invoices( AND time < {db.timestamp_now} - {db.interval_seconds(2592000)} """ ) - - # then we delete all expired invoices, checking one by one - rows = await (conn or db).fetchall( + # then we delete all invoices whose expiry date is in the past + await (conn or db).execute( f""" - SELECT bolt11 - FROM apipayments - WHERE pending = true - AND bolt11 IS NOT NULL - AND amount > 0 AND time < {db.timestamp_now} - {db.interval_seconds(86400)} + DELETE FROM apipayments + WHERE pending = true AND amount > 0 + AND expiry < {db.timestamp_now} """ ) - logger.debug(f"Checking expiry of {len(rows)} invoices") - for (payment_request,) in rows: - try: - invoice = bolt11.decode(payment_request) - except: - continue - expiration_date = datetime.datetime.fromtimestamp(invoice.date + invoice.expiry) - if expiration_date > datetime.datetime.utcnow(): - continue - logger.debug( - f"Deleting expired invoice: {invoice.payment_hash} (expired: {expiration_date})" - ) - await (conn or db).execute( - """ - DELETE FROM apipayments - WHERE pending = true AND hash = ? - """, - (invoice.payment_hash,), - ) + # # then we delete all expired invoices, checking one by one + # rows = await (conn or db).fetchall( + # f""" + # SELECT bolt11 + # FROM apipayments + # WHERE pending = true + # AND bolt11 IS NOT NULL + # AND amount > 0 AND time < {db.timestamp_now} - {db.interval_seconds(86400)} + # """ + # ) + # logger.debug(f"Checking expiry of {len(rows)} invoices") + # for (payment_request,) in rows: + # try: + # invoice = bolt11.decode(payment_request) + # except: + # continue + + # expiration_date = datetime.datetime.fromtimestamp(invoice.date + invoice.expiry) + # if expiration_date > datetime.datetime.utcnow(): + # continue + # logger.debug( + # f"Deleting expired invoice: {invoice.payment_hash} (expired: {expiration_date})" + # ) + # await (conn or db).execute( + # """ + # DELETE FROM apipayments + # WHERE pending = true AND hash = ? + # """, + # (invoice.payment_hash,), + # ) # payments @@ -375,15 +383,21 @@ async def create_payment( ) -> Payment: # todo: add this when tests are fixed - # previous_payment = await get_wallet_payment(wallet_id, payment_hash, conn=conn) - # assert previous_payment is None, "Payment already exists" + previous_payment = await get_wallet_payment(wallet_id, payment_hash, conn=conn) + assert previous_payment is None, "Payment already exists" + + try: + invoice = bolt11.decode(payment_request) + expiration_date = datetime.datetime.fromtimestamp(invoice.date + invoice.expiry) + except: + expiration_date = datetime.datetime.now() + datetime.timedelta(days=31) await (conn or db).execute( """ INSERT INTO apipayments (wallet, checking_id, bolt11, hash, preimage, - amount, pending, memo, fee, extra, webhook) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + amount, pending, memo, fee, extra, webhook, expiry) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( wallet_id, @@ -399,6 +413,7 @@ async def create_payment( if extra and extra != {} and type(extra) is dict else None, webhook, + expiration_date, ), ) diff --git a/lnbits/core/migrations.py b/lnbits/core/migrations.py index ebecb5e30..e422df08a 100644 --- a/lnbits/core/migrations.py +++ b/lnbits/core/migrations.py @@ -1,4 +1,6 @@ from sqlalchemy.exc import OperationalError # type: ignore +from lnbits import bolt11 +import datetime async def m000_create_migrations_table(db): @@ -188,3 +190,60 @@ async def m005_balance_check_balance_notify(db): ); """ ) + + +async def m006_add_invoice_expiry_to_apipayments(db): + """ + Adds invoice expiry field to apipayments and precomputes them for + existing entries + """ + try: + rows = await ( + await db.execute( + f""" + SELECT bolt11, checking_id + FROM apipayments + WHERE pending = true + AND bolt11 IS NOT NULL + AND expiry IS NULL + AND amount > 0 AND time < {db.timestamp_now} - {db.interval_seconds(86400)} + """ + ) + ).fetchall() + # then we delete all expired invoices, checking one by one + print(f"Checking expiry of {len(rows)} invoices") + for i, ( + payment_request, + payment_hash, + ) in enumerate(rows): + print(f"Checking invoice {i}/{len(rows)}") + try: + invoice = bolt11.decode(payment_request) + except: + continue + if payment_hash != invoice.payment_hash: + print("Error: {payment_hash} != {invoice.payment_hash}") + continue + + expiration_date = datetime.datetime.fromtimestamp( + invoice.date + invoice.expiry + ) + print( + f"Setting expiry of invoice {invoice.payment_hash} to {expiration_date}" + ) + await db.execute( + """ + UPDATE apipayments SET expiry = ? + WHERE checking_id = ? AND amount > 0 + """, + ( + expiration_date, + invoice.payment_hash, + ), + ) + except OperationalError: + # this is necessary now because it may be the case that this migration will + # run twice in some environments. + # catching errors like this won't be necessary in anymore now that we + # keep track of db versions so no migration ever runs twice. + pass diff --git a/poetry.lock b/poetry.lock index 343fffbfb..281b94749 100644 --- a/poetry.lock +++ b/poetry.lock @@ -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 = "1a0341703a38328d0b00ab169a02b238d88b2d51ab8663da5591d3ee32de55ec" [metadata.files] aiofiles = [ @@ -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"}, diff --git a/pyproject.toml b/pyproject.toml index 19dac8600..40bf3c04c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,7 @@ protobuf = "^4.21.6" Cerberus = "^1.3.4" async-timeout = "^4.0.2" pyln-client = "0.11.1" +setuptools = "^65.4.1" [tool.poetry.dev-dependencies] isort = "^5.10.1" From 4e69924c9c377a16fe6a7d173e2411e6e616fb4d Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 2 Dec 2022 17:38:36 +0100 Subject: [PATCH 03/34] fix datetime conversion --- lnbits/core/crud.py | 3 ++- lnbits/core/migrations.py | 8 ++++++-- lnbits/db.py | 16 ++++++++-------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index 8c46ae1a8..e111abd5f 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -348,6 +348,7 @@ async def delete_expired_invoices( """ ) + # payments # -------- @@ -399,7 +400,7 @@ async def create_payment( if extra and extra != {} and type(extra) is dict else None, webhook, - expiration_date, + db.datetime_to_timestamp(expiration_date), ), ) diff --git a/lnbits/core/migrations.py b/lnbits/core/migrations.py index 7dbfe2f16..a67715854 100644 --- a/lnbits/core/migrations.py +++ b/lnbits/core/migrations.py @@ -197,6 +197,10 @@ async def m006_add_invoice_expiry_to_apipayments(db): Adds invoice expiry field to apipayments and precomputes them for existing entries """ + try: + await db.execute("ALTER TABLE apipayments ADD COLUMN expiry TIMESTAMP") + except OperationalError: + pass try: rows = await ( await db.execute( @@ -206,7 +210,7 @@ async def m006_add_invoice_expiry_to_apipayments(db): WHERE pending = true AND bolt11 IS NOT NULL AND expiry IS NULL - AND amount > 0 AND time < {db.timestamp_now} - {db.interval_seconds(86400)} + AND amount > 0 AND time < {db.timestamp_now} """ ) ).fetchall() @@ -237,7 +241,7 @@ async def m006_add_invoice_expiry_to_apipayments(db): WHERE checking_id = ? AND amount > 0 """, ( - expiration_date, + db.datetime_to_timestamp(expiration_date), invoice.payment_hash, ), ) diff --git a/lnbits/db.py b/lnbits/db.py index e83b4bf84..e4ee38823 100644 --- a/lnbits/db.py +++ b/lnbits/db.py @@ -29,6 +29,13 @@ class Compat: return f"{seconds}" return "" + def datetime_to_timestamp(self, date: datetime.datetime): + if self.type in {POSTGRES, COCKROACH}: + return date.strftime("%Y-%m-%d %H:%M:%S") + elif self.type == SQLITE: + return time.mktime(date.timetuple()) + return "" + @property def timestamp_now(self) -> str: if self.type in {POSTGRES, COCKROACH}: @@ -149,14 +156,7 @@ class Database(Compat): psycopg2.extensions.register_type( psycopg2.extensions.new_type( - (1184, 1114), - "TIMESTAMP2INT", - _parse_timestamp - # lambda value, curs: time.mktime( - # datetime.datetime.strptime( - # value, "%Y-%m-%d %H:%M:%S.%f" - # ).timetuple() - # ), + (1184, 1114), "TIMESTAMP2INT", _parse_timestamp ) ) else: From 1a98f15d4858f00993ac50352f386f26841014b3 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 2 Dec 2022 17:39:56 +0100 Subject: [PATCH 04/34] remove STARTUP_INVOICE_EXPIRY_CHECK --- .env.example | 2 -- lnbits/core/migrations.py | 6 ++++-- lnbits/settings.py | 1 - lnbits/tasks.py | 3 +-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index b5c9feb27..898f90bd6 100644 --- a/.env.example +++ b/.env.example @@ -38,8 +38,6 @@ LNBITS_SERVICE_FEE="0.0" LNBITS_RESERVE_FEE_MIN=2000 # value in percent LNBITS_RESERVE_FEE_PERCENT=1.0 -# check invoice expiry on every startup (can be slow on large instances) -STARTUP_INVOICE_EXPIRY_CHECK=True # Change theme LNBITS_SITE_TITLE="LNbits" diff --git a/lnbits/core/migrations.py b/lnbits/core/migrations.py index a67715854..04a145eb0 100644 --- a/lnbits/core/migrations.py +++ b/lnbits/core/migrations.py @@ -1,7 +1,9 @@ -from sqlalchemy.exc import OperationalError # type: ignore -from lnbits import bolt11 import datetime +from sqlalchemy.exc import OperationalError # type: ignore + +from lnbits import bolt11 + async def m000_create_migrations_table(db): await db.execute( diff --git a/lnbits/settings.py b/lnbits/settings.py index d08a9d45f..17fce293c 100644 --- a/lnbits/settings.py +++ b/lnbits/settings.py @@ -69,7 +69,6 @@ PREFER_SECURE_URLS = env.bool("LNBITS_FORCE_HTTPS", default=True) RESERVE_FEE_MIN = env.int("LNBITS_RESERVE_FEE_MIN", default=2000) RESERVE_FEE_PERCENT = env.float("LNBITS_RESERVE_FEE_PERCENT", default=1.0) SERVICE_FEE = env.float("LNBITS_SERVICE_FEE", default=0.0) -STARTUP_INVOICE_EXPIRY_CHECK = env.bool("STARTUP_INVOICE_EXPIRY_CHECK", default=True) try: LNBITS_COMMIT = ( diff --git a/lnbits/tasks.py b/lnbits/tasks.py index c24bffa9a..fd2af8090 100644 --- a/lnbits/tasks.py +++ b/lnbits/tasks.py @@ -15,7 +15,6 @@ from lnbits.core.crud import ( get_standalone_payment, ) from lnbits.core.services import redeem_lnurl_withdraw -from lnbits.settings import WALLET, STARTUP_INVOICE_EXPIRY_CHECK from .core import db @@ -144,7 +143,7 @@ async def check_pending_payments(): f"Task: pending check finished for {len(pending_payments)} payments (took {time.time() - start_time:0.3f} s)" ) # we delete expired invoices once upon the first pending check - if incoming and STARTUP_INVOICE_EXPIRY_CHECK: + if incoming: logger.debug("Task: deleting all expired invoices") start_time: float = time.time() await delete_expired_invoices(conn=conn) From 2134b63cea017ab6e571593cf7242e154d5e8829 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 2 Dec 2022 17:48:47 +0100 Subject: [PATCH 05/34] readd comments --- 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 e111abd5f..9fdfa9ba5 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -370,8 +370,8 @@ async def create_payment( ) -> Payment: # todo: add this when tests are fixed - previous_payment = await get_wallet_payment(wallet_id, payment_hash, conn=conn) - assert previous_payment is None, "Payment already exists" + # previous_payment = await get_wallet_payment(wallet_id, payment_hash, conn=conn) + # assert previous_payment is None, "Payment already exists" try: invoice = bolt11.decode(payment_request) From 7a6450f0327e52db8691bc17ce9f2307ae821883 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 2 Dec 2022 18:51:18 +0100 Subject: [PATCH 06/34] add expiry to Payment model --- lnbits/core/models.py | 15 +++++++++++++++ lnbits/tasks.py | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lnbits/core/models.py b/lnbits/core/models.py index 216acafd8..a61fbb02a 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -1,6 +1,8 @@ import hashlib import hmac import json +import time +import datetime from sqlite3 import Row from typing import Dict, List, NamedTuple, Optional @@ -83,6 +85,7 @@ class Payment(BaseModel): bolt11: str preimage: str payment_hash: str + expiry: int extra: Optional[Dict] = {} wallet_id: str webhook: Optional[str] @@ -101,6 +104,7 @@ class Payment(BaseModel): fee=row["fee"], memo=row["memo"], time=row["time"], + expiry=row["expiry"], wallet_id=row["wallet"], webhook=row["webhook"], webhook_status=row["webhook_status"], @@ -128,6 +132,10 @@ class Payment(BaseModel): def is_out(self) -> bool: return self.amount < 0 + @property + def is_expired(self) -> bool: + return self.expiry < time.time() + @property def is_uncheckable(self) -> bool: return self.checking_id.startswith("internal_") @@ -170,6 +178,13 @@ class Payment(BaseModel): logger.debug(f"Status: {status}") + if self.is_in and self.is_expired: + expiration_date = datetime.datetime.fromtimestamp(self.expiry) + logger.debug( + f"Deleting expired incoming payment {self.checking_id}: expired {expiration_date}" + ) + await self.delete(conn) + if self.is_out and status.failed: logger.warning( f"Deleting outgoing failed payment {self.checking_id}: {status}" diff --git a/lnbits/tasks.py b/lnbits/tasks.py index fd2af8090..e7f6a6beb 100644 --- a/lnbits/tasks.py +++ b/lnbits/tasks.py @@ -15,7 +15,7 @@ from lnbits.core.crud import ( get_standalone_payment, ) from lnbits.core.services import redeem_lnurl_withdraw - +from lnbits.settings import WALLET from .core import db From 167c2ce9f15e95181a9d1c70c2b0c271e7b02ed9 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 2 Dec 2022 18:51:52 +0100 Subject: [PATCH 07/34] make format --- lnbits/core/models.py | 2 +- lnbits/tasks.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lnbits/core/models.py b/lnbits/core/models.py index a61fbb02a..824bbc88a 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -1,8 +1,8 @@ +import datetime import hashlib import hmac import json import time -import datetime from sqlite3 import Row from typing import Dict, List, NamedTuple, Optional diff --git a/lnbits/tasks.py b/lnbits/tasks.py index e7f6a6beb..5368c4ea7 100644 --- a/lnbits/tasks.py +++ b/lnbits/tasks.py @@ -16,6 +16,7 @@ from lnbits.core.crud import ( ) from lnbits.core.services import redeem_lnurl_withdraw from lnbits.settings import WALLET + from .core import db From 32448c4b0c43c74e654d0a6b695700f4a3bf97c8 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 2 Dec 2022 18:55:53 +0100 Subject: [PATCH 08/34] on-the-fly deletion --- lnbits/core/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnbits/core/models.py b/lnbits/core/models.py index 824bbc88a..8abf23d83 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -178,7 +178,7 @@ class Payment(BaseModel): logger.debug(f"Status: {status}") - if self.is_in and self.is_expired: + if self.is_in and status.pending and self.is_expired: expiration_date = datetime.datetime.fromtimestamp(self.expiry) logger.debug( f"Deleting expired incoming payment {self.checking_id}: expired {expiration_date}" From befdeb040e672b2c44bf22537be0a8033c82f33f Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Fri, 2 Dec 2022 19:05:22 +0100 Subject: [PATCH 09/34] add expiry to frontend --- lnbits/static/js/base.js | 6 ++++++ lnbits/static/js/components.js | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lnbits/static/js/base.js b/lnbits/static/js/base.js index 579db400b..3f29ccbd6 100644 --- a/lnbits/static/js/base.js +++ b/lnbits/static/js/base.js @@ -184,6 +184,7 @@ window.LNbits = { bolt11: data.bolt11, preimage: data.preimage, payment_hash: data.payment_hash, + expiry: data.expiry, extra: data.extra, wallet_id: data.wallet_id, webhook: data.webhook, @@ -195,6 +196,11 @@ window.LNbits = { 'YYYY-MM-DD HH:mm' ) obj.dateFrom = moment(obj.date).fromNow() + obj.expirydate = Quasar.utils.date.formatDate( + new Date(obj.expiry * 1000), + 'YYYY-MM-DD HH:mm' + ) + obj.expirydateFrom = moment(obj.expirydate).fromNow() obj.msat = obj.amount obj.sat = obj.msat / 1000 obj.tag = obj.extra.tag diff --git a/lnbits/static/js/components.js b/lnbits/static/js/components.js index ab3f7f089..0d40fe10e 100644 --- a/lnbits/static/js/components.js +++ b/lnbits/static/js/components.js @@ -192,9 +192,13 @@ Vue.component('lnbits-payment-details', {
-
Date:
+
Created:
{{ payment.date }} ({{ payment.dateFrom }})
+
+
Expiry:
+
{{ payment.expirydate }} ({{ payment.expirydateFrom }})
+
Description:
{{ payment.memo }}
From 441d5337a3bd016b3b5838ba5362fcbb4db324e9 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 6 Dec 2022 10:48:16 +0100 Subject: [PATCH 10/34] fix expiry integer --- lnbits/core/crud.py | 1 + lnbits/core/migrations.py | 48 +++++++++++++++++++-------------------- lnbits/core/models.py | 2 +- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/lnbits/core/crud.py b/lnbits/core/crud.py index 9fdfa9ba5..2baa0507a 100644 --- a/lnbits/core/crud.py +++ b/lnbits/core/crud.py @@ -377,6 +377,7 @@ async def create_payment( invoice = bolt11.decode(payment_request) expiration_date = datetime.datetime.fromtimestamp(invoice.date + invoice.expiry) except: + # assume maximum bolt11 expiry of 31 days to be on the safe side expiration_date = datetime.datetime.now() + datetime.timedelta(days=31) await (conn or db).execute( diff --git a/lnbits/core/migrations.py b/lnbits/core/migrations.py index 04a145eb0..84cb7f1f0 100644 --- a/lnbits/core/migrations.py +++ b/lnbits/core/migrations.py @@ -1,5 +1,5 @@ import datetime - +from loguru import logger from sqlalchemy.exc import OperationalError # type: ignore from lnbits import bolt11 @@ -216,37 +216,35 @@ async def m006_add_invoice_expiry_to_apipayments(db): """ ) ).fetchall() - # then we delete all expired invoices, checking one by one - print(f"Checking expiry of {len(rows)} invoices") + logger.info(f"Checking expiry of {len(rows)} invoices") for i, ( payment_request, - payment_hash, + checking_id, ) in enumerate(rows): - print(f"Checking invoice {i}/{len(rows)}") + logger.info(f"Checking invoice {i}/{len(rows)}") try: invoice = bolt11.decode(payment_request) + if invoice.expiry is None: + continue + + expiration_date = datetime.datetime.fromtimestamp( + invoice.date + invoice.expiry + ) + logger.info( + f"Setting expiry of invoice {invoice.payment_hash} to {expiration_date}" + ) + await db.execute( + """ + UPDATE apipayments SET expiry = ? + WHERE checking_id = ? AND amount > 0 + """, + ( + db.datetime_to_timestamp(expiration_date), + checking_id, + ), + ) except: continue - if payment_hash != invoice.payment_hash: - print("Error: {payment_hash} != {invoice.payment_hash}") - continue - - expiration_date = datetime.datetime.fromtimestamp( - invoice.date + invoice.expiry - ) - print( - f"Setting expiry of invoice {invoice.payment_hash} to {expiration_date}" - ) - await db.execute( - """ - UPDATE apipayments SET expiry = ? - WHERE checking_id = ? AND amount > 0 - """, - ( - db.datetime_to_timestamp(expiration_date), - invoice.payment_hash, - ), - ) except OperationalError: # this is necessary now because it may be the case that this migration will # run twice in some environments. diff --git a/lnbits/core/models.py b/lnbits/core/models.py index 8abf23d83..5fe27905d 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -85,7 +85,7 @@ class Payment(BaseModel): bolt11: str preimage: str payment_hash: str - expiry: int + expiry: float extra: Optional[Dict] = {} wallet_id: str webhook: Optional[str] From 8c324d86344c059c3eed6f277871b598e2ecf585 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 6 Dec 2022 10:48:34 +0100 Subject: [PATCH 11/34] make format --- lnbits/core/migrations.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lnbits/core/migrations.py b/lnbits/core/migrations.py index 84cb7f1f0..d98c959ff 100644 --- a/lnbits/core/migrations.py +++ b/lnbits/core/migrations.py @@ -1,4 +1,5 @@ import datetime + from loguru import logger from sqlalchemy.exc import OperationalError # type: ignore From 269ea0f9759dc0cf7a2d26944b892edce8804db2 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 6 Dec 2022 13:08:36 +0100 Subject: [PATCH 12/34] optional expiry (float now) in Payment model --- lnbits/core/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lnbits/core/models.py b/lnbits/core/models.py index 5fe27905d..e9a014ce6 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -85,7 +85,7 @@ class Payment(BaseModel): bolt11: str preimage: str payment_hash: str - expiry: float + expiry: Optional[float] extra: Optional[Dict] = {} wallet_id: str webhook: Optional[str] @@ -134,7 +134,7 @@ class Payment(BaseModel): @property def is_expired(self) -> bool: - return self.expiry < time.time() + return self.expiry < time.time() if self.expiry else False @property def is_uncheckable(self) -> bool: @@ -178,10 +178,10 @@ class Payment(BaseModel): logger.debug(f"Status: {status}") - if self.is_in and status.pending and self.is_expired: + if self.is_in and self.expiry and status.pending and self.is_expired: expiration_date = datetime.datetime.fromtimestamp(self.expiry) logger.debug( - f"Deleting expired incoming payment {self.checking_id}: expired {expiration_date}" + f"Deleting expired incoming pending payment {self.checking_id}: expired {expiration_date}" ) await self.delete(conn) From 07724eaa0f48c149f93c9d00c4e2db4a3f88f7aa Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 6 Dec 2022 13:21:34 +0100 Subject: [PATCH 13/34] clean up if clause --- lnbits/core/models.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lnbits/core/models.py b/lnbits/core/models.py index e9a014ce6..62f8aa393 100644 --- a/lnbits/core/models.py +++ b/lnbits/core/models.py @@ -178,14 +178,13 @@ class Payment(BaseModel): logger.debug(f"Status: {status}") - if self.is_in and self.expiry and status.pending and self.is_expired: + if self.is_in and status.pending and self.is_expired and self.expiry: expiration_date = datetime.datetime.fromtimestamp(self.expiry) logger.debug( f"Deleting expired incoming pending payment {self.checking_id}: expired {expiration_date}" ) await self.delete(conn) - - if self.is_out and status.failed: + elif self.is_out and status.failed: logger.warning( f"Deleting outgoing failed payment {self.checking_id}: {status}" ) From 36cebffbf59a1477ff919a6fad47e3db22795c37 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 6 Dec 2022 13:23:51 +0100 Subject: [PATCH 14/34] clean migration --- lnbits/core/migrations.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lnbits/core/migrations.py b/lnbits/core/migrations.py index d98c959ff..9853c33b9 100644 --- a/lnbits/core/migrations.py +++ b/lnbits/core/migrations.py @@ -197,13 +197,14 @@ async def m005_balance_check_balance_notify(db): async def m006_add_invoice_expiry_to_apipayments(db): """ - Adds invoice expiry field to apipayments and precomputes them for + Adds invoice expiry column to apipayments and precomputes them for existing entries """ try: await db.execute("ALTER TABLE apipayments ADD COLUMN expiry TIMESTAMP") except OperationalError: pass + try: rows = await ( await db.execute( @@ -217,12 +218,11 @@ async def m006_add_invoice_expiry_to_apipayments(db): """ ) ).fetchall() - logger.info(f"Checking expiry of {len(rows)} invoices") + logger.info(f"Mirgraion: Checking expiry of {len(rows)} invoices") for i, ( payment_request, checking_id, ) in enumerate(rows): - logger.info(f"Checking invoice {i}/{len(rows)}") try: invoice = bolt11.decode(payment_request) if invoice.expiry is None: @@ -232,7 +232,7 @@ async def m006_add_invoice_expiry_to_apipayments(db): invoice.date + invoice.expiry ) logger.info( - f"Setting expiry of invoice {invoice.payment_hash} to {expiration_date}" + f"Mirgraion: {i}/{len(rows)} setting expiry of invoice {invoice.payment_hash} to {expiration_date}" ) await db.execute( """ From 00a3bd9ce724fe99d277bf6317a721b3237fa308 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 6 Dec 2022 13:26:42 +0100 Subject: [PATCH 15/34] add migrated mock data to tests --- tests/data/mock_data.zip | Bin 37478 -> 39411 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/data/mock_data.zip b/tests/data/mock_data.zip index e992b988747cae0cb4472f77ab7f21d70787fb1b..01d4a13ee9cc573617fa180e9e78c9c8c55c6875 100644 GIT binary patch delta 7467 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 ZhdvnrBT7;yVh6E@3`Qi5C0<9nM02Ws_9)H* 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 From 3bb11dc4873e84ffc749e7be97368172b1a3e924 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 6 Dec 2022 16:21:19 +0100 Subject: [PATCH 16/34] separate migrations --- lnbits/core/migrations.py | 10 +++++++--- lnbits/db.py | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lnbits/core/migrations.py b/lnbits/core/migrations.py index 9853c33b9..9c2373498 100644 --- a/lnbits/core/migrations.py +++ b/lnbits/core/migrations.py @@ -197,14 +197,18 @@ async def m005_balance_check_balance_notify(db): async def m006_add_invoice_expiry_to_apipayments(db): """ - Adds invoice expiry column to apipayments and precomputes them for - existing entries + Adds invoice expiry column to apipayments. """ try: await db.execute("ALTER TABLE apipayments ADD COLUMN expiry TIMESTAMP") except OperationalError: pass + +async def m007_set_invoice_expiries(db): + """ + Precomputes invoice expiry for existing pending incoming payments. + """ try: rows = await ( await db.execute( @@ -232,7 +236,7 @@ async def m006_add_invoice_expiry_to_apipayments(db): invoice.date + invoice.expiry ) logger.info( - f"Mirgraion: {i}/{len(rows)} setting expiry of invoice {invoice.payment_hash} to {expiration_date}" + f"Mirgraion: {i+1}/{len(rows)} setting expiry of invoice {invoice.payment_hash} to {expiration_date}" ) await db.execute( """ diff --git a/lnbits/db.py b/lnbits/db.py index e4ee38823..7d294197f 100644 --- a/lnbits/db.py +++ b/lnbits/db.py @@ -132,6 +132,8 @@ class Database(Compat): import psycopg2 # type: ignore def _parse_timestamp(value, _): + if value is None: + return None f = "%Y-%m-%d %H:%M:%S.%f" if not "." in value: f = "%Y-%m-%d %H:%M:%S" From 6eb9c5d9eec2efdc0b461c27586822c05a60c325 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 6 Dec 2022 21:04:10 +0100 Subject: [PATCH 17/34] better sort sql clause --- lnbits/core/migrations.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lnbits/core/migrations.py b/lnbits/core/migrations.py index 9c2373498..eabe5a7c5 100644 --- a/lnbits/core/migrations.py +++ b/lnbits/core/migrations.py @@ -216,9 +216,10 @@ async def m007_set_invoice_expiries(db): SELECT bolt11, checking_id FROM apipayments WHERE pending = true + AND amount > 0 AND bolt11 IS NOT NULL AND expiry IS NULL - AND amount > 0 AND time < {db.timestamp_now} + AND time < {db.timestamp_now} """ ) ).fetchall() From e15cfeb943a43c744fc0bcd960b6784a98a40afc Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Tue, 6 Dec 2022 21:05:09 +0100 Subject: [PATCH 18/34] print only if migration takes place --- lnbits/core/migrations.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lnbits/core/migrations.py b/lnbits/core/migrations.py index eabe5a7c5..2bffa5c77 100644 --- a/lnbits/core/migrations.py +++ b/lnbits/core/migrations.py @@ -223,7 +223,8 @@ async def m007_set_invoice_expiries(db): """ ) ).fetchall() - logger.info(f"Mirgraion: Checking expiry of {len(rows)} invoices") + if len(rows): + logger.info(f"Mirgraion: Checking expiry of {len(rows)} invoices") for i, ( payment_request, checking_id, From 0538035084ad37e606ba22822000483ef6902418 Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Thu, 8 Dec 2022 14:33:52 +0100 Subject: [PATCH 19/34] msat values are ints --- lnbits/core/views/api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lnbits/core/views/api.py b/lnbits/core/views/api.py index 995cf9e70..21342d686 100644 --- a/lnbits/core/views/api.py +++ b/lnbits/core/views/api.py @@ -702,9 +702,9 @@ async def api_auditor(wallet: WalletTypeInfo = Depends(get_key_type)): node_balance, delta = None, None return { - "node_balance_msats": node_balance, - "lnbits_balance_msats": total_balance, - "delta_msats": delta, + "node_balance_msats": int(node_balance), + "lnbits_balance_msats": int(total_balance), + "delta_msats": int(delta), "timestamp": int(time.time()), } From cdefd58e1243e460ecd929498ec70d163ef31a1c Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Thu, 8 Dec 2022 21:33:51 +0100 Subject: [PATCH 20/34] fix: requirements.txt bump cashu to 0.5.5 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 06e8642f7..d54714559 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ attrs==22.1.0 ; python_version >= "3.7" and python_version < "4.0" base58==2.1.1 ; python_version >= "3.7" and python_version < "4.0" bech32==1.2.0 ; python_version >= "3.7" and python_version < "4.0" bitstring==3.1.9 ; python_version >= "3.7" and python_version < "4.0" -cashu==0.5.4 ; python_version >= "3.7" and python_version < "4.0" +cashu==0.5.5 ; python_version >= "3.7" and python_version < "4.0" cerberus==1.3.4 ; python_version >= "3.7" and python_version < "4.0" certifi==2022.9.24 ; python_version >= "3.7" and python_version < "4.0" cffi==1.15.1 ; python_version >= "3.7" and python_version < "4.0" From a50dd80ad5a85f089426427c2d7b070c03401b0b Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Sun, 11 Dec 2022 00:59:07 +0100 Subject: [PATCH 21/34] feat: display mint name --- lnbits/db.py | 11 +++-------- lnbits/extensions/cashu/templates/cashu/wallet.html | 4 ++-- lnbits/extensions/cashu/views.py | 10 ++++++++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lnbits/db.py b/lnbits/db.py index e83b4bf84..25ee77806 100644 --- a/lnbits/db.py +++ b/lnbits/db.py @@ -125,6 +125,8 @@ class Database(Compat): import psycopg2 # type: ignore def _parse_timestamp(value, _): + if value is None: + return None f = "%Y-%m-%d %H:%M:%S.%f" if not "." in value: f = "%Y-%m-%d %H:%M:%S" @@ -149,14 +151,7 @@ class Database(Compat): psycopg2.extensions.register_type( psycopg2.extensions.new_type( - (1184, 1114), - "TIMESTAMP2INT", - _parse_timestamp - # lambda value, curs: time.mktime( - # datetime.datetime.strptime( - # value, "%Y-%m-%d %H:%M:%S.%f" - # ).timetuple() - # ), + (1184, 1114), "TIMESTAMP2INT", _parse_timestamp ) ) else: diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html index a133f5920..dcb7194b7 100644 --- a/lnbits/extensions/cashu/templates/cashu/wallet.html +++ b/lnbits/extensions/cashu/templates/cashu/wallet.html @@ -1,5 +1,5 @@ -{% extends "public.html" %} {% block toolbar_title %} {% raw %} {{name}} Cashu -{% endraw %} {% endblock %} {% block footer %}{% endblock %} {% block +{% extends "public.html" %} {% block toolbar_title %} {% raw %} Cashu {% endraw +%} - {{mint_name}} {% endblock %} {% block footer %}{% endblock %} {% block page_container %} diff --git a/lnbits/extensions/cashu/views.py b/lnbits/extensions/cashu/views.py index 0de791c40..1aab68bd1 100644 --- a/lnbits/extensions/cashu/views.py +++ b/lnbits/extensions/cashu/views.py @@ -27,11 +27,17 @@ async def index( @cashu_ext.get("/wallet") async def wallet(request: Request, mint_id: str): + cashu = await get_cashu(mint_id) + if not cashu: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." + ) return cashu_renderer().TemplateResponse( "cashu/wallet.html", { "request": request, "web_manifest": f"/cashu/manifest/{mint_id}.webmanifest", + "mint_name": cashu.name, }, ) @@ -41,7 +47,7 @@ async def cashu(request: Request, mintID): cashu = await get_cashu(mintID) if not cashu: raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist." + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." ) return cashu_renderer().TemplateResponse( "cashu/mint.html", @@ -54,7 +60,7 @@ async def manifest(cashu_id: str): cashu = await get_cashu(cashu_id) if not cashu: raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="TPoS does not exist." + status_code=HTTPStatus.NOT_FOUND, detail="Mint does not exist." ) return { From c949e8a299c4660588e95cb38252a81160c6c94d Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Sun, 11 Dec 2022 01:36:03 +0100 Subject: [PATCH 22/34] feat: add copy token button --- lnbits/extensions/cashu/templates/cashu/wallet.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lnbits/extensions/cashu/templates/cashu/wallet.html b/lnbits/extensions/cashu/templates/cashu/wallet.html index dcb7194b7..88dffe7c7 100644 --- a/lnbits/extensions/cashu/templates/cashu/wallet.html +++ b/lnbits/extensions/cashu/templates/cashu/wallet.html @@ -752,7 +752,13 @@ page_container %}
- Receive Tokens + Receive + Date: Mon, 12 Dec 2022 23:16:31 +0100 Subject: [PATCH 23/34] fix: log status check --- lnbits/extensions/cashu/views_api.py | 29 +++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/lnbits/extensions/cashu/views_api.py b/lnbits/extensions/cashu/views_api.py index ad253abf0..bf01dbff7 100644 --- a/lnbits/extensions/cashu/views_api.py +++ b/lnbits/extensions/cashu/views_api.py @@ -280,22 +280,33 @@ async def melt_coins( fees_msat = fee_reserve(invoice_obj.amount_msat) else: fees_msat = 0 - assert total_provided >= amount + fees_msat / 1000, Exception( + assert total_provided >= amount + math.ceil(fees_msat / 1000), Exception( f"Provided proofs ({total_provided} sats) not enough for Lightning payment ({amount + fees_msat} sats)." ) - + logger.debug(f"Cashu: Initiating payment of {total_provided} sats") await pay_invoice( wallet_id=cashu.wallet, payment_request=invoice, - description=f"pay cashu invoice", - extra={"tag": "cashu", "cahsu_name": cashu.name}, + description=f"Pay cashu invoice", + extra={"tag": "cashu", "cashu_name": cashu.name}, ) - status: PaymentStatus = await check_transaction_status( - cashu.wallet, invoice_obj.payment_hash - ) - if status.paid == True: + try: + logger.debug( + f"Cashu: Wallet {cashu.wallet} fetting PaymentStatus of {invoice_obj.payment_hash}" + ) + status: PaymentStatus = await check_transaction_status( + cashu.wallet, invoice_obj.payment_hash + ) + logger.debug(f"Cashu: Got status.paid: {status.paid}") + if status.paid == True: + logger.debug("Cashu: Payment successful, invalidating proofs") + await ledger._invalidate_proofs(proofs) + except Exception as e: + logger.error(e) + logger.error("Cashu: Error in payment status check, invalidating proofs") await ledger._invalidate_proofs(proofs) + return GetMeltResponse(paid=status.paid, preimage=status.preimage) @@ -333,7 +344,7 @@ async def check_fees( fees_msat = fee_reserve(invoice_obj.amount_msat) else: fees_msat = 0 - return CheckFeesResponse(fee=fees_msat / 1000) + return CheckFeesResponse(fee=math.ceil(fees_msat / 1000)) @cashu_ext.post("/api/v1/{cashu_id}/split") From 6ccc7833777a8af4902a3b194a38042fb415283d Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Tue, 13 Dec 2022 13:36:21 +0200 Subject: [PATCH 24/34] feat: store & show `webhook` response body --- lnbits/extensions/satspay/helpers.py | 6 +++- lnbits/extensions/satspay/static/js/utils.js | 1 + .../satspay/templates/satspay/index.html | 36 +++++++++++++++---- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/lnbits/extensions/satspay/helpers.py b/lnbits/extensions/satspay/helpers.py index 60c5ba4ab..b21a3ae29 100644 --- a/lnbits/extensions/satspay/helpers.py +++ b/lnbits/extensions/satspay/helpers.py @@ -37,7 +37,11 @@ async def call_webhook(charge: Charges): json=public_charge(charge), timeout=40, ) - return {"webhook_success": r.is_success, "webhook_message": r.reason_phrase} + return { + "webhook_success": r.is_success, + "webhook_message": r.reason_phrase, + "webhook_response": r.text, + } except Exception as e: logger.warning(f"Failed to call webhook for charge {charge.id}") logger.warning(e) diff --git a/lnbits/extensions/satspay/static/js/utils.js b/lnbits/extensions/satspay/static/js/utils.js index 2b1be8bdc..5317673f0 100644 --- a/lnbits/extensions/satspay/static/js/utils.js +++ b/lnbits/extensions/satspay/static/js/utils.js @@ -23,6 +23,7 @@ const mapCharge = (obj, oldObj = {}) => { charge.displayUrl = ['/satspay/', obj.id].join('') charge.expanded = oldObj.expanded || false charge.pendingBalance = oldObj.pendingBalance || 0 + charge.extra = charge.extra ? JSON.parse(charge.extra) : charge.extra return charge } diff --git a/lnbits/extensions/satspay/templates/satspay/index.html b/lnbits/extensions/satspay/templates/satspay/index.html index 602b1a288..2dda87924 100644 --- a/lnbits/extensions/satspay/templates/satspay/index.html +++ b/lnbits/extensions/satspay/templates/satspay/index.html @@ -227,7 +227,12 @@ >
- + {{props.row.webhook_message }}
@@ -528,6 +533,23 @@ + + + + + +
+ Close +
+
+
{% endblock %} {% block scripts %} {{ window_vars(user) }}