simplify environment variables required.

instead of multiple keys/macaroons with different permissions we request only one.
if someone wants to use lnbits with an invoice macaroon they're free to do it and
we will just fail on 'pay' methods, as before.

this also grandfathers the previous environment variables names so everything keeps
working without people having to change their setups.

in the meantime some bugs with lntxbot and c-lightning were fixed and the `requests`
dependency was eliminated because I can't organize myself into meaningful chunks of
changes.
This commit is contained in:
fiatjaf
2020-10-08 16:03:21 -03:00
parent fc6e49b821
commit 9185342c72
15 changed files with 145 additions and 157 deletions
+1 -1
View File
@@ -73,7 +73,7 @@ class CLightningWallet(Wallet):
raise KeyError("supplied an invalid checking_id")
def get_payment_status(self, checking_id: str) -> PaymentStatus:
r = self.ln.listpays(payment_hash=checking_id)
r = self.ln.call("listpays", {"payment_hash": checking_id})
if not r["pays"]:
return PaymentStatus(False)
if r["pays"][0]["payment_hash"] == checking_id:
+19 -18
View File
@@ -1,7 +1,7 @@
import trio # type: ignore
import httpx
from os import getenv
from typing import Optional, Dict, AsyncGenerator
from requests import get, post
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet
@@ -11,8 +11,9 @@ class LNbitsWallet(Wallet):
def __init__(self):
self.endpoint = getenv("LNBITS_ENDPOINT")
self.auth_admin = {"X-Api-Key": getenv("LNBITS_ADMIN_KEY")}
self.auth_invoice = {"X-Api-Key": getenv("LNBITS_INVOICE_KEY")}
key = getenv("LNBITS_KEY") or getenv("LNBITS_ADMIN_KEY") or getenv("LNBITS_INVOICE_KEY")
self.key = {"X-Api-Key": key}
def create_invoice(
self, amount: int, memo: Optional[str] = None, description_hash: Optional[bytes] = None
@@ -23,45 +24,45 @@ class LNbitsWallet(Wallet):
else:
data["memo"] = memo or ""
r = post(
r = httpx.post(
url=f"{self.endpoint}/api/v1/payments",
headers=self.auth_invoice,
headers=self.key,
json=data,
)
ok, checking_id, payment_request, error_message = r.ok, None, None, None
ok, checking_id, payment_request, error_message = not r.is_error, None, None, None
if r.ok:
if r.is_error:
error_message = r.json()["message"]
else:
data = r.json()
checking_id, payment_request = data["checking_id"], data["payment_request"]
else:
error_message = r.json()["message"]
return InvoiceResponse(ok, checking_id, payment_request, error_message)
def pay_invoice(self, bolt11: str) -> PaymentResponse:
r = post(url=f"{self.endpoint}/api/v1/payments", headers=self.auth_admin, json={"out": True, "bolt11": bolt11})
ok, checking_id, fee_msat, error_message = True, None, 0, None
r = httpx.post(url=f"{self.endpoint}/api/v1/payments", headers=self.key, json={"out": True, "bolt11": bolt11})
ok, checking_id, fee_msat, error_message = not r.is_error, None, 0, None
if r.ok:
if r.is_error:
error_message = r.json()["message"]
else:
data = r.json()
checking_id = data["checking_id"]
else:
error_message = r.json()["message"]
return PaymentResponse(ok, checking_id, fee_msat, error_message)
def get_invoice_status(self, checking_id: str) -> PaymentStatus:
r = get(url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.auth_invoice)
r = httpx.get(url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.key)
if not r.ok:
if r.is_error:
return PaymentStatus(None)
return PaymentStatus(r.json()["paid"])
def get_payment_status(self, checking_id: str) -> PaymentStatus:
r = get(url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.auth_invoice)
r = httpx.get(url=f"{self.endpoint}/api/v1/payments/{checking_id}", headers=self.key)
if not r.ok:
if r.is_error:
return PaymentStatus(None)
return PaymentStatus(r.json()["paid"])
+11 -5
View File
@@ -46,22 +46,28 @@ class LndWallet(Wallet):
self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
self.port = int(getenv("LND_GRPC_PORT"))
self.cert_path = getenv("LND_GRPC_CERT") or getenv("LND_CERT")
self.auth_admin = getenv("LND_GRPC_ADMIN_MACAROON") or getenv("LND_ADMIN_MACAROON")
self.auth_invoices = getenv("LND_GRPC_INVOICE_MACAROON") or getenv("LND_INVOICE_MACAROON")
self.macaroon_path = (
getenv("LND_GRPC_MACAROON")
or getenv("LND_GRPC_ADMIN_MACAROON")
or getenv("LND_ADMIN_MACAROON")
or getenv("LND_GRPC_INVOICE_MACAROON")
or getenv("LND_INVOICE_MACAROON")
)
network = getenv("LND_GRPC_NETWORK", "mainnet")
self.admin_rpc = lndgrpc.LNDClient(
f"{self.endpoint}:{self.port}",
cert_filepath=self.cert_path,
network=network,
macaroon_filepath=self.auth_admin,
macaroon_filepath=self.macaroon_path,
)
self.invoices_rpc = lndgrpc.LNDClient(
f"{self.endpoint}:{self.port}",
cert_filepath=self.cert_path,
network=network,
macaroon_filepath=self.auth_invoices,
macaroon_filepath=self.macaroon_path,
)
def create_invoice(
@@ -129,7 +135,7 @@ class LndWallet(Wallet):
ln.Invoice,
),
)
macaroon = load_macaroon(self.auth_admin)
macaroon = load_macaroon(self.macaroon_path)
async for inv in subscribe_invoices(
ln.InvoiceSubscription(),
+18 -17
View File
@@ -11,19 +11,20 @@ class LndRestWallet(Wallet):
"""https://api.lightning.community/rest/index.html#lnd-rest-api-reference"""
def __init__(self):
endpoint = getenv("LND_REST_ENDPOINT")
endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
endpoint = "https://" + endpoint if not endpoint.startswith("http") else endpoint
self.endpoint = endpoint
self.auth_admin = {
"Grpc-Metadata-macaroon": getenv("LND_ADMIN_MACAROON") or getenv("LND_REST_ADMIN_MACAROON"),
}
self.auth_invoice = {
"Grpc-Metadata-macaroon": getenv("LND_INVOICE_MACAROON") or getenv("LND_REST_INVOICE_MACAROON")
}
self.auth_cert = getenv("LND_REST_CERT")
macaroon = (
getenv("LND_MACAROON")
or getenv("LND_ADMIN_MACAROON")
or getenv("LND_REST_ADMIN_MACAROON")
or getenv("LND_INVOICE_MACAROON")
or getenv("LND_REST_INVOICE_MACAROON")
)
self.auth = {"Grpc-Metadata-macaroon": macaroon}
self.cert = getenv("LND_REST_CERT")
def create_invoice(
self, amount: int, memo: Optional[str] = None, description_hash: Optional[bytes] = None
@@ -39,8 +40,8 @@ class LndRestWallet(Wallet):
r = httpx.post(
url=f"{self.endpoint}/v1/invoices",
headers=self.auth_invoice,
verify=self.auth_cert,
headers=self.auth,
verify=self.cert,
json=data,
)
@@ -62,8 +63,8 @@ class LndRestWallet(Wallet):
def pay_invoice(self, bolt11: str) -> PaymentResponse:
r = httpx.post(
url=f"{self.endpoint}/v1/channels/transactions",
headers=self.auth_admin,
verify=self.auth_cert,
headers=self.auth,
verify=self.cert,
json={"payment_request": bolt11},
)
@@ -84,8 +85,8 @@ class LndRestWallet(Wallet):
checking_id = checking_id.replace("_", "/")
r = httpx.get(
url=f"{self.endpoint}/v1/invoice/{checking_id}",
headers=self.auth_invoice,
verify=self.auth_cert,
headers=self.auth,
verify=self.cert,
)
if r.is_error or not r.json().get("settled"):
@@ -98,8 +99,8 @@ class LndRestWallet(Wallet):
def get_payment_status(self, checking_id: str) -> PaymentStatus:
r = httpx.get(
url=f"{self.endpoint}/v1/payments",
headers=self.auth_admin,
verify=self.auth_cert,
headers=self.auth,
verify=self.cert,
params={"include_incomplete": "True", "max_payments": "20"},
)
@@ -118,7 +119,7 @@ class LndRestWallet(Wallet):
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
url = self.endpoint + "/v1/invoices/subscribe"
async with httpx.AsyncClient(timeout=None, headers=self.auth_admin, verify=self.auth_cert) as client:
async with httpx.AsyncClient(timeout=None, headers=self.auth, verify=self.cert) as client:
async with client.stream("GET", url) as r:
async for line in r.aiter_lines():
try:
+8 -8
View File
@@ -15,8 +15,8 @@ class LNPayWallet(Wallet):
def __init__(self):
endpoint = getenv("LNPAY_API_ENDPOINT", "https://lnpay.co/v1")
self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
self.auth_admin = getenv("LNPAY_ADMIN_KEY")
self.auth_api = {"X-Api-Key": getenv("LNPAY_API_KEY")}
self.wallet_key = getenv("LNPAY_WALLET_KEY") or getenv("LNPAY_ADMIN_KEY")
self.auth = {"X-Api-Key": getenv("LNPAY_API_KEY")}
def create_invoice(
self,
@@ -31,8 +31,8 @@ class LNPayWallet(Wallet):
data["memo"] = memo or ""
r = httpx.post(
url=f"{self.endpoint}/user/wallet/{self.auth_admin}/invoice",
headers=self.auth_api,
url=f"{self.endpoint}/user/wallet/{self.wallet_key}/invoice",
headers=self.auth,
json=data,
)
ok, checking_id, payment_request, error_message = (
@@ -50,8 +50,8 @@ class LNPayWallet(Wallet):
def pay_invoice(self, bolt11: str) -> PaymentResponse:
r = httpx.post(
url=f"{self.endpoint}/user/wallet/{self.auth_admin}/withdraw",
headers=self.auth_api,
url=f"{self.endpoint}/user/wallet/{self.wallet_key}/withdraw",
headers=self.auth,
json={"payment_request": bolt11},
)
ok, checking_id, fee_msat, error_message = r.status_code == 201, None, 0, None
@@ -67,7 +67,7 @@ class LNPayWallet(Wallet):
def get_payment_status(self, checking_id: str) -> PaymentStatus:
r = httpx.get(
url=f"{self.endpoint}/user/lntx/{checking_id}?fields=settled",
headers=self.auth_api,
headers=self.auth,
)
if r.is_error:
@@ -91,7 +91,7 @@ class LNPayWallet(Wallet):
async with httpx.AsyncClient() as client:
r = await client.get(
f"{self.endpoint}/user/lntx/{lntx_id}?fields=settled",
headers=self.auth_api,
headers=self.auth,
)
data = r.json()
if data["settled"]:
+41 -36
View File
@@ -1,7 +1,7 @@
import trio # type: ignore
import httpx
from os import getenv
from typing import Optional, Dict, AsyncGenerator
from requests import post
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet
@@ -12,8 +12,9 @@ class LntxbotWallet(Wallet):
def __init__(self):
endpoint = getenv("LNTXBOT_API_ENDPOINT")
self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
self.auth_admin = {"Authorization": f"Basic {getenv('LNTXBOT_ADMIN_KEY')}"}
self.auth_invoice = {"Authorization": f"Basic {getenv('LNTXBOT_INVOICE_KEY')}"}
key = getenv("LNTXBOT_KEY") or getenv("LNTXBOT_ADMIN_KEY") or getenv("LNTXBOT_INVOICE_KEY")
self.auth = {"Authorization": f"Basic {key}"}
def create_invoice(
self, amount: int, memo: Optional[str] = None, description_hash: Optional[bytes] = None
@@ -24,44 +25,47 @@ class LntxbotWallet(Wallet):
else:
data["memo"] = memo or ""
r = post(
r = httpx.post(
url=f"{self.endpoint}/addinvoice",
headers=self.auth_invoice,
headers=self.auth,
json=data,
)
ok, checking_id, payment_request, error_message = r.ok, None, None, None
if r.ok:
data = r.json()
checking_id, payment_request = data["payment_hash"], data["pay_req"]
if "error" in data and data["error"]:
ok = False
if r.is_error:
try:
data = r.json()
error_message = data["message"]
except:
error_message = r.text
pass
return InvoiceResponse(ok, checking_id, payment_request, error_message)
def pay_invoice(self, bolt11: str) -> PaymentResponse:
r = post(url=f"{self.endpoint}/payinvoice", headers=self.auth_admin, json={"invoice": bolt11})
ok, checking_id, fee_msat, error_message = r.ok, None, 0, None
if r.ok:
data = r.json()
if "payment_hash" in data:
checking_id, fee_msat = data["decoded"]["payment_hash"], data["fee_msat"]
elif "error" in data and data["error"]:
ok, error_message = False, data["message"]
return PaymentResponse(ok, checking_id, fee_msat, error_message)
def get_invoice_status(self, checking_id: str) -> PaymentStatus:
r = post(url=f"{self.endpoint}/invoicestatus/{checking_id}?wait=false", headers=self.auth_invoice)
if not r.ok or "error" in r.json():
return PaymentStatus(None)
return InvoiceResponse(False, None, None, error_message)
data = r.json()
return InvoiceResponse(True, data["payment_hash"], data["pay_req"], None)
def pay_invoice(self, bolt11: str) -> PaymentResponse:
r = httpx.post(url=f"{self.endpoint}/payinvoice", headers=self.auth, json={"invoice": bolt11})
if r.is_error:
try:
data = r.json()
error_message = data["message"]
except:
error_message = r.text
pass
return PaymentResponse(False, None, 0, error_message)
data = r.json()
return PaymentResponse(True, data["decoded"]["payment_hash"], data["fee_msat"], None)
def get_invoice_status(self, checking_id: str) -> PaymentStatus:
r = httpx.post(url=f"{self.endpoint}/invoicestatus/{checking_id}?wait=false", headers=self.auth)
data = r.json()
if r.is_error or "error" in data:
return PaymentStatus(None)
if "preimage" not in data:
return PaymentStatus(False)
@@ -69,13 +73,14 @@ class LntxbotWallet(Wallet):
return PaymentStatus(True)
def get_payment_status(self, checking_id: str) -> PaymentStatus:
r = post(url=f"{self.endpoint}/paymentstatus/{checking_id}", headers=self.auth_invoice)
r = httpx.post(url=f"{self.endpoint}/paymentstatus/{checking_id}", headers=self.auth)
if not r.ok or "error" in r.json():
data = r.json()
if r.is_error or "error" in data:
return PaymentStatus(None)
statuses = {"complete": True, "failed": False, "pending": None, "unknown": None}
return PaymentStatus(statuses[r.json().get("status", "unknown")])
return PaymentStatus(statuses[data.get("status", "unknown")])
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
print("lntxbot does not support paid invoices stream yet")
+5 -6
View File
@@ -16,8 +16,9 @@ class OpenNodeWallet(Wallet):
def __init__(self):
endpoint = getenv("OPENNODE_API_ENDPOINT")
self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
self.auth_admin = {"Authorization": getenv("OPENNODE_ADMIN_KEY")}
self.auth_invoice = {"Authorization": getenv("OPENNODE_INVOICE_KEY")}
key = getenv("OPENNODE_KEY") or getenv("OPENNODE_ADMIN_KEY") or getenv("OPENNODE_INVOICE_KEY")
self.auth = {"Authorization": key}
def create_invoice(
self, amount: int, memo: Optional[str] = None, description_hash: Optional[bytes] = None
@@ -45,9 +46,7 @@ class OpenNodeWallet(Wallet):
return InvoiceResponse(True, checking_id, payment_request, None)
def pay_invoice(self, bolt11: str) -> PaymentResponse:
r = httpx.post(
f"{self.endpoint}/v2/withdrawals", headers=self.auth_admin, json={"type": "ln", "address": bolt11}
)
r = httpx.post(f"{self.endpoint}/v2/withdrawals", headers=self.auth, json={"type": "ln", "address": bolt11})
if r.is_error:
error_message = r.json()["message"]
@@ -68,7 +67,7 @@ class OpenNodeWallet(Wallet):
return PaymentStatus(statuses[r.json()["data"]["status"]])
def get_payment_status(self, checking_id: str) -> PaymentStatus:
r = httpx.get(f"{self.endpoint}/v1/withdrawal/{checking_id}", headers=self.auth_admin)
r = httpx.get(f"{self.endpoint}/v1/withdrawal/{checking_id}", headers=self.auth)
if r.is_error:
return PaymentStatus(None)