feat: HodlInvoices (Rebase) (#2869)
Co-authored-by: pseudozach <git@pseudozach.com> Co-authored-by: dni ⚡ <office@dnilabs.com> Co-authored-by: Vlad Stan <stan.v.vlad@gmail.com>
This commit is contained in:
co-authored by
pseudozach
dni ⚡
Vlad Stan
parent
6e9f451419
commit
f91c933919
@@ -8,6 +8,7 @@ from typing import TYPE_CHECKING, NamedTuple
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.exceptions import InvoiceError
|
||||
from lnbits.settings import settings
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -152,6 +153,29 @@ class Wallet(ABC):
|
||||
) -> Coroutine[None, None, PaymentStatus]:
|
||||
pass
|
||||
|
||||
async def create_hold_invoice(
|
||||
self,
|
||||
amount: int,
|
||||
payment_hash: str,
|
||||
memo: str | None = None,
|
||||
description_hash: bytes | None = None,
|
||||
unhashed_description: bytes | None = None,
|
||||
**kwargs,
|
||||
) -> InvoiceResponse:
|
||||
raise InvoiceError(
|
||||
message="Hold invoices are not supported by this wallet.", status="failed"
|
||||
)
|
||||
|
||||
async def settle_hold_invoice(self, preimage: str) -> InvoiceResponse:
|
||||
raise InvoiceError(
|
||||
message="Hold invoices are not supported by this wallet.", status="failed"
|
||||
)
|
||||
|
||||
async def cancel_hold_invoice(self, payment_hash: str) -> InvoiceResponse:
|
||||
raise InvoiceError(
|
||||
message="Hold invoices are not supported by this wallet.", status="failed"
|
||||
)
|
||||
|
||||
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
||||
while settings.lnbits_running:
|
||||
for invoice in self.pending_invoices:
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# NO CHECKED-IN PROTOBUF GENCODE
|
||||
# source: invoices.proto
|
||||
# Protobuf Python Version: 5.28.1
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import runtime_version as _runtime_version
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import builder as _builder
|
||||
_runtime_version.ValidateProtobufRuntimeVersion(
|
||||
_runtime_version.Domain.PUBLIC,
|
||||
5,
|
||||
28,
|
||||
1,
|
||||
'',
|
||||
'invoices.proto'
|
||||
)
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
import lnbits.wallets.lnd_grpc_files.lightning_pb2 as lightning__pb2
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0einvoices.proto\x12\x0binvoicesrpc\x1a\x0flightning.proto\"(\n\x10\x43\x61ncelInvoiceMsg\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\"\x13\n\x11\x43\x61ncelInvoiceResp\"\xe4\x01\n\x15\x41\x64\x64HoldInvoiceRequest\x12\x0c\n\x04memo\x18\x01 \x01(\t\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\x12\r\n\x05value\x18\x03 \x01(\x03\x12\x12\n\nvalue_msat\x18\n \x01(\x03\x12\x18\n\x10\x64\x65scription_hash\x18\x04 \x01(\x0c\x12\x0e\n\x06\x65xpiry\x18\x05 \x01(\x03\x12\x15\n\rfallback_addr\x18\x06 \x01(\t\x12\x13\n\x0b\x63ltv_expiry\x18\x07 \x01(\x04\x12%\n\x0broute_hints\x18\x08 \x03(\x0b\x32\x10.lnrpc.RouteHint\x12\x0f\n\x07private\x18\t \x01(\x08\"V\n\x12\x41\x64\x64HoldInvoiceResp\x12\x17\n\x0fpayment_request\x18\x01 \x01(\t\x12\x11\n\tadd_index\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_addr\x18\x03 \x01(\x0c\"$\n\x10SettleInvoiceMsg\x12\x10\n\x08preimage\x18\x01 \x01(\x0c\"\x13\n\x11SettleInvoiceResp\"5\n\x1dSubscribeSingleInvoiceRequest\x12\x0e\n\x06r_hash\x18\x02 \x01(\x0cJ\x04\x08\x01\x10\x02\"\x99\x01\n\x10LookupInvoiceMsg\x12\x16\n\x0cpayment_hash\x18\x01 \x01(\x0cH\x00\x12\x16\n\x0cpayment_addr\x18\x02 \x01(\x0cH\x00\x12\x10\n\x06set_id\x18\x03 \x01(\x0cH\x00\x12\x34\n\x0flookup_modifier\x18\x04 \x01(\x0e\x32\x1b.invoicesrpc.LookupModifierB\r\n\x0binvoice_ref\".\n\nCircuitKey\x12\x0f\n\x07\x63han_id\x18\x01 \x01(\x04\x12\x0f\n\x07htlc_id\x18\x02 \x01(\x04\"\xdd\x02\n\x11HtlcModifyRequest\x12\x1f\n\x07invoice\x18\x01 \x01(\x0b\x32\x0e.lnrpc.Invoice\x12\x36\n\x15\x65xit_htlc_circuit_key\x18\x02 \x01(\x0b\x32\x17.invoicesrpc.CircuitKey\x12\x15\n\rexit_htlc_amt\x18\x03 \x01(\x04\x12\x18\n\x10\x65xit_htlc_expiry\x18\x04 \x01(\r\x12\x16\n\x0e\x63urrent_height\x18\x05 \x01(\r\x12\x64\n\x1d\x65xit_htlc_wire_custom_records\x18\x06 \x03(\x0b\x32=.invoicesrpc.HtlcModifyRequest.ExitHtlcWireCustomRecordsEntry\x1a@\n\x1e\x45xitHtlcWireCustomRecordsEntry\x12\x0b\n\x03key\x18\x01 \x01(\x04\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"z\n\x12HtlcModifyResponse\x12,\n\x0b\x63ircuit_key\x18\x01 \x01(\x0b\x32\x17.invoicesrpc.CircuitKey\x12\x15\n\x08\x61mt_paid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x12\n\ncancel_set\x18\x03 \x01(\x08\x42\x0b\n\t_amt_paid*D\n\x0eLookupModifier\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\x11\n\rHTLC_SET_ONLY\x10\x01\x12\x12\n\x0eHTLC_SET_BLANK\x10\x02\x32\xf0\x03\n\x08Invoices\x12V\n\x16SubscribeSingleInvoice\x12*.invoicesrpc.SubscribeSingleInvoiceRequest\x1a\x0e.lnrpc.Invoice0\x01\x12N\n\rCancelInvoice\x12\x1d.invoicesrpc.CancelInvoiceMsg\x1a\x1e.invoicesrpc.CancelInvoiceResp\x12U\n\x0e\x41\x64\x64HoldInvoice\x12\".invoicesrpc.AddHoldInvoiceRequest\x1a\x1f.invoicesrpc.AddHoldInvoiceResp\x12N\n\rSettleInvoice\x12\x1d.invoicesrpc.SettleInvoiceMsg\x1a\x1e.invoicesrpc.SettleInvoiceResp\x12@\n\x0fLookupInvoiceV2\x12\x1d.invoicesrpc.LookupInvoiceMsg\x1a\x0e.lnrpc.Invoice\x12S\n\x0cHtlcModifier\x12\x1f.invoicesrpc.HtlcModifyResponse\x1a\x1e.invoicesrpc.HtlcModifyRequest(\x01\x30\x01\x42\x33Z1github.com/lightningnetwork/lnd/lnrpc/invoicesrpcb\x06proto3')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'invoices_pb2', _globals)
|
||||
if not _descriptor._USE_C_DESCRIPTORS:
|
||||
_globals['DESCRIPTOR']._loaded_options = None
|
||||
_globals['DESCRIPTOR']._serialized_options = b'Z1github.com/lightningnetwork/lnd/lnrpc/invoicesrpc'
|
||||
_globals['_HTLCMODIFYREQUEST_EXITHTLCWIRECUSTOMRECORDSENTRY']._loaded_options = None
|
||||
_globals['_HTLCMODIFYREQUEST_EXITHTLCWIRECUSTOMRECORDSENTRY']._serialized_options = b'8\001'
|
||||
_globals['_LOOKUPMODIFIER']._serialized_start=1224
|
||||
_globals['_LOOKUPMODIFIER']._serialized_end=1292
|
||||
_globals['_CANCELINVOICEMSG']._serialized_start=48
|
||||
_globals['_CANCELINVOICEMSG']._serialized_end=88
|
||||
_globals['_CANCELINVOICERESP']._serialized_start=90
|
||||
_globals['_CANCELINVOICERESP']._serialized_end=109
|
||||
_globals['_ADDHOLDINVOICEREQUEST']._serialized_start=112
|
||||
_globals['_ADDHOLDINVOICEREQUEST']._serialized_end=340
|
||||
_globals['_ADDHOLDINVOICERESP']._serialized_start=342
|
||||
_globals['_ADDHOLDINVOICERESP']._serialized_end=428
|
||||
_globals['_SETTLEINVOICEMSG']._serialized_start=430
|
||||
_globals['_SETTLEINVOICEMSG']._serialized_end=466
|
||||
_globals['_SETTLEINVOICERESP']._serialized_start=468
|
||||
_globals['_SETTLEINVOICERESP']._serialized_end=487
|
||||
_globals['_SUBSCRIBESINGLEINVOICEREQUEST']._serialized_start=489
|
||||
_globals['_SUBSCRIBESINGLEINVOICEREQUEST']._serialized_end=542
|
||||
_globals['_LOOKUPINVOICEMSG']._serialized_start=545
|
||||
_globals['_LOOKUPINVOICEMSG']._serialized_end=698
|
||||
_globals['_CIRCUITKEY']._serialized_start=700
|
||||
_globals['_CIRCUITKEY']._serialized_end=746
|
||||
_globals['_HTLCMODIFYREQUEST']._serialized_start=749
|
||||
_globals['_HTLCMODIFYREQUEST']._serialized_end=1098
|
||||
_globals['_HTLCMODIFYREQUEST_EXITHTLCWIRECUSTOMRECORDSENTRY']._serialized_start=1034
|
||||
_globals['_HTLCMODIFYREQUEST_EXITHTLCWIRECUSTOMRECORDSENTRY']._serialized_end=1098
|
||||
_globals['_HTLCMODIFYRESPONSE']._serialized_start=1100
|
||||
_globals['_HTLCMODIFYRESPONSE']._serialized_end=1222
|
||||
_globals['_INVOICES']._serialized_start=1295
|
||||
_globals['_INVOICES']._serialized_end=1791
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -0,0 +1,392 @@
|
||||
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
||||
"""Client and server classes corresponding to protobuf-defined services."""
|
||||
import grpc
|
||||
import warnings
|
||||
|
||||
import lnbits.wallets.lnd_grpc_files.invoices_pb2 as invoices__pb2
|
||||
import lnbits.wallets.lnd_grpc_files.lightning_pb2 as lightning__pb2
|
||||
|
||||
GRPC_GENERATED_VERSION = '1.68.1'
|
||||
GRPC_VERSION = grpc.__version__
|
||||
_version_not_supported = False
|
||||
|
||||
try:
|
||||
from grpc._utilities import first_version_is_lower
|
||||
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
|
||||
except ImportError:
|
||||
_version_not_supported = True
|
||||
|
||||
if _version_not_supported:
|
||||
raise RuntimeError(
|
||||
f'The grpc package installed is at version {GRPC_VERSION},'
|
||||
+ f' but the generated code in invoices_pb2_grpc.py depends on'
|
||||
+ f' grpcio>={GRPC_GENERATED_VERSION}.'
|
||||
+ f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
|
||||
+ f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
|
||||
)
|
||||
|
||||
|
||||
class InvoicesStub(object):
|
||||
"""
|
||||
Comments in this file will be directly parsed into the API
|
||||
Documentation as descriptions of the associated method, message, or field.
|
||||
These descriptions should go right above the definition of the object, and
|
||||
can be in either block or // comment format.
|
||||
|
||||
An RPC method can be matched to an lncli command by placing a line in the
|
||||
beginning of the description in exactly the following format:
|
||||
lncli: `methodname`
|
||||
|
||||
Failure to specify the exact name of the command will cause documentation
|
||||
generation to fail.
|
||||
|
||||
More information on how exactly the gRPC documentation is generated from
|
||||
this proto file can be found here:
|
||||
https://github.com/lightninglabs/lightning-api
|
||||
|
||||
Invoices is a service that can be used to create, accept, settle and cancel
|
||||
invoices.
|
||||
"""
|
||||
|
||||
def __init__(self, channel):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
channel: A grpc.Channel.
|
||||
"""
|
||||
self.SubscribeSingleInvoice = channel.unary_stream(
|
||||
'/invoicesrpc.Invoices/SubscribeSingleInvoice',
|
||||
request_serializer=invoices__pb2.SubscribeSingleInvoiceRequest.SerializeToString,
|
||||
response_deserializer=lightning__pb2.Invoice.FromString,
|
||||
_registered_method=True)
|
||||
self.CancelInvoice = channel.unary_unary(
|
||||
'/invoicesrpc.Invoices/CancelInvoice',
|
||||
request_serializer=invoices__pb2.CancelInvoiceMsg.SerializeToString,
|
||||
response_deserializer=invoices__pb2.CancelInvoiceResp.FromString,
|
||||
_registered_method=True)
|
||||
self.AddHoldInvoice = channel.unary_unary(
|
||||
'/invoicesrpc.Invoices/AddHoldInvoice',
|
||||
request_serializer=invoices__pb2.AddHoldInvoiceRequest.SerializeToString,
|
||||
response_deserializer=invoices__pb2.AddHoldInvoiceResp.FromString,
|
||||
_registered_method=True)
|
||||
self.SettleInvoice = channel.unary_unary(
|
||||
'/invoicesrpc.Invoices/SettleInvoice',
|
||||
request_serializer=invoices__pb2.SettleInvoiceMsg.SerializeToString,
|
||||
response_deserializer=invoices__pb2.SettleInvoiceResp.FromString,
|
||||
_registered_method=True)
|
||||
self.LookupInvoiceV2 = channel.unary_unary(
|
||||
'/invoicesrpc.Invoices/LookupInvoiceV2',
|
||||
request_serializer=invoices__pb2.LookupInvoiceMsg.SerializeToString,
|
||||
response_deserializer=lightning__pb2.Invoice.FromString,
|
||||
_registered_method=True)
|
||||
self.HtlcModifier = channel.stream_stream(
|
||||
'/invoicesrpc.Invoices/HtlcModifier',
|
||||
request_serializer=invoices__pb2.HtlcModifyResponse.SerializeToString,
|
||||
response_deserializer=invoices__pb2.HtlcModifyRequest.FromString,
|
||||
_registered_method=True)
|
||||
|
||||
|
||||
class InvoicesServicer(object):
|
||||
"""
|
||||
Comments in this file will be directly parsed into the API
|
||||
Documentation as descriptions of the associated method, message, or field.
|
||||
These descriptions should go right above the definition of the object, and
|
||||
can be in either block or // comment format.
|
||||
|
||||
An RPC method can be matched to an lncli command by placing a line in the
|
||||
beginning of the description in exactly the following format:
|
||||
lncli: `methodname`
|
||||
|
||||
Failure to specify the exact name of the command will cause documentation
|
||||
generation to fail.
|
||||
|
||||
More information on how exactly the gRPC documentation is generated from
|
||||
this proto file can be found here:
|
||||
https://github.com/lightninglabs/lightning-api
|
||||
|
||||
Invoices is a service that can be used to create, accept, settle and cancel
|
||||
invoices.
|
||||
"""
|
||||
|
||||
def SubscribeSingleInvoice(self, request, context):
|
||||
"""
|
||||
SubscribeSingleInvoice returns a uni-directional stream (server -> client)
|
||||
to notify the client of state transitions of the specified invoice.
|
||||
Initially the current invoice state is always sent out.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def CancelInvoice(self, request, context):
|
||||
"""lncli: `cancelinvoice`
|
||||
CancelInvoice cancels a currently open invoice. If the invoice is already
|
||||
canceled, this call will succeed. If the invoice is already settled, it will
|
||||
fail.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def AddHoldInvoice(self, request, context):
|
||||
"""lncli: `addholdinvoice`
|
||||
AddHoldInvoice creates a hold invoice. It ties the invoice to the hash
|
||||
supplied in the request.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def SettleInvoice(self, request, context):
|
||||
"""lncli: `settleinvoice`
|
||||
SettleInvoice settles an accepted invoice. If the invoice is already
|
||||
settled, this call will succeed.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def LookupInvoiceV2(self, request, context):
|
||||
"""
|
||||
LookupInvoiceV2 attempts to look up at invoice. An invoice can be referenced
|
||||
using either its payment hash, payment address, or set ID.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def HtlcModifier(self, request_iterator, context):
|
||||
"""
|
||||
HtlcModifier is a bidirectional streaming RPC that allows a client to
|
||||
intercept and modify the HTLCs that attempt to settle the given invoice. The
|
||||
server will send HTLCs of invoices to the client and the client can modify
|
||||
some aspects of the HTLC in order to pass the invoice acceptance tests.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
|
||||
def add_InvoicesServicer_to_server(servicer, server):
|
||||
rpc_method_handlers = {
|
||||
'SubscribeSingleInvoice': grpc.unary_stream_rpc_method_handler(
|
||||
servicer.SubscribeSingleInvoice,
|
||||
request_deserializer=invoices__pb2.SubscribeSingleInvoiceRequest.FromString,
|
||||
response_serializer=lightning__pb2.Invoice.SerializeToString,
|
||||
),
|
||||
'CancelInvoice': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.CancelInvoice,
|
||||
request_deserializer=invoices__pb2.CancelInvoiceMsg.FromString,
|
||||
response_serializer=invoices__pb2.CancelInvoiceResp.SerializeToString,
|
||||
),
|
||||
'AddHoldInvoice': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.AddHoldInvoice,
|
||||
request_deserializer=invoices__pb2.AddHoldInvoiceRequest.FromString,
|
||||
response_serializer=invoices__pb2.AddHoldInvoiceResp.SerializeToString,
|
||||
),
|
||||
'SettleInvoice': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.SettleInvoice,
|
||||
request_deserializer=invoices__pb2.SettleInvoiceMsg.FromString,
|
||||
response_serializer=invoices__pb2.SettleInvoiceResp.SerializeToString,
|
||||
),
|
||||
'LookupInvoiceV2': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.LookupInvoiceV2,
|
||||
request_deserializer=invoices__pb2.LookupInvoiceMsg.FromString,
|
||||
response_serializer=lightning__pb2.Invoice.SerializeToString,
|
||||
),
|
||||
'HtlcModifier': grpc.stream_stream_rpc_method_handler(
|
||||
servicer.HtlcModifier,
|
||||
request_deserializer=invoices__pb2.HtlcModifyResponse.FromString,
|
||||
response_serializer=invoices__pb2.HtlcModifyRequest.SerializeToString,
|
||||
),
|
||||
}
|
||||
generic_handler = grpc.method_handlers_generic_handler(
|
||||
'invoicesrpc.Invoices', rpc_method_handlers)
|
||||
server.add_generic_rpc_handlers((generic_handler,))
|
||||
server.add_registered_method_handlers('invoicesrpc.Invoices', rpc_method_handlers)
|
||||
|
||||
|
||||
# This class is part of an EXPERIMENTAL API.
|
||||
class Invoices(object):
|
||||
"""
|
||||
Comments in this file will be directly parsed into the API
|
||||
Documentation as descriptions of the associated method, message, or field.
|
||||
These descriptions should go right above the definition of the object, and
|
||||
can be in either block or // comment format.
|
||||
|
||||
An RPC method can be matched to an lncli command by placing a line in the
|
||||
beginning of the description in exactly the following format:
|
||||
lncli: `methodname`
|
||||
|
||||
Failure to specify the exact name of the command will cause documentation
|
||||
generation to fail.
|
||||
|
||||
More information on how exactly the gRPC documentation is generated from
|
||||
this proto file can be found here:
|
||||
https://github.com/lightninglabs/lightning-api
|
||||
|
||||
Invoices is a service that can be used to create, accept, settle and cancel
|
||||
invoices.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def SubscribeSingleInvoice(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_stream(
|
||||
request,
|
||||
target,
|
||||
'/invoicesrpc.Invoices/SubscribeSingleInvoice',
|
||||
invoices__pb2.SubscribeSingleInvoiceRequest.SerializeToString,
|
||||
lightning__pb2.Invoice.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
|
||||
@staticmethod
|
||||
def CancelInvoice(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(
|
||||
request,
|
||||
target,
|
||||
'/invoicesrpc.Invoices/CancelInvoice',
|
||||
invoices__pb2.CancelInvoiceMsg.SerializeToString,
|
||||
invoices__pb2.CancelInvoiceResp.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
|
||||
@staticmethod
|
||||
def AddHoldInvoice(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(
|
||||
request,
|
||||
target,
|
||||
'/invoicesrpc.Invoices/AddHoldInvoice',
|
||||
invoices__pb2.AddHoldInvoiceRequest.SerializeToString,
|
||||
invoices__pb2.AddHoldInvoiceResp.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
|
||||
@staticmethod
|
||||
def SettleInvoice(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(
|
||||
request,
|
||||
target,
|
||||
'/invoicesrpc.Invoices/SettleInvoice',
|
||||
invoices__pb2.SettleInvoiceMsg.SerializeToString,
|
||||
invoices__pb2.SettleInvoiceResp.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
|
||||
@staticmethod
|
||||
def LookupInvoiceV2(request,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.unary_unary(
|
||||
request,
|
||||
target,
|
||||
'/invoicesrpc.Invoices/LookupInvoiceV2',
|
||||
invoices__pb2.LookupInvoiceMsg.SerializeToString,
|
||||
lightning__pb2.Invoice.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
|
||||
@staticmethod
|
||||
def HtlcModifier(request_iterator,
|
||||
target,
|
||||
options=(),
|
||||
channel_credentials=None,
|
||||
call_credentials=None,
|
||||
insecure=False,
|
||||
compression=None,
|
||||
wait_for_ready=None,
|
||||
timeout=None,
|
||||
metadata=None):
|
||||
return grpc.experimental.stream_stream(
|
||||
request_iterator,
|
||||
target,
|
||||
'/invoicesrpc.Invoices/HtlcModifier',
|
||||
invoices__pb2.HtlcModifyResponse.SerializeToString,
|
||||
invoices__pb2.HtlcModifyRequest.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -8,13 +8,15 @@ from typing import Optional
|
||||
import grpc
|
||||
from loguru import logger
|
||||
|
||||
import lnbits.wallets.lnd_grpc_files.invoices_pb2 as invoices
|
||||
import lnbits.wallets.lnd_grpc_files.invoices_pb2_grpc as invoicesrpc
|
||||
import lnbits.wallets.lnd_grpc_files.lightning_pb2 as ln
|
||||
import lnbits.wallets.lnd_grpc_files.lightning_pb2_grpc as lnrpc
|
||||
import lnbits.wallets.lnd_grpc_files.router_pb2 as router
|
||||
import lnbits.wallets.lnd_grpc_files.router_pb2_grpc as routerrpc
|
||||
from lnbits.helpers import normalize_endpoint
|
||||
from lnbits.settings import settings
|
||||
from lnbits.utils.crypto import random_secret_and_hash
|
||||
from lnbits.wallets.lnd_grpc_files.router_pb2_grpc import RouterStub
|
||||
|
||||
from .base import (
|
||||
InvoiceResponse,
|
||||
@@ -98,7 +100,8 @@ class LndWallet(Wallet):
|
||||
f"{self.endpoint}:{self.port}", composite_creds
|
||||
)
|
||||
self.rpc = lnrpc.LightningStub(channel)
|
||||
self.routerpc = routerrpc.RouterStub(channel)
|
||||
self.routerpc = RouterStub(channel)
|
||||
self.invoicesrpc = invoicesrpc.InvoicesStub(channel)
|
||||
|
||||
def metadata_callback(self, _, callback):
|
||||
callback([("macaroon", self.macaroon)], None)
|
||||
@@ -108,7 +111,7 @@ class LndWallet(Wallet):
|
||||
|
||||
async def status(self) -> StatusResponse:
|
||||
try:
|
||||
resp = await self.rpc.ChannelBalance(ln.ChannelBalanceRequest())
|
||||
resp = await self.rpc.ChannelBalance(ln.ChannelBalanceRequest()) # type: ignore
|
||||
except Exception as exc:
|
||||
return StatusResponse(f"Unable to connect, got: '{exc}'", 0)
|
||||
|
||||
@@ -144,7 +147,7 @@ class LndWallet(Wallet):
|
||||
data["r_hash"] = bytes.fromhex(payment_hash)
|
||||
data["r_preimage"] = bytes.fromhex(preimage)
|
||||
try:
|
||||
req = ln.Invoice(**data)
|
||||
req = ln.Invoice(**data) # type: ignore
|
||||
resp = await self.rpc.AddInvoice(req)
|
||||
# response model
|
||||
# {
|
||||
@@ -168,7 +171,7 @@ class LndWallet(Wallet):
|
||||
|
||||
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
|
||||
# fee_limit_fixed = ln.FeeLimit(fixed=fee_limit_msat // 1000)
|
||||
req = router.SendPaymentRequest(
|
||||
req = router.SendPaymentRequest( # type: ignore
|
||||
payment_request=bolt11,
|
||||
fee_limit_msat=fee_limit_msat,
|
||||
timeout_seconds=30,
|
||||
@@ -227,7 +230,7 @@ class LndWallet(Wallet):
|
||||
# that use different checking_id formats
|
||||
raise ValueError
|
||||
|
||||
resp = await self.rpc.LookupInvoice(ln.PaymentHash(r_hash=r_hash))
|
||||
resp = await self.rpc.LookupInvoice(ln.PaymentHash(r_hash=r_hash)) # type: ignore
|
||||
if resp.settled:
|
||||
return PaymentSuccessStatus(preimage=resp.r_preimage.hex())
|
||||
|
||||
@@ -271,7 +274,7 @@ class LndWallet(Wallet):
|
||||
|
||||
try:
|
||||
resp = self.routerpc.TrackPaymentV2(
|
||||
router.TrackPaymentRequest(payment_hash=r_hash)
|
||||
router.TrackPaymentRequest(payment_hash=r_hash) # type: ignore
|
||||
)
|
||||
async for payment in resp:
|
||||
if len(payment.htlcs) and statuses[payment.status]:
|
||||
@@ -288,7 +291,7 @@ class LndWallet(Wallet):
|
||||
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
||||
while settings.lnbits_running:
|
||||
try:
|
||||
request = ln.InvoiceSubscription()
|
||||
request = ln.InvoiceSubscription() # type: ignore
|
||||
async for i in self.rpc.SubscribeInvoices(request):
|
||||
if not i.settled:
|
||||
continue
|
||||
@@ -301,3 +304,65 @@ class LndWallet(Wallet):
|
||||
"retrying in 5 seconds"
|
||||
)
|
||||
await asyncio.sleep(5)
|
||||
|
||||
async def create_hold_invoice(
|
||||
self,
|
||||
amount: int,
|
||||
payment_hash: str,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
**kwargs,
|
||||
) -> InvoiceResponse:
|
||||
data: dict = {
|
||||
"description_hash": b"",
|
||||
"value": amount,
|
||||
"hash": hex_to_bytes(payment_hash),
|
||||
"private": True,
|
||||
"memo": memo or "",
|
||||
}
|
||||
if kwargs.get("expiry"):
|
||||
data["expiry"] = kwargs["expiry"]
|
||||
if description_hash:
|
||||
data["description_hash"] = description_hash
|
||||
elif unhashed_description:
|
||||
data["description_hash"] = sha256(unhashed_description).digest()
|
||||
try:
|
||||
req = invoices.AddHoldInvoiceRequest(**data) # type: ignore
|
||||
res = await self.invoicesrpc.AddHoldInvoice(req)
|
||||
logger.debug(f"AddHoldInvoice response: {res}")
|
||||
except Exception as exc:
|
||||
logger.warning(exc)
|
||||
error_message = str(exc)
|
||||
return InvoiceResponse(ok=False, error_message=error_message)
|
||||
return InvoiceResponse(
|
||||
ok=True, checking_id=payment_hash, payment_request=str(res.payment_request)
|
||||
)
|
||||
|
||||
async def settle_hold_invoice(self, preimage: str) -> InvoiceResponse:
|
||||
try:
|
||||
req = invoices.SettleInvoiceMsg(preimage=hex_to_bytes(preimage)) # type: ignore
|
||||
await self.invoicesrpc.SettleInvoice(req)
|
||||
except grpc.aio.AioRpcError as exc:
|
||||
return InvoiceResponse(
|
||||
ok=False, error_message=exc.details() or "unknown grpc exception"
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.warning(exc)
|
||||
return InvoiceResponse(ok=False, error_message=str(exc))
|
||||
return InvoiceResponse(ok=True, preimage=preimage)
|
||||
|
||||
async def cancel_hold_invoice(self, payment_hash: str) -> InvoiceResponse:
|
||||
try:
|
||||
req = invoices.CancelInvoiceMsg(payment_hash=hex_to_bytes(payment_hash)) # type: ignore
|
||||
res = await self.invoicesrpc.CancelInvoice(req)
|
||||
logger.debug(f"CancelInvoice response: {res}")
|
||||
except Exception as exc:
|
||||
logger.warning(exc)
|
||||
# If we cannot cancel the invoice, we return an error message
|
||||
# and True for ok that should be ignored by the service
|
||||
return InvoiceResponse(
|
||||
ok=False, checking_id=payment_hash, error_message=str(exc)
|
||||
)
|
||||
# If we reach here, the invoice was successfully canceled and payment failed
|
||||
return InvoiceResponse(True, checking_id=payment_hash)
|
||||
|
||||
@@ -28,7 +28,7 @@ from .macaroon import load_macaroon
|
||||
|
||||
|
||||
class LndRestWallet(Wallet):
|
||||
"""https://api.lightning.community/rest/index.html#lnd-rest-api-reference"""
|
||||
"""https://api.lightning.community/#lnd-rest-api-reference"""
|
||||
|
||||
__node_cls__ = LndRestNode
|
||||
features = [Feature.nodemanager]
|
||||
@@ -322,3 +322,77 @@ class LndRestWallet(Wallet):
|
||||
" seconds"
|
||||
)
|
||||
await asyncio.sleep(5)
|
||||
|
||||
async def create_hold_invoice(
|
||||
self,
|
||||
amount: int,
|
||||
payment_hash: str,
|
||||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
unhashed_description: Optional[bytes] = None,
|
||||
**_,
|
||||
) -> InvoiceResponse:
|
||||
data: dict = {
|
||||
"value": amount,
|
||||
"private": True,
|
||||
"hash": base64.b64encode(bytes.fromhex(payment_hash)).decode("ascii"),
|
||||
}
|
||||
if description_hash:
|
||||
data["description_hash"] = base64.b64encode(description_hash).decode(
|
||||
"ascii"
|
||||
)
|
||||
elif unhashed_description:
|
||||
data["description_hash"] = base64.b64encode(
|
||||
hashlib.sha256(unhashed_description).digest()
|
||||
).decode("ascii")
|
||||
else:
|
||||
data["memo"] = memo or ""
|
||||
|
||||
try:
|
||||
r = await self.client.post(url="/v2/invoices/hodl", json=data)
|
||||
r.raise_for_status()
|
||||
data = r.json()
|
||||
except httpx.HTTPStatusError as exc:
|
||||
logger.warning(exc)
|
||||
return InvoiceResponse(ok=False, error_message=exc.response.text)
|
||||
except Exception as exc:
|
||||
logger.error(exc)
|
||||
return InvoiceResponse(ok=False, error_message=str(exc))
|
||||
|
||||
payment_request = data["payment_request"]
|
||||
payment_hash = base64.b64encode(bytes.fromhex(payment_hash)).decode("ascii")
|
||||
|
||||
return InvoiceResponse(
|
||||
ok=True, checking_id=payment_hash, payment_request=payment_request
|
||||
)
|
||||
|
||||
async def settle_hold_invoice(self, preimage: str) -> InvoiceResponse:
|
||||
data: dict = {
|
||||
"preimage": base64.b64encode(bytes.fromhex(preimage)).decode("ascii")
|
||||
}
|
||||
try:
|
||||
r = await self.client.post(url="/v2/invoices/settle", json=data)
|
||||
r.raise_for_status()
|
||||
return InvoiceResponse(ok=True, preimage=preimage)
|
||||
except httpx.HTTPStatusError as exc:
|
||||
logger.warning(exc)
|
||||
return InvoiceResponse(ok=False, error_message=exc.response.text)
|
||||
except Exception as exc:
|
||||
logger.error(exc)
|
||||
return InvoiceResponse(ok=False, error_message=str(exc))
|
||||
|
||||
async def cancel_hold_invoice(self, payment_hash: str) -> InvoiceResponse:
|
||||
rhash = bytes.fromhex(payment_hash)
|
||||
try:
|
||||
r = await self.client.post(
|
||||
url="/v2/invoices/cancel",
|
||||
json={"payment_hash": base64.b64encode(rhash).decode("ascii")},
|
||||
)
|
||||
r.raise_for_status()
|
||||
logger.debug(f"Cancel hold invoice response: {r.text}")
|
||||
return InvoiceResponse(ok=True, checking_id=payment_hash)
|
||||
except httpx.HTTPStatusError as exc:
|
||||
return InvoiceResponse(ok=False, error_message=exc.response.text)
|
||||
except Exception as exc:
|
||||
logger.error(exc)
|
||||
return InvoiceResponse(ok=False, error_message=str(exc))
|
||||
|
||||
Reference in New Issue
Block a user