2023-03-26 20:53:50 +02:00
|
|
|
import grpc
|
2023-05-17 16:02:28 +02:00
|
|
|
from fastapi import HTTPException, status
|
2023-05-01 20:48:10 +02:00
|
|
|
from loguru import logger
|
|
|
|
|
|
|
|
|
|
from app.lightning.exceptions import NodeNotFoundError
|
2023-03-26 20:53:50 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def generic_grpc_error_handler(error: grpc.aio._call.AioRpcError):
|
|
|
|
|
details = error.details()
|
|
|
|
|
logger.debug(details)
|
|
|
|
|
|
|
|
|
|
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, detail=details)
|
2023-05-01 20:48:10 +02:00
|
|
|
|
|
|
|
|
|
2026-07-11 12:43:31 +02:00
|
|
|
# Substrings (matched case-insensitively) that indicate the backend failed to
|
|
|
|
|
# decode a payment request because the invoice is malformed or for the wrong
|
|
|
|
|
# network. Covers both LND (gRPC, e.g. the Go `strconv.ParseUint ... invalid
|
|
|
|
|
# syntax` a regtest invoice triggers on a mainnet node) and CLN (which prefixes
|
|
|
|
|
# its decode errors with `Invalid bolt11:`).
|
|
|
|
|
_PAY_REQ_DECODE_FAILURE_SIGNATURES = (
|
|
|
|
|
"checksum failed",
|
|
|
|
|
"invalid syntax",
|
|
|
|
|
"invalid bech32",
|
|
|
|
|
"bad bech32",
|
|
|
|
|
"unable to decode",
|
|
|
|
|
"invalid character",
|
|
|
|
|
"invalid bolt11",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
_PAY_REQ_DECODE_FAILURE_DETAIL = (
|
|
|
|
|
"Could not decode the payment request. It may be malformed or intended "
|
|
|
|
|
"for a different network (e.g. a regtest or testnet invoice on a mainnet "
|
|
|
|
|
"node)."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def raise_for_pay_req_decode_error(details: str) -> None:
|
|
|
|
|
"""Raise HTTP 400 when `details` looks like an invoice-decode failure.
|
|
|
|
|
|
|
|
|
|
Gives a single, clear message across LND and CLN instead of leaking the
|
|
|
|
|
backend's raw error. Returns without raising for anything unrecognized so
|
|
|
|
|
the caller can handle it (typically as a 500).
|
|
|
|
|
"""
|
|
|
|
|
text = (details or "").lower()
|
|
|
|
|
if any(sig in text for sig in _PAY_REQ_DECODE_FAILURE_SIGNATURES):
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status.HTTP_400_BAD_REQUEST,
|
|
|
|
|
detail=_PAY_REQ_DECODE_FAILURE_DETAIL,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2023-05-01 20:48:10 +02:00
|
|
|
async def alias_or_empty(func, node_pub: str) -> str:
|
|
|
|
|
logger.debug(f"alias_or_empty({node_pub})")
|
|
|
|
|
|
|
|
|
|
if not node_pub:
|
2023-05-17 16:02:28 +02:00
|
|
|
logger.debug("alias_or_empty('') -> ''")
|
2023-05-01 20:48:10 +02:00
|
|
|
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
res = await func(node_pub)
|
|
|
|
|
logger.debug(f"alias_or_empty -> {node_pub}")
|
|
|
|
|
|
|
|
|
|
return res
|
|
|
|
|
except NodeNotFoundError:
|
|
|
|
|
logger.debug(f"NodeNotFoundError for node_pub={node_pub}")
|
|
|
|
|
|
|
|
|
|
return ""
|