feat: introduce self.features to wallets, refactor feature nodemanager (#3260)

This commit is contained in:
dni ⚡ 2025-07-10 15:40:01 +02:00
parent 256a8098c6
commit 6e9f451419
No known key found for this signature in database
GPG key ID: D1F416F29AD26E87
9 changed files with 36 additions and 29 deletions

View file

@ -7,8 +7,9 @@ from pydantic import BaseModel
from starlette.status import HTTP_503_SERVICE_UNAVAILABLE
from lnbits.decorators import check_admin, check_super_user, parse_filters
from lnbits.nodes import get_node_class
from lnbits.settings import settings
from lnbits.wallets import get_funding_source
from lnbits.wallets.base import Feature
from ...db import Filters, Page
from ...nodes.base import (
@ -26,9 +27,13 @@ from ...nodes.base import (
from ...utils.cache import cache
def require_node():
node_class = get_node_class()
if not node_class:
def require_node() -> Node:
funding_source = get_funding_source()
if (
not funding_source.features
or Feature.nodemanager not in funding_source.features
or not funding_source.__node_cls__
):
raise HTTPException(
status_code=HTTPStatus.NOT_IMPLEMENTED,
detail="Active backend does not implement Node API",
@ -38,7 +43,7 @@ def require_node():
status_code=HTTPStatus.SERVICE_UNAVAILABLE,
detail="Not enabled",
)
return node_class
return funding_source.__node_cls__(funding_source)
def check_public():

View file

@ -16,7 +16,6 @@ from packaging import version
from pydantic.schema import field_schema
from lnbits.jinja2_templating import Jinja2Templates
from lnbits.nodes import get_node_class
from lnbits.settings import settings
from lnbits.utils.crypto import AESCipher
@ -83,8 +82,8 @@ def template_renderer(additional_folders: Optional[list] = None) -> Jinja2Templa
"LNBITS_CUSTOM_BADGE_COLOR": settings.lnbits_custom_badge_color,
"LNBITS_EXTENSIONS_DEACTIVATE_ALL": settings.lnbits_extensions_deactivate_all,
"LNBITS_NEW_ACCOUNTS_ALLOWED": settings.new_accounts_allowed,
"LNBITS_NODE_UI": settings.lnbits_node_ui and get_node_class() is not None,
"LNBITS_NODE_UI_AVAILABLE": get_node_class() is not None,
"LNBITS_NODE_UI": settings.lnbits_node_ui and settings.has_nodemanager,
"LNBITS_NODE_UI_AVAILABLE": settings.has_nodemanager,
"LNBITS_QR_LOGO": settings.lnbits_qr_logo,
"LNBITS_SERVICE_FEE": settings.lnbits_service_fee,
"LNBITS_SERVICE_FEE_MAX": settings.lnbits_service_fee_max,

View file

@ -1,15 +0,0 @@
from typing import Optional
from .base import Node
def get_node_class() -> Optional[Node]:
return NODE
def set_node_class(node: Node):
global NODE
NODE = node
NODE: Optional[Node] = None

View file

@ -11,12 +11,12 @@ from httpx import HTTPStatusError
from loguru import logger
from lnbits.db import Filters, Page
from lnbits.nodes import Node
from lnbits.nodes.base import (
ChannelBalance,
ChannelPoint,
ChannelState,
ChannelStats,
Node,
NodeChannel,
NodeFees,
NodeInfoResponse,

View file

@ -987,6 +987,8 @@ class TransientSettings(InstalledExtensionsSettings, ExchangeHistorySettings):
server_startup_time: int = Field(default=time())
has_nodemanager: bool = Field(default=False)
@property
def lnbits_server_up_time(self) -> str:
up_time = int(time() - self.server_startup_time)

View file

@ -2,9 +2,8 @@ from __future__ import annotations
import importlib
from lnbits.nodes import set_node_class
from lnbits.settings import settings
from lnbits.wallets.base import Wallet
from lnbits.wallets.base import Feature, Wallet
from .alby import AlbyWallet
from .blink import BlinkWallet
@ -35,13 +34,12 @@ from .void import VoidWallet
from .zbd import ZBDWallet
def set_funding_source(class_name: str | None = None):
def set_funding_source(class_name: str | None = None) -> None:
backend_wallet_class = class_name or settings.lnbits_backend_wallet_class
funding_source_constructor = getattr(wallets_module, backend_wallet_class)
global funding_source
funding_source = funding_source_constructor()
if funding_source.__node_cls__:
set_node_class(funding_source.__node_cls__(funding_source))
settings.has_nodemanager = funding_source.has_feature(Feature.nodemanager)
def get_funding_source() -> Wallet:

View file

@ -3,6 +3,7 @@ from __future__ import annotations
import asyncio
from abc import ABC, abstractmethod
from collections.abc import AsyncGenerator, Coroutine
from enum import Enum
from typing import TYPE_CHECKING, NamedTuple
from loguru import logger
@ -13,6 +14,12 @@ if TYPE_CHECKING:
from lnbits.nodes.base import Node
class Feature(Enum):
nodemanager = "nodemanager"
# hold = "hold"
# bolt12 = "bolt12"
class StatusResponse(NamedTuple):
error_message: str | None
balance_msat: int
@ -100,6 +107,10 @@ class PaymentPendingStatus(PaymentStatus):
class Wallet(ABC):
__node_cls__: type[Node] | None = None
features: list[Feature] | None = None
def has_feature(self, feature: Feature) -> bool:
return self.features is not None and feature in self.features
def __init__(self) -> None:
self.pending_invoices: list[str] = []

View file

@ -14,6 +14,7 @@ from lnbits.settings import settings
from lnbits.utils.crypto import random_secret_and_hash
from .base import (
Feature,
InvoiceResponse,
PaymentFailedStatus,
PaymentPendingStatus,
@ -31,12 +32,16 @@ async def run_sync(func) -> Any:
class CoreLightningWallet(Wallet):
"""Core Lightning RPC implementation."""
__node_cls__ = CoreLightningNode
features = [Feature.nodemanager]
async def cleanup(self):
pass
def __init__(self):
rpc = settings.corelightning_rpc or settings.clightning_rpc
if not rpc:
raise ValueError(

View file

@ -14,6 +14,7 @@ from lnbits.settings import settings
from lnbits.utils.crypto import random_secret_and_hash
from .base import (
Feature,
InvoiceResponse,
PaymentFailedStatus,
PaymentPendingStatus,
@ -30,6 +31,7 @@ class LndRestWallet(Wallet):
"""https://api.lightning.community/rest/index.html#lnd-rest-api-reference"""
__node_cls__ = LndRestNode
features = [Feature.nodemanager]
def __init__(self):
if not settings.lnd_rest_endpoint: