Vlads comments

This commit is contained in:
Arc 2026-07-31 11:23:40 +01:00
parent 05b1d24d0d
commit 5cb7afe18a
5 changed files with 77 additions and 45 deletions

View file

@ -24,25 +24,6 @@ from .base import (
Wallet,
)
_PRE_DISPATCH_CREATE_SWAP_ERROR_CODES = {
StatusCode.INVALID_ARGUMENT,
StatusCode.PERMISSION_DENIED,
StatusCode.UNAUTHENTICATED,
}
_PRE_DISPATCH_CREATE_SWAP_ERROR_MESSAGES = (
"boltz error: could not find route to pay invoice",
)
def _is_pre_dispatch_create_swap_error(exc: AioRpcError) -> bool:
if exc.code() in _PRE_DISPATCH_CREATE_SWAP_ERROR_CODES:
return True
details = (exc.details() or "").lower()
return any(
message in details for message in _PRE_DISPATCH_CREATE_SWAP_ERROR_MESSAGES
)
class BoltzWallet(Wallet):
"""
@ -445,3 +426,23 @@ class BoltzWallet(Wallet):
except Exception as e:
logger.error(f"❌ Failed to create Boltz wallet: {e}")
_PRE_DISPATCH_CREATE_SWAP_ERROR_CODES = {
StatusCode.INVALID_ARGUMENT,
StatusCode.PERMISSION_DENIED,
StatusCode.UNAUTHENTICATED,
}
_PRE_DISPATCH_CREATE_SWAP_ERROR_MESSAGES = (
"boltz error: could not find route to pay invoice",
)
def _is_pre_dispatch_create_swap_error(exc: AioRpcError) -> bool:
if exc.code() in _PRE_DISPATCH_CREATE_SWAP_ERROR_CODES:
return True
details = (exc.details() or "").lower()
return any(
message in details for message in _PRE_DISPATCH_CREATE_SWAP_ERROR_MESSAGES
)

View file

@ -380,10 +380,11 @@ class CLNRestWallet(Wallet):
pay = pays_list[-1]
if pay["status"] == "complete":
status = pay.get("status")
if status == "complete":
fee_msat = pay["amount_sent_msat"] - pay["amount_msat"]
return PaymentSuccessStatus(fee_msat=fee_msat, preimage=pay["preimage"])
if pay["status"] == "failed":
if status == "failed":
return PaymentFailedStatus()
except Exception as exc:

View file

@ -82,24 +82,6 @@ def bytes_to_hex(b: bytes) -> str:
# error when we communicate with the lnd rpc server.
environ["GRPC_SSL_CIPHER_SUITES"] = "HIGH+ECDSA"
_PRE_DISPATCH_PAYMENT_ERROR_CODES = {
grpc.StatusCode.INVALID_ARGUMENT,
grpc.StatusCode.PERMISSION_DENIED,
grpc.StatusCode.UNAUTHENTICATED,
}
_PRE_DISPATCH_PAYMENT_ERROR_MESSAGES = (
"invoice not for current active network",
"invoice expired",
)
def _is_pre_dispatch_payment_error(exc: grpc.aio.AioRpcError) -> bool:
if exc.code() in _PRE_DISPATCH_PAYMENT_ERROR_CODES:
return True
details = (exc.details() or "").lower()
return any(message in details for message in _PRE_DISPATCH_PAYMENT_ERROR_MESSAGES)
class LndWallet(Wallet):
rpc: LightningStub
@ -402,3 +384,22 @@ class LndWallet(Wallet):
)
# If we reach here, the invoice was successfully canceled and payment failed
return InvoiceResponse(True, checking_id=payment_hash)
_PRE_DISPATCH_PAYMENT_ERROR_CODES = {
grpc.StatusCode.INVALID_ARGUMENT,
grpc.StatusCode.PERMISSION_DENIED,
grpc.StatusCode.UNAUTHENTICATED,
}
_PRE_DISPATCH_PAYMENT_ERROR_MESSAGES = (
"invoice not for current active network",
"invoice expired",
)
def _is_pre_dispatch_payment_error(exc: grpc.aio.AioRpcError) -> bool:
if exc.code() in _PRE_DISPATCH_PAYMENT_ERROR_CODES:
return True
details = (exc.details() or "").lower()
return any(message in details for message in _PRE_DISPATCH_PAYMENT_ERROR_MESSAGES)

View file

@ -278,8 +278,7 @@ class StrikeWallet(Wallet):
)
# Handle failed payment
failed_states = {"CANCELED", "FAILED", "TIMED_OUT"}
if state in failed_states:
if state == "FAILED":
logger.warning(
f"Strike payment {payment_id} failed with state: {state}"
)
@ -633,7 +632,7 @@ class StrikeWallet(Wallet):
if state in {"SUCCEEDED", "COMPLETED"}:
self.pending_payments.pop(checking_id, None)
return PaymentSuccessStatus(fee_msat=fee_msat, preimage=preimage)
if state in {"CANCELED", "FAILED", "TIMED_OUT"}:
if state == "FAILED":
self.pending_payments.pop(checking_id, None)
return PaymentFailedStatus()
@ -675,7 +674,7 @@ class StrikeWallet(Wallet):
if state in {"SUCCEEDED", "COMPLETED"}:
self.pending_payments.pop(checking_id, None)
return PaymentSuccessStatus(fee_msat=fee_msat, preimage=preimage)
if state in {"CANCELED", "FAILED", "TIMED_OUT"}:
if state == "FAILED":
self.pending_payments.pop(checking_id, None)
return PaymentFailedStatus()
@ -719,8 +718,10 @@ class StrikeWallet(Wallet):
"be a legacy invoice payment hash. Keeping pending."
)
return PaymentPendingStatus()
except ValueError:
pass
except ValueError as exc:
logger.warning(
f"Payment identifier '{checking_id}' is not valid hex: {exc}"
)
logger.warning(f"Payment {checking_id} not found. Marking as failed.")
self.pending_payments.pop(checking_id, None)
return PaymentFailedStatus()

View file

@ -845,6 +845,34 @@ async def test_strike_terminal_state_does_not_require_payment_id(
assert response.checking_id == "payment-hash"
@pytest.mark.anyio
@pytest.mark.parametrize("state", ["CANCELED", "TIMED_OUT", "UNKNOWN"])
async def test_strike_undocumented_payment_state_is_pending(
mocker: MockerFixture, state: str
):
wallet = object.__new__(StrikeWallet)
wallet.pending_payments = {}
mocker.patch(
"lnbits.wallets.strike.bolt11_decode",
return_value=SimpleNamespace(payment_hash="payment-hash"),
)
mocker.patch.object(
wallet,
"_create_payment_quote",
return_value=("quote-id", None),
)
mocker.patch.object(
wallet,
"_execute_payment_quote",
return_value=({"state": state, "paymentId": "payment-id"}, None),
)
response = await wallet.pay_invoice("bolt11", 1_000)
assert response.ok is None
assert response.checking_id == "payment-id"
@pytest.mark.anyio
async def test_strike_persisted_payment_hash_not_found_stays_pending(
mocker: MockerFixture,