mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-03-03 17:37:06 +01:00
make lndgrpc work using the purerpc library and a manually-declared method.
This commit is contained in:
parent
9994e61615
commit
f5b8ed8fc6
6 changed files with 94 additions and 27 deletions
|
@ -34,7 +34,7 @@ You will need to copy `.env.example` to `.env`, then set variables there.
|
||||||

|

|
||||||
|
|
||||||
You might also need to install additional packages, depending on the [backend wallet](../guide/wallets.md) you use.
|
You might also need to install additional packages, depending on the [backend wallet](../guide/wallets.md) you use.
|
||||||
E.g. when you want to use LND you have to `pipenv run pip install lndgrpc`.
|
E.g. when you want to use LND you have to `pipenv run pip install lndgrpc` and `pipenv run pip install pureprc`.
|
||||||
|
|
||||||
Take a look at [Polar][polar] for an excellent way of spinning up a Lightning Network dev environment.
|
Take a look at [Polar][polar] for an excellent way of spinning up a Lightning Network dev environment.
|
||||||
|
|
||||||
|
|
|
@ -32,4 +32,5 @@ E.g. when you want to use LND you have to run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./venv/bin/pip install lndgrpc
|
./venv/bin/pip install lndgrpc
|
||||||
|
./venv/bin/pip install purerpc
|
||||||
```
|
```
|
||||||
|
|
|
@ -29,7 +29,7 @@ Using this wallet requires the installation of the `pylightning` Python package.
|
||||||
|
|
||||||
### LND (gRPC)
|
### LND (gRPC)
|
||||||
|
|
||||||
Using this wallet requires the installation of the `lndgrpc` Python package.
|
Using this wallet requires the installation of the `lndgrpc` and `purerpc` Python packages.
|
||||||
|
|
||||||
- `LNBITS_BACKEND_WALLET_CLASS`: **LndWallet**
|
- `LNBITS_BACKEND_WALLET_CLASS`: **LndWallet**
|
||||||
- `LND_GRPC_ENDPOINT`: ip_address
|
- `LND_GRPC_ENDPOINT`: ip_address
|
||||||
|
|
|
@ -4,6 +4,12 @@ try:
|
||||||
except ImportError: # pragma: nocover
|
except ImportError: # pragma: nocover
|
||||||
lndgrpc = None
|
lndgrpc = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
import purerpc # type: ignore
|
||||||
|
except ImportError: # pragma: nocover
|
||||||
|
purerpc = None
|
||||||
|
|
||||||
|
import trio # type: ignore
|
||||||
import binascii
|
import binascii
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
|
@ -34,33 +40,31 @@ class LndWallet(Wallet):
|
||||||
if lndgrpc is None: # pragma: nocover
|
if lndgrpc is None: # pragma: nocover
|
||||||
raise ImportError("The `lndgrpc` library must be installed to use `LndWallet`.")
|
raise ImportError("The `lndgrpc` library must be installed to use `LndWallet`.")
|
||||||
|
|
||||||
|
if purerpc is None:
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
warnings.warn("To enable invoices subscription on `LndWallet` the `purerpc` library must be nistalled.")
|
||||||
|
|
||||||
endpoint = getenv("LND_GRPC_ENDPOINT")
|
endpoint = getenv("LND_GRPC_ENDPOINT")
|
||||||
endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
|
self.endpoint = endpoint[:-1] if endpoint.endswith("/") else endpoint
|
||||||
port = getenv("LND_GRPC_PORT")
|
self.port = int(getenv("LND_GRPC_PORT"))
|
||||||
cert = getenv("LND_GRPC_CERT") or getenv("LND_CERT")
|
self.cert_path = getenv("LND_GRPC_CERT") or getenv("LND_CERT")
|
||||||
auth_admin = getenv("LND_GRPC_ADMIN_MACAROON") or getenv("LND_ADMIN_MACAROON")
|
self.auth_admin = getenv("LND_GRPC_ADMIN_MACAROON") or getenv("LND_ADMIN_MACAROON")
|
||||||
auth_invoices = getenv("LND_GRPC_INVOICE_MACAROON") or getenv("LND_INVOICE_MACAROON")
|
self.auth_invoices = getenv("LND_GRPC_INVOICE_MACAROON") or getenv("LND_INVOICE_MACAROON")
|
||||||
network = getenv("LND_GRPC_NETWORK", "mainnet")
|
network = getenv("LND_GRPC_NETWORK", "mainnet")
|
||||||
|
|
||||||
self.admin_rpc = lndgrpc.LNDClient(
|
self.admin_rpc = lndgrpc.LNDClient(
|
||||||
endpoint + ":" + port,
|
f"{self.endpoint}:{self.port}",
|
||||||
cert_filepath=cert,
|
cert_filepath=self.cert_path,
|
||||||
network=network,
|
network=network,
|
||||||
macaroon_filepath=auth_admin,
|
macaroon_filepath=self.auth_admin,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.invoices_rpc = lndgrpc.LNDClient(
|
self.invoices_rpc = lndgrpc.LNDClient(
|
||||||
endpoint + ":" + port,
|
f"{self.endpoint}:{self.port}",
|
||||||
cert_filepath=cert,
|
cert_filepath=self.cert_path,
|
||||||
network=network,
|
network=network,
|
||||||
macaroon_filepath=auth_invoices,
|
macaroon_filepath=self.auth_invoices,
|
||||||
)
|
|
||||||
|
|
||||||
self.async_rpc = lndgrpc.AsyncLNDClient(
|
|
||||||
endpoint + ":" + port,
|
|
||||||
cert_filepath=cert,
|
|
||||||
network=network,
|
|
||||||
macaroon_filepath=auth_invoices,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_invoice(
|
def create_invoice(
|
||||||
|
@ -114,9 +118,71 @@ class LndWallet(Wallet):
|
||||||
return PaymentStatus(True)
|
return PaymentStatus(True)
|
||||||
|
|
||||||
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
||||||
async for inv in self.async_rpc._ln_stub.SubscribeInvoices(ln.InvoiceSubscription()):
|
if not purerpc:
|
||||||
|
trio.sleep(5)
|
||||||
|
yield ""
|
||||||
|
|
||||||
|
async with purerpc.secure_channel(
|
||||||
|
self.endpoint,
|
||||||
|
self.port,
|
||||||
|
get_ssl_context(self.cert_path),
|
||||||
|
) as channel:
|
||||||
|
client = purerpc.Client("lnrpc.Lightning", channel)
|
||||||
|
subscribe_invoices = client.get_method_stub(
|
||||||
|
"SubscribeInvoices",
|
||||||
|
purerpc.RPCSignature(
|
||||||
|
purerpc.Cardinality.UNARY_STREAM,
|
||||||
|
ln.InvoiceSubscription,
|
||||||
|
ln.Invoice,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
macaroon = load_macaroon(self.auth_admin)
|
||||||
|
|
||||||
|
async for inv in subscribe_invoices(
|
||||||
|
ln.InvoiceSubscription(),
|
||||||
|
metadata=[("macaroon", macaroon)],
|
||||||
|
):
|
||||||
if not inv.settled:
|
if not inv.settled:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
checking_id = stringify_checking_id(inv.r_hash)
|
checking_id = stringify_checking_id(inv.r_hash)
|
||||||
yield checking_id
|
yield checking_id
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
|
@ -95,6 +95,6 @@ class LNPayWallet(Wallet):
|
||||||
)
|
)
|
||||||
data = r.json()
|
data = r.json()
|
||||||
if data["settled"]:
|
if data["settled"]:
|
||||||
self.send.send(lntx_id)
|
await self.send.send(lntx_id)
|
||||||
|
|
||||||
return "", HTTPStatus.NO_CONTENT
|
return "", HTTPStatus.NO_CONTENT
|
||||||
|
|
|
@ -97,5 +97,5 @@ class OpenNodeWallet(Wallet):
|
||||||
print("invalid webhook, not from opennode")
|
print("invalid webhook, not from opennode")
|
||||||
return "", HTTPStatus.NO_CONTENT
|
return "", HTTPStatus.NO_CONTENT
|
||||||
|
|
||||||
self.send.send(charge_id)
|
await self.send.send(charge_id)
|
||||||
return "", HTTPStatus.NO_CONTENT
|
return "", HTTPStatus.NO_CONTENT
|
||||||
|
|
Loading…
Add table
Reference in a new issue