Browse Source

harden sse connections to backends a little.

atmext
fiatjaf 4 years ago
parent
commit
1d00060419
  1. 5
      docs/guide/wallets.md
  2. 78
      lnbits/wallets/lndgrpc.py
  3. 14
      lnbits/wallets/lndrest.py
  4. 8
      lnbits/wallets/spark.py

5
docs/guide/wallets.md

@ -52,9 +52,10 @@ Using this wallet requires the installation of the `lndgrpc` and `purerpc` Pytho
- `LNBITS_ENDPOINT`: e.g. https://lnbits.com - `LNBITS_ENDPOINT`: e.g. https://lnbits.com
- `LNBITS_KEY`: lnbitsAdminKey - `LNBITS_KEY`: lnbitsAdminKey
### LNPay ### LNPay
For the invoice listener to work you have a publicly accessible URL in your LNbits and must set up [LNPay webhooks](https://lnpay.co/webhook/) pointing to `<your LNbits host>/wallet/webhook` with the "Wallet Receive" event and no secret.
- `LNBITS_BACKEND_WALLET_CLASS`: **LNPayWallet** - `LNBITS_BACKEND_WALLET_CLASS`: **LNPayWallet**
- `LNPAY_API_ENDPOINT`: https://lnpay.co/v1/ - `LNPAY_API_ENDPOINT`: https://lnpay.co/v1/
- `LNPAY_API_KEY`: sak_apiKey - `LNPAY_API_KEY`: sak_apiKey
@ -70,6 +71,8 @@ Using this wallet requires the installation of the `lndgrpc` and `purerpc` Pytho
### OpenNode ### OpenNode
For the invoice to work you must have a publicly accessible URL in your LNbits. No manual webhook setting is necessary.
- `LNBITS_BACKEND_WALLET_CLASS`: **OpenNodeWallet** - `LNBITS_BACKEND_WALLET_CLASS`: **OpenNodeWallet**
- `OPENNODE_API_ENDPOINT`: https://api.opennode.com/ - `OPENNODE_API_ENDPOINT`: https://api.opennode.com/
- `OPENNODE_KEY`: opennodeAdminApiKey - `OPENNODE_KEY`: opennodeAdminApiKey

78
lnbits/wallets/lndgrpc.py

@ -18,6 +18,45 @@ from typing import Optional, Dict, AsyncGenerator
from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet from .base import InvoiceResponse, PaymentResponse, PaymentStatus, Wallet
def get_ssl_context(cert_path: str):
import ssl
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
context.options |= ssl.OP_NO_SSLv2
context.options |= ssl.OP_NO_SSLv3
context.options |= ssl.OP_NO_TLSv1
context.options |= ssl.OP_NO_TLSv1_1
context.options |= ssl.OP_NO_COMPRESSION
context.set_ciphers(
":".join(
[
"ECDHE+AESGCM",
"ECDHE+CHACHA20",
"DHE+AESGCM",
"DHE+CHACHA20",
"ECDH+AESGCM",
"DH+AESGCM",
"ECDH+AES",
"DH+AES",
"RSA+AESGCM",
"RSA+AES",
"!aNULL",
"!eNULL",
"!MD5",
"!DSS",
]
)
)
context.load_verify_locations(capath=cert_path)
return context
def load_macaroon(macaroon_path: str):
with open(macaroon_path, "rb") as f:
macaroon_bytes = f.read()
return macaroon_bytes.hex()
def parse_checking_id(checking_id: str) -> bytes: def parse_checking_id(checking_id: str) -> bytes:
return base64.b64decode( return base64.b64decode(
checking_id.replace("_", "/"), checking_id.replace("_", "/"),
@ -147,41 +186,4 @@ class LndWallet(Wallet):
checking_id = stringify_checking_id(inv.r_hash) checking_id = stringify_checking_id(inv.r_hash)
yield checking_id yield checking_id
print("lost connection to lnd InvoiceSubscription, please restart lnbits.")
def get_ssl_context(cert_path: str):
import ssl
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
context.options |= ssl.OP_NO_SSLv2
context.options |= ssl.OP_NO_SSLv3
context.options |= ssl.OP_NO_TLSv1
context.options |= ssl.OP_NO_TLSv1_1
context.options |= ssl.OP_NO_COMPRESSION
context.set_ciphers(
":".join(
[
"ECDHE+AESGCM",
"ECDHE+CHACHA20",
"DHE+AESGCM",
"DHE+CHACHA20",
"ECDH+AESGCM",
"DH+AESGCM",
"ECDH+AES",
"DH+AES",
"RSA+AESGCM",
"RSA+AES",
"!aNULL",
"!eNULL",
"!MD5",
"!DSS",
]
)
)
context.load_verify_locations(capath=cert_path)
return context
def load_macaroon(macaroon_path: str):
with open(macaroon_path, "rb") as f:
macaroon_bytes = f.read()
return macaroon_bytes.hex()

14
lnbits/wallets/lndrest.py

@ -1,3 +1,4 @@
import trio # type: ignore
import httpx import httpx
import json import json
import base64 import base64
@ -120,7 +121,13 @@ class LndRestWallet(Wallet):
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
url = self.endpoint + "/v1/invoices/subscribe" url = self.endpoint + "/v1/invoices/subscribe"
async with httpx.AsyncClient(timeout=None, headers=self.auth, verify=self.cert) as client: while True:
try:
async with httpx.AsyncClient(
timeout=None,
headers=self.auth,
verify=self.cert,
) as client:
async with client.stream("GET", url) as r: async with client.stream("GET", url) as r:
async for line in r.aiter_lines(): async for line in r.aiter_lines():
try: try:
@ -132,3 +139,8 @@ class LndRestWallet(Wallet):
payment_hash = base64.b64decode(inv["r_hash"]).hex() payment_hash = base64.b64decode(inv["r_hash"]).hex()
yield payment_hash yield payment_hash
except (OSError, httpx.ReadError):
pass
print("lost connection to lnd invoices stream, retrying in 5 seconds")
await trio.sleep(5)

8
lnbits/wallets/spark.py

@ -1,3 +1,4 @@
import trio # type: ignore
import random import random
import json import json
import httpx import httpx
@ -96,6 +97,8 @@ class SparkWallet(Wallet):
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]: async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
url = self.url + "/stream?access-key=" + self.token url = self.url + "/stream?access-key=" + self.token
while True:
try:
async with httpx.AsyncClient(timeout=None) as client: async with httpx.AsyncClient(timeout=None) as client:
async with client.stream("GET", url) as r: async with client.stream("GET", url) as r:
async for line in r.aiter_lines(): async for line in r.aiter_lines():
@ -103,3 +106,8 @@ class SparkWallet(Wallet):
data = json.loads(line[5:]) data = json.loads(line[5:])
if "pay_index" in data and data.get("status") == "paid": if "pay_index" in data and data.get("status") == "paid":
yield data["label"] yield data["label"]
except (OSError, httpx.ReadError):
pass
print("lost connection to spark /stream, retrying in 5 seconds")
await trio.sleep(5)

Loading…
Cancel
Save