Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
98900ac
chore: git move
motorina0 Apr 10, 2024
40443f5
chore: bring certs
motorina0 Apr 10, 2024
62a43f6
refactor: make interface uniform
motorina0 Apr 10, 2024
a81f9de
feat: skip by funding source
motorina0 Apr 10, 2024
7c061f6
chore: code format
motorina0 Apr 10, 2024
e980ba7
refactor: add more logs
motorina0 Apr 10, 2024
ae5408f
chore: fix `make check`
motorina0 Apr 10, 2024
9d5b8fc
chore: file renaming
motorina0 Apr 11, 2024
41f416e
chore: add `pytest_mock` test lib
motorina0 Apr 11, 2024
d07321d
refactor: extract `tests/wallets/helpers.py`
motorina0 Apr 11, 2024
25d0bc5
refactor: re-order stuff
motorina0 Apr 11, 2024
108fa72
refactor: extract more into `helpers.py`
motorina0 Apr 11, 2024
87eb87b
refactor: extract `check_assertions`
motorina0 Apr 11, 2024
4ce518a
chore: fix `make check`
motorina0 Apr 11, 2024
1ffea01
refactor: move files
motorina0 Apr 11, 2024
c96c18e
refactor: extract fixtures models
motorina0 Apr 11, 2024
fba15e2
refactor: smaller methods
motorina0 Apr 11, 2024
d03aa1c
refactor: simplify methods
motorina0 Apr 11, 2024
dd6e774
refactor: variable renaming
motorina0 Apr 11, 2024
6d0ef45
refactor: extract mehtod
motorina0 Apr 11, 2024
92c2248
refactor: give names to functions
motorina0 Apr 11, 2024
58f8abd
refactor: move more logic to models
motorina0 Apr 11, 2024
3b386f1
refactor: extract stuff
motorina0 Apr 11, 2024
1d78f55
refactor: more typings
motorina0 Apr 11, 2024
54b989d
refactor: more typings
motorina0 Apr 11, 2024
7dfb587
refactor: function renaming
motorina0 Apr 11, 2024
26c3c0c
chore: add the `mock` dev dependency
motorina0 Apr 11, 2024
9b18d38
Revert "chore: add the `mock` dev dependency"
motorina0 Apr 11, 2024
08830ef
chore: add dev dependency `types-mock`
motorina0 Apr 11, 2024
0ebe830
chore: add dev dependency `mock`
motorina0 Apr 12, 2024
9f877a7
fix: failing for the wrong reason
motorina0 Apr 12, 2024
05cd566
fix nix
dni Apr 15, 2024
0e40629
fix: log message
motorina0 Apr 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
pytest-md = prev.pytest-md.overridePythonAttrs (
old: { buildInputs = (old.buildInputs or []) ++ [ prev.setuptools ]; }
);
types-mock = prev.pytest-md.overridePythonAttrs (
old: { buildInputs = (old.buildInputs or []) ++ [ prev.setuptools ]; }
);
});
};
});
Expand Down
176 changes: 102 additions & 74 deletions lnbits/wallets/corelightning.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,19 @@ def __init__(self):
async def status(self) -> StatusResponse:
try:
funds: dict = self.ln.listfunds() # type: ignore
if len(funds) == 0:
return StatusResponse("no data", 0)

return StatusResponse(
None, sum([int(ch["our_amount_msat"]) for ch in funds["channels"]])
)
except RpcError as exc:
error_message = f"lightningd '{exc.method}' failed with '{exc.error}'."
logger.warning(exc)
error_message = f"RPC '{exc.method}' failed with '{exc.error}'."
return StatusResponse(error_message, 0)
except Exception as exc:
logger.warning(f"Failed to connect, got: '{exc}'")
return StatusResponse(f"Unable to connect, got: '{exc}'", 0)

async def create_invoice(
self,
Expand All @@ -69,7 +76,7 @@ async def create_invoice(
unhashed_description: Optional[bytes] = None,
**kwargs,
) -> InvoiceResponse:
label = f"lbl{random.random()}"
label = kwargs.get("label", f"lbl{random.random()}")
msat: int = int(amount * 1000)
try:
if description_hash and not unhashed_description:
Expand All @@ -95,14 +102,18 @@ async def create_invoice(
if r.get("code") and r.get("code") < 0: # type: ignore
raise Exception(r.get("message"))

return InvoiceResponse(True, r["payment_hash"], r["bolt11"], "")
return InvoiceResponse(True, r["payment_hash"], r["bolt11"], None)
except RpcError as exc:
error_message = (
f"CoreLightning method '{exc.method}' failed with"
f" '{exc.error.get('message') or exc.error}'." # type: ignore
)
logger.warning(exc)
error_message = f"RPC '{exc.method}' failed with '{exc.error}'."
return InvoiceResponse(False, None, None, error_message)
except KeyError as exc:
logger.warning(exc)
return InvoiceResponse(
False, None, None, "Server error: 'missing required fields'"
)
except Exception as e:
logger.warning(e)
return InvoiceResponse(False, None, None, str(e))

async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
Expand All @@ -111,94 +122,111 @@ async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse
except Bolt11Exception as exc:
return PaymentResponse(False, None, None, None, str(exc))

previous_payment = await self.get_payment_status(invoice.payment_hash)
if previous_payment.paid:
return PaymentResponse(False, None, None, None, "invoice already paid")
try:
previous_payment = await self.get_payment_status(invoice.payment_hash)
if previous_payment.paid:
return PaymentResponse(False, None, None, None, "invoice already paid")

if not invoice.amount_msat or invoice.amount_msat <= 0:
return PaymentResponse(
False, None, None, None, "CLN 0 amount invoice not supported"
)
if not invoice.amount_msat or invoice.amount_msat <= 0:
return PaymentResponse(
False, None, None, None, "CLN 0 amount invoice not supported"
)

fee_limit_percent = fee_limit_msat / invoice.amount_msat * 100
# so fee_limit_percent is applied even
# on payments with fee < 5000 millisatoshi
# (which is default value of exemptfee)
payload = {
"bolt11": bolt11,
"maxfeepercent": f"{fee_limit_percent:.11}",
"exemptfee": 0,
# so fee_limit_percent is applied even on payments with fee < 5000
# millisatoshi (which is default value of exemptfee)
"description": invoice.description,
}

fee_limit_percent = fee_limit_msat / invoice.amount_msat * 100
# so fee_limit_percent is applied even on payments with fee < 5000 millisatoshi
# (which is default value of exemptfee)
payload = {
"bolt11": bolt11,
"maxfeepercent": f"{fee_limit_percent:.11}",
"exemptfee": 0,
# so fee_limit_percent is applied even on payments with fee < 5000
# millisatoshi (which is default value of exemptfee)
"description": invoice.description,
}
try:
r = await run_sync(lambda: self.ln.call("pay", payload))

fee_msat = -int(r["amount_sent_msat"] - r["amount_msat"])
return PaymentResponse(
True, r["payment_hash"], fee_msat, r["payment_preimage"], None
)
except RpcError as exc:
logger.warning(exc)
try:
error_message = exc.error["attempts"][-1]["fail_reason"] # type: ignore
except Exception:
error_message = (
f"CoreLightning method '{exc.method}' failed with"
f" '{exc.error.get('message') or exc.error}'." # type: ignore
)
error_message = f"RPC '{exc.method}' failed with '{exc.error}'."
return PaymentResponse(False, None, None, None, error_message)

fee_msat = -int(r["amount_sent_msat"] - r["amount_msat"])
return PaymentResponse(
True, r["payment_hash"], fee_msat, r["payment_preimage"], None
)
except KeyError as exc:
logger.warning(exc)
return PaymentResponse(
False, None, None, None, "Server error: 'missing required fields'"
)
except Exception as exc:
logger.info(f"Failed to pay invoice {bolt11}")
logger.warning(exc)
return PaymentResponse(False, None, None, None, f"Payment failed: '{exc}'.")

async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
try:
r: dict = self.ln.listinvoices(payment_hash=checking_id) # type: ignore
except RpcError:
return PaymentPendingStatus()
if not r["invoices"]:
return PaymentPendingStatus()

invoice_resp = r["invoices"][-1]

if invoice_resp["payment_hash"] == checking_id:
if invoice_resp["status"] == "paid":
return PaymentSuccessStatus()
elif invoice_resp["status"] == "unpaid":
if not r["invoices"]:
return PaymentPendingStatus()
elif invoice_resp["status"] == "expired":
return PaymentFailedStatus()
else:
logger.warning(f"supplied an invalid checking_id: {checking_id}")
return PaymentPendingStatus()

async def get_payment_status(self, checking_id: str) -> PaymentStatus:
try:
r: dict = self.ln.listpays(payment_hash=checking_id) # type: ignore
except Exception:
invoice_resp = r["invoices"][-1]

if invoice_resp["payment_hash"] == checking_id:
if invoice_resp["status"] == "paid":
return PaymentSuccessStatus()
elif invoice_resp["status"] == "unpaid":
return PaymentPendingStatus()
elif invoice_resp["status"] == "expired":
return PaymentFailedStatus()
else:
logger.warning(f"supplied an invalid checking_id: {checking_id}")
return PaymentPendingStatus()
if "pays" not in r:
except RpcError as exc:
logger.warning(exc)
return PaymentPendingStatus()
except Exception as exc:
logger.warning(exc)
return PaymentPendingStatus()
if not r["pays"]:
# no payment with this payment_hash is found
return PaymentFailedStatus()

payment_resp = r["pays"][-1]

if payment_resp["payment_hash"] == checking_id:
status = payment_resp["status"]
if status == "complete":
fee_msat = -int(
payment_resp["amount_sent_msat"] - payment_resp["amount_msat"]
)
async def get_payment_status(self, checking_id: str) -> PaymentStatus:
try:
r: dict = self.ln.listpays(payment_hash=checking_id) # type: ignore

return PaymentSuccessStatus(
fee_msat=fee_msat, preimage=payment_resp["preimage"]
)
elif status == "failed":
if "pays" not in r:
return PaymentPendingStatus()
if not r["pays"]:
# no payment with this payment_hash is found
return PaymentFailedStatus()

payment_resp = r["pays"][-1]

if payment_resp["payment_hash"] == checking_id:
status = payment_resp["status"]
if status == "complete":
fee_msat = -int(
payment_resp["amount_sent_msat"] - payment_resp["amount_msat"]
)

return PaymentSuccessStatus(
fee_msat=fee_msat, preimage=payment_resp["preimage"]
)
elif status == "failed":
return PaymentFailedStatus()
else:
return PaymentPendingStatus()
else:
return PaymentPendingStatus()
else:
logger.warning(f"supplied an invalid checking_id: {checking_id}")
return PaymentPendingStatus()
logger.warning(f"supplied an invalid checking_id: {checking_id}")
return PaymentPendingStatus()

except Exception as exc:
logger.warning(exc)
return PaymentPendingStatus()

async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
while True:
Expand Down
46 changes: 45 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ json5 = "^0.9.17"
asgi-lifespan = "^2.1.0"
pytest-md = "^0.2.0"
pytest-httpserver = "^1.0.10"
pytest-mock = "^3.14.0"
types-mock = "^5.1.0.20240311"
mock = "^5.1.0"

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
Loading