glclient

  1from . import scheduler_pb2 as schedpb
  2from . import greenlight_pb2 as nodepb
  3from pyln import grpc as clnpb  # type: ignore
  4from pyln.grpc import Amount, AmountOrAll, AmountOrAny  # noqa: F401
  5from . import glclient as native
  6from .glclient import backup_decrypt_with_seed  # noqa: F401
  7from .tls import TlsConfig
  8from google.protobuf.message import Message as PbMessage
  9from binascii import hexlify, unhexlify
 10from typing import Optional, List, Iterable, Any, Type, TypeVar
 11import logging
 12from glclient.lsps import LspClient
 13from glclient.glclient import Credentials
 14
 15
 16backup_decrypt_with_seed = native.backup_decrypt_with_seed
 17
 18
 19# Keep in sync with the libhsmd version, this is tested in unit tests.
 20__version__ = "v24.02"
 21
 22
 23E = TypeVar("E", bound=PbMessage)
 24
 25
 26def _convert(cls: Type[E], res: Iterable[Any]) -> E:
 27    return cls.FromString(bytes(res))
 28
 29
 30class Signer(object):
 31    def __init__(self, secret: bytes, network: str, creds: Credentials):
 32        self.inner = native.Signer(secret, network, creds)
 33        self.creds = creds
 34        self.handle: Optional[native.SignerHandle] = None
 35
 36    def run_in_thread(self) -> "native.SignerHandle":
 37        if self.handle is not None:
 38            raise ValueError(
 39                "This signer is already running, please shut it down before starting it again"
 40            )
 41        self.handle = self.inner.run_in_thread()
 42        return self.handle
 43
 44    def run_in_foreground(self) -> None:
 45        return self.inner.run_in_foreground()
 46
 47    def node_id(self) -> bytes:
 48        return bytes(self.inner.node_id())
 49
 50    def version(self) -> str:
 51        return self.inner.version()
 52
 53    def sign_challenge(self, message: bytes) -> bytes:
 54        return bytes(self.inner.sign_challenge(message))
 55
 56    def shutdown(self) -> None:
 57        if self.handle is None:
 58            raise ValueError("Attempted to shut down a signer that is not running")
 59        self.handle.shutdown()
 60        self.handle = None
 61
 62    def create_rune(
 63        self, restrictions: List[List[str]], rune: Optional[str] = None
 64    ) -> str:
 65        return self.inner.create_rune(restrictions, rune)
 66
 67    def is_running(self) -> bool:
 68        return self.handle is not None
 69
 70
 71class Scheduler(object):
 72
 73    def __init__(self, network: str, creds: Optional[Credentials] = None):
 74        self.network = network
 75        self.creds = creds if creds is not None else native.Credentials()
 76        self.inner = native.Scheduler(network, self.creds)
 77
 78    def schedule(self) -> schedpb.NodeInfoResponse:
 79        res = self.inner.schedule()
 80        return schedpb.NodeInfoResponse.FromString(bytes(res))
 81
 82    def get_node_info(self, wait: bool = False):
 83        res = self.inner.get_node_info(wait)
 84        return schedpb.NodeInfoResponse.FromString(bytes(res))
 85
 86    def register(self, signer: Signer, invite_code: Optional[str] = None) -> schedpb.RegistrationResponse:
 87        res = self.inner.register(signer.inner, invite_code)
 88        return schedpb.RegistrationResponse.FromString(bytes(res))
 89
 90    def recover(self, signer: Signer) -> schedpb.RecoveryResponse:
 91        res = self.inner.recover(signer.inner)
 92        return schedpb.RecoveryResponse.FromString(bytes(res))
 93
 94    def authenticate(self, creds: Credentials):
 95        self.creds = creds
 96        self.inner = self.inner.authenticate(creds)
 97        return self
 98
 99    def export_node(self) -> schedpb.ExportNodeResponse:
100        res = schedpb.ExportNodeResponse
101        return res.FromString(bytes(self.inner.export_node()))
102
103    def node(self) -> "Node":
104        res = self.inner.node()
105        info = schedpb.NodeInfoResponse.FromString(bytes(res))
106        return Node(
107            node_id=self.creds.node_id(),
108            grpc_uri=info.grpc_uri,
109            creds=self.creds,
110        )
111
112    def get_invite_codes(self) -> schedpb.ListInviteCodesResponse:
113        cls = schedpb.ListInviteCodesResponse
114        return cls.FromString(bytes(self.inner.get_invite_codes()))
115
116    def add_outgoing_webhook(self, uri: str) -> schedpb.AddOutgoingWebhookResponse:
117        res = self.inner.add_outgoing_webhook(uri)
118        return schedpb.AddOutgoingWebhookResponse.FromString(bytes(res))
119
120    def list_outgoing_webhooks(self) -> schedpb.ListOutgoingWebhooksResponse:
121        res = self.inner.list_outgoing_webhooks()
122        return schedpb.ListOutgoingWebhooksResponse.FromString(bytes(res))
123
124    def delete_outgoing_webhook(self, webhook_id: int) -> None:
125        res = self.inner.delete_outgoing_webhooks([webhook_id])
126
127    def delete_outgoing_webhooks(self, webhook_ids: List[int]) -> None:
128        res = self.inner.delete_outgoing_webhooks(webhook_ids)
129
130    def rotate_outgoing_webhook_secret(self, webhook_id: int) -> schedpb.WebhookSecretResponse:
131        res = self.inner.rotate_outgoing_webhook_secret(webhook_id)
132        return schedpb.WebhookSecretResponse.FromString(bytes(res))
133
134
135class Node(object):
136
137    def __init__(
138        self, node_id: bytes, grpc_uri: str, creds: Credentials
139    ) -> None:
140        self.creds = creds
141        self.grpc_uri = grpc_uri
142        self.inner = native.Node(
143            node_id=node_id, grpc_uri=grpc_uri, creds=creds
144        )
145        self.logger = logging.getLogger("glclient.Node")
146
147    def get_info(self) -> clnpb.GetinfoResponse:
148        uri = "/cln.Node/Getinfo"
149        req = clnpb.GetinfoRequest().SerializeToString()
150        res = clnpb.GetinfoResponse
151
152        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
153
154    def stop(self) -> None:
155        uri = "/cln.Node/Stop"
156        req = clnpb.StopRequest().SerializeToString()
157
158        try:
159            # This fails, since we just get disconnected, but that's
160            # on purpose, so drop the error silently.
161            self.inner.call(uri, bytes(req))
162        except ValueError as e:
163            self.logger.debug(
164                f"Caught an expected exception: {e}. Don't worry it's expected."
165            )
166
167    def list_funds(
168        self,
169        spent: Optional[bool] = None,
170    ) -> clnpb.ListfundsResponse:
171        uri = "/cln.Node/ListFunds"
172        res = clnpb.ListfundsResponse
173        req = clnpb.ListfundsRequest(
174            spent=spent,
175        ).SerializeToString()
176
177        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
178
179    def list_peers(self) -> clnpb.ListpeersResponse:
180        uri = "/cln.Node/ListPeers"
181        req = clnpb.ListpeersRequest().SerializeToString()
182        res = clnpb.ListpeersResponse
183
184        return res.FromString(
185            bytes(self.inner.call(uri, bytes(req)))
186        )
187
188    def list_peer_channels(
189            self,
190            node_id: Optional[bytes] = None
191    ) -> clnpb.ListpeerchannelsResponse:
192        uri = "/cln.Node/ListPeerChannels"
193        req = clnpb.ListpeerchannelsRequest(
194            id=node_id,
195        ).SerializeToString()
196        res = clnpb.ListpeerchannelsResponse
197        return res.FromString(
198            bytes(self.inner.call(uri, bytes(req)))
199        )
200
201    def list_closed_channels(self) -> clnpb.ListclosedchannelsResponse:
202        uri = "/cln.Node/ListClosedChannels"
203        req = clnpb.ListclosedchannelsRequest().SerializeToString()
204        res = clnpb.ListclosedchannelsResponse
205
206        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
207
208    def list_channels(
209        self,
210        short_channel_id: Optional[str] = None,
211        source: Optional[bytes] = None,
212        destination: Optional[bytes] = None,
213    ) -> clnpb.ListchannelsResponse:
214        uri = "/cln.Node/ListChannels"
215        req = clnpb.ListchannelsRequest(
216            short_channel_id=short_channel_id, source=source, destination=destination
217        ).SerializeToString()
218        res = clnpb.ListchannelsResponse
219
220        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
221
222    def listpays(
223        self,
224        bolt11: Optional[str] = None,
225        payment_hash: Optional[bytes] = None,
226        status: Optional[clnpb.ListpaysRequest.ListpaysStatus.ValueType] = None,
227    ) -> clnpb.ListpaysResponse:
228        uri = "/cln.Node/ListPays"
229        req = clnpb.ListpaysRequest(
230            bolt11=bolt11,
231            payment_hash=payment_hash,
232            status=status,
233        ).SerializeToString()
234        res = clnpb.ListpaysResponse
235
236        return res.FromString(bytes(self.inner.call(uri, req)))
237
238    def list_invoices(
239        self,
240        label: Optional[str] = None,
241        invstring: Optional[str] = None,
242        payment_hash: Optional[bytes] = None,
243        offer_id: Optional[str] = None,
244        index: Optional[clnpb.ListinvoicesRequest.ListinvoicesIndex.ValueType] = None,
245        start: Optional[int] = None,
246        limit: Optional[int] = None,
247    ) -> clnpb.ListinvoicesResponse:
248        uri = "/cln.Node/ListInvoices"
249        res = clnpb.ListinvoicesResponse
250        req = clnpb.ListinvoicesRequest(
251            label=label,
252            invstring=invstring,
253            payment_hash=payment_hash,
254            offer_id=offer_id,
255            index=index,
256            start=start,
257            limit=limit,
258        ).SerializeToString()
259        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
260
261    def connect_peer(
262        self, node_id, host: Optional[str] = None, port: Optional[int] = None
263    ) -> clnpb.ConnectResponse:
264        if len(node_id) == 33:
265            node_id = hexlify(node_id)
266
267        if isinstance(node_id, bytes):
268            node_id = node_id.decode("ASCII")
269
270        uri = "/cln.Node/ConnectPeer"
271        res = clnpb.ConnectResponse
272        req = clnpb.ConnectRequest(
273            id=node_id,
274            host=host,
275            port=port,
276        ).SerializeToString()
277
278        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
279
280    def decode(self, string: str) -> clnpb.DecodeResponse:
281        uri = "/cln.Node/Decode"
282        res = clnpb.DecodeResponse
283        req = clnpb.DecodeRequest(
284            string=string,
285        ).SerializeToString()
286
287        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
288
289    def decodepay(
290        self, bolt11: str, description: Optional[str]
291    ) -> clnpb.DecodepayResponse:
292        uri = "/cln.Node/DecodePay"
293        res = clnpb.DecodepayResponse
294        req = clnpb.DecodepayRequest(
295            bolt11=bolt11,
296            description=description,
297        ).SerializeToString()
298
299        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
300
301    def disconnect_peer(self, peer_id: str, force=False) -> clnpb.DisconnectResponse:
302        uri = "/cln.Node/Disconnect"
303        res = clnpb.DisconnectResponse
304        req = clnpb.DisconnectRequest(
305            id=bytes.fromhex(peer_id),
306            force=force,
307        ).SerializeToString()
308
309        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
310
311    def new_address(self) -> clnpb.NewaddrResponse:
312        uri = "/cln.Node/NewAddr"
313        req = clnpb.NewaddrRequest().SerializeToString()
314        res = clnpb.NewaddrResponse
315
316        return res.FromString(bytes(self.inner.call(uri, req)))
317
318    def withdraw(
319        self, destination, amount: AmountOrAll, minconf: int = 0
320    ) -> clnpb.WithdrawResponse:
321        uri = "/cln.Node/Withdraw"
322        res = clnpb.WithdrawResponse
323        req = clnpb.WithdrawRequest(
324            destination=destination, satoshi=amount, minconf=minconf
325        ).SerializeToString()
326
327        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
328
329    def fund_channel(
330        self,
331        id: bytes,
332        amount,
333        announce: Optional[bool] = False,
334        minconf: Optional[int] = 1,
335    ) -> clnpb.FundchannelResponse:
336
337        if len(id) != 33:
338            raise ValueError("id is not 33 bytes long")
339
340        uri = "/cln.Node/FundChannel"
341        res = clnpb.FundchannelResponse
342        req = clnpb.FundchannelRequest(
343            id=id,
344            amount=amount,
345            announce=announce,
346            minconf=minconf,
347        ).SerializeToString()
348
349        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
350
351    def close(
352        self, id: bytes, unilateraltimeout=None, destination=None
353    ) -> clnpb.CloseResponse:
354        if len(id) != 33:
355            raise ValueError("node_id is not 33 bytes long")
356
357        uri = "/cln.Node/Close"
358        res = clnpb.CloseResponse
359        req = clnpb.CloseRequest(
360            id=id.hex(),
361            unilateraltimeout=unilateraltimeout,
362            destination=destination,
363        ).SerializeToString()
364
365        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
366
367    def invoice(
368        self,
369        amount_msat: clnpb.AmountOrAny,
370        label: str,
371        description: str,
372        expiry: Optional[int] = None,
373        fallbacks: Optional[List[str]] = None,
374        preimage: Optional[bytes] = None,
375        cltv: Optional[int] = None,
376        deschashonly: Optional[bool] = None,
377    ) -> clnpb.InvoiceResponse:
378        if preimage and len(preimage) != 32:
379            raise ValueError("Preimage must be 32 bytes in length")
380
381        uri = "/cln.Node/Invoice"
382        res = clnpb.InvoiceResponse
383        req = clnpb.InvoiceRequest(
384            amount_msat=amount_msat,
385            label=label,
386            description=description,
387            preimage=preimage,
388            expiry=expiry,
389            fallbacks=fallbacks,
390            cltv=cltv,
391            deschashonly=deschashonly,
392        ).SerializeToString()
393
394        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
395
396    def pay(
397        self,
398        bolt11: str,
399        amount_msat: Optional[clnpb.Amount] = None,
400        retry_for: int = 0,
401        maxfee: Optional[clnpb.Amount] = None,
402        maxfeepercent: Optional[float] = None,
403    ) -> clnpb.PayResponse:
404        uri = "/cln.Node/Pay"
405        res = clnpb.PayResponse
406        req = clnpb.PayRequest(
407            bolt11=bolt11,
408            amount_msat=amount_msat,
409            retry_for=retry_for,
410            maxfeepercent=maxfeepercent,
411            maxfee=maxfee,
412        ).SerializeToString()
413
414        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
415
416    def trampoline_pay(
417            self,
418            bolt11: str,
419            trampoline_node_id: bytes,
420            amount_msat: Optional[int] = None,
421            label: Optional[str] = None
422    ):
423        res = self.inner.trampoline_pay(
424            bolt11=bolt11,
425            trampoline_node_id=trampoline_node_id,
426            amount_msat=amount_msat,
427            label=label,
428        )
429        return nodepb.TrampolinePayResponse.FromString(bytes(res))
430
431    def keysend(
432        self,
433        destination: bytes,
434        amount: clnpb.Amount,
435        label: Optional[str] = None,
436        routehints: Optional[clnpb.RoutehintList] = None,
437        extratlvs: Optional[clnpb.TlvStream] = None,
438    ) -> clnpb.KeysendResponse:
439        uri = "/cln.Node/KeySend"
440        res = clnpb.KeysendResponse
441        req = clnpb.KeysendRequest(
442            destination=normalize_node_id(destination, string=False),
443            amount_msat=amount,
444            label=label if label else "",
445            routehints=routehints,
446            extratlvs=extratlvs,
447        ).SerializeToString()
448
449        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
450
451    def stream_log(self):
452        """Stream logs as they get generated on the server side."""
453        stream = self.inner.stream_log(b"")
454        while True:
455            n = stream.next()
456            if n is None:
457                break
458            yield nodepb.LogEntry.FromString(bytes(n))
459
460    def stream_incoming(self):
461        stream = self.inner.stream_incoming(b"")
462        while True:
463            n = stream.next()
464            if n is None:
465                break
466            yield nodepb.IncomingPayment.FromString(bytes(n))
467
468    def stream_custommsg(self):
469        stream = self.inner.stream_custommsg(b"")
470        while True:
471            n = stream.next()
472            if n is None:
473                break
474            yield nodepb.Custommsg.FromString(bytes(n))
475
476    def send_custommsg(self, node_id: str, msg: bytes) -> clnpb.SendcustommsgResponse:
477        uri = "/cln.Node/SendCustomMsg"
478        res = clnpb.SendcustommsgResponse
479        req = clnpb.SendcustommsgRequest(
480            node_id=normalize_node_id(node_id),
481            msg=msg,
482        ).SerializeToString()
483
484        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
485
486    def datastore(self, key, string=None, hex=None, mode=None, generation=None):
487        uri = "/cln.Node/Datastore"
488        req = clnpb.DatastoreRequest(
489            key=key, string=string, hex=hex, mode=mode, generation=generation
490        ).SerializeToString()
491        res = clnpb.DatastoreResponse
492        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
493
494    def del_datastore(self, key, generation=None):
495        uri = "/cln.Node/DelDatastore"
496        req = clnpb.DeldatastoreRequest(
497            key=key, generation=generation
498        ).SerializeToString()
499        res = clnpb.DeldatastoreResponse
500        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
501
502    def list_datastore(self, key=None):
503        uri = "/cln.Node/ListDatastore"
504        req = clnpb.ListdatastoreRequest(key=key).SerializeToString()
505        res = clnpb.ListdatastoreResponse
506        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
507
508    def get_lsp_client(
509        self,
510    ) -> LspClient:
511        native_lsps = self.inner.get_lsp_client()
512        return LspClient(native_lsps)
513
514    def configure(self, close_to_addr: str) -> None:
515        req = nodepb.GlConfig(close_to_addr=close_to_addr).SerializeToString()
516
517        return self.inner.configure(req)
518
519    def wait_blockheight(
520        self,
521        blockheight: int,
522        timeout: Optional[int] = None,
523    ):
524        """Wait until the blockchain has reached the specified blockheight."""
525        uri = "/cln.Node/WaitBlockheight"
526        req = clnpb.WaitblockheightRequest(
527            blockheight=blockheight, timeout=timeout
528        ).SerializeToString()
529        res = clnpb.WaitblockheightResponse
530        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
531
532    def fetch_invoice(
533        self,
534        offer: str,
535        amount_msat: Optional[Amount] = None,
536        quantity: Optional[int] = None,
537        recurrence_counter: Optional[int] = None,
538        recurrence_start: Optional[int] = None,
539        recurrence_label: Optional[str] = None,
540        timeout: Optional[int] = None,
541        payer_note: Optional[str] = None,
542    ) -> clnpb.FetchinvoiceResponse:
543        """Fetch an invoice based on an offer.
544
545        Contact the issuer of an offer to get an actual invoice that
546        can be paid. It highlights any changes between the offer and
547        the returned invoice.
548
549        """
550        uri = "/cln.Node/FetchInvoice"
551        req = clnpb.FetchinvoiceRequest(
552            offer=offer,
553            amount_msat=amount_msat,
554            quantity=quantity,
555            recurrence_counter=recurrence_counter,
556            recurrence_start=recurrence_start,
557            recurrence_label=recurrence_label,
558            timeout=timeout,
559            payer_note=payer_note,
560        ).SerializeToString()
561        res = clnpb.FetchinvoiceResponse
562        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
563
564    def wait(self, subsystem, indexname, nextvalue: int) -> clnpb.WaitResponse:
565        """Wait for the next event in the provided subsystem.
566
567        Returns once the index given by indexname in subsystem reaches
568        or exceeds nextvalue.
569
570        """
571        uri = "/cln.Node/Wait"
572        req = clnpb.WaitRequest(
573            subsystem=subsystem,
574            indexname=indexname,
575            nextvalue=nextvalue,
576        ).SerializeToString()
577        res = clnpb.WaitResponse
578        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
579
580
581def normalize_node_id(node_id, string=False):
582    if len(node_id) == 66:
583        node_id = unhexlify(node_id)
584
585    if len(node_id) != 33:
586        raise ValueError("node_id is not 33 (binary) or 66 (hex) bytes long")
587
588    if isinstance(node_id, str):
589        node_id = node_id.encode("ASCII")
590    return node_id if not string else hexlify(node_id).encode("ASCII")
def backup_decrypt_with_seed(encrypted, seed):
class Signer:
31class Signer(object):
32    def __init__(self, secret: bytes, network: str, creds: Credentials):
33        self.inner = native.Signer(secret, network, creds)
34        self.creds = creds
35        self.handle: Optional[native.SignerHandle] = None
36
37    def run_in_thread(self) -> "native.SignerHandle":
38        if self.handle is not None:
39            raise ValueError(
40                "This signer is already running, please shut it down before starting it again"
41            )
42        self.handle = self.inner.run_in_thread()
43        return self.handle
44
45    def run_in_foreground(self) -> None:
46        return self.inner.run_in_foreground()
47
48    def node_id(self) -> bytes:
49        return bytes(self.inner.node_id())
50
51    def version(self) -> str:
52        return self.inner.version()
53
54    def sign_challenge(self, message: bytes) -> bytes:
55        return bytes(self.inner.sign_challenge(message))
56
57    def shutdown(self) -> None:
58        if self.handle is None:
59            raise ValueError("Attempted to shut down a signer that is not running")
60        self.handle.shutdown()
61        self.handle = None
62
63    def create_rune(
64        self, restrictions: List[List[str]], rune: Optional[str] = None
65    ) -> str:
66        return self.inner.create_rune(restrictions, rune)
67
68    def is_running(self) -> bool:
69        return self.handle is not None
Signer(secret: bytes, network: str, creds: Credentials)
32    def __init__(self, secret: bytes, network: str, creds: Credentials):
33        self.inner = native.Signer(secret, network, creds)
34        self.creds = creds
35        self.handle: Optional[native.SignerHandle] = None
def run_in_thread(self) -> SignerHandle:
37    def run_in_thread(self) -> "native.SignerHandle":
38        if self.handle is not None:
39            raise ValueError(
40                "This signer is already running, please shut it down before starting it again"
41            )
42        self.handle = self.inner.run_in_thread()
43        return self.handle
def run_in_foreground(self) -> None:
45    def run_in_foreground(self) -> None:
46        return self.inner.run_in_foreground()
def node_id(self) -> bytes:
48    def node_id(self) -> bytes:
49        return bytes(self.inner.node_id())
def version(self) -> str:
51    def version(self) -> str:
52        return self.inner.version()
def sign_challenge(self, message: bytes) -> bytes:
54    def sign_challenge(self, message: bytes) -> bytes:
55        return bytes(self.inner.sign_challenge(message))
def shutdown(self) -> None:
57    def shutdown(self) -> None:
58        if self.handle is None:
59            raise ValueError("Attempted to shut down a signer that is not running")
60        self.handle.shutdown()
61        self.handle = None
def create_rune(self, restrictions: List[List[str]], rune: Optional[str] = None) -> str:
63    def create_rune(
64        self, restrictions: List[List[str]], rune: Optional[str] = None
65    ) -> str:
66        return self.inner.create_rune(restrictions, rune)
def is_running(self) -> bool:
68    def is_running(self) -> bool:
69        return self.handle is not None
class Scheduler:
 72class Scheduler(object):
 73
 74    def __init__(self, network: str, creds: Optional[Credentials] = None):
 75        self.network = network
 76        self.creds = creds if creds is not None else native.Credentials()
 77        self.inner = native.Scheduler(network, self.creds)
 78
 79    def schedule(self) -> schedpb.NodeInfoResponse:
 80        res = self.inner.schedule()
 81        return schedpb.NodeInfoResponse.FromString(bytes(res))
 82
 83    def get_node_info(self, wait: bool = False):
 84        res = self.inner.get_node_info(wait)
 85        return schedpb.NodeInfoResponse.FromString(bytes(res))
 86
 87    def register(self, signer: Signer, invite_code: Optional[str] = None) -> schedpb.RegistrationResponse:
 88        res = self.inner.register(signer.inner, invite_code)
 89        return schedpb.RegistrationResponse.FromString(bytes(res))
 90
 91    def recover(self, signer: Signer) -> schedpb.RecoveryResponse:
 92        res = self.inner.recover(signer.inner)
 93        return schedpb.RecoveryResponse.FromString(bytes(res))
 94
 95    def authenticate(self, creds: Credentials):
 96        self.creds = creds
 97        self.inner = self.inner.authenticate(creds)
 98        return self
 99
100    def export_node(self) -> schedpb.ExportNodeResponse:
101        res = schedpb.ExportNodeResponse
102        return res.FromString(bytes(self.inner.export_node()))
103
104    def node(self) -> "Node":
105        res = self.inner.node()
106        info = schedpb.NodeInfoResponse.FromString(bytes(res))
107        return Node(
108            node_id=self.creds.node_id(),
109            grpc_uri=info.grpc_uri,
110            creds=self.creds,
111        )
112
113    def get_invite_codes(self) -> schedpb.ListInviteCodesResponse:
114        cls = schedpb.ListInviteCodesResponse
115        return cls.FromString(bytes(self.inner.get_invite_codes()))
116
117    def add_outgoing_webhook(self, uri: str) -> schedpb.AddOutgoingWebhookResponse:
118        res = self.inner.add_outgoing_webhook(uri)
119        return schedpb.AddOutgoingWebhookResponse.FromString(bytes(res))
120
121    def list_outgoing_webhooks(self) -> schedpb.ListOutgoingWebhooksResponse:
122        res = self.inner.list_outgoing_webhooks()
123        return schedpb.ListOutgoingWebhooksResponse.FromString(bytes(res))
124
125    def delete_outgoing_webhook(self, webhook_id: int) -> None:
126        res = self.inner.delete_outgoing_webhooks([webhook_id])
127
128    def delete_outgoing_webhooks(self, webhook_ids: List[int]) -> None:
129        res = self.inner.delete_outgoing_webhooks(webhook_ids)
130
131    def rotate_outgoing_webhook_secret(self, webhook_id: int) -> schedpb.WebhookSecretResponse:
132        res = self.inner.rotate_outgoing_webhook_secret(webhook_id)
133        return schedpb.WebhookSecretResponse.FromString(bytes(res))
Scheduler(network: str, creds: Optional[Credentials] = None)
74    def __init__(self, network: str, creds: Optional[Credentials] = None):
75        self.network = network
76        self.creds = creds if creds is not None else native.Credentials()
77        self.inner = native.Scheduler(network, self.creds)
def schedule(self) -> glclient.scheduler_pb2.NodeInfoResponse:
79    def schedule(self) -> schedpb.NodeInfoResponse:
80        res = self.inner.schedule()
81        return schedpb.NodeInfoResponse.FromString(bytes(res))
def get_node_info(self, wait: bool = False):
83    def get_node_info(self, wait: bool = False):
84        res = self.inner.get_node_info(wait)
85        return schedpb.NodeInfoResponse.FromString(bytes(res))
def register( self, signer: glclient.Signer, invite_code: Optional[str] = None) -> glclient.scheduler_pb2.RegistrationResponse:
87    def register(self, signer: Signer, invite_code: Optional[str] = None) -> schedpb.RegistrationResponse:
88        res = self.inner.register(signer.inner, invite_code)
89        return schedpb.RegistrationResponse.FromString(bytes(res))
def recover(self, signer: glclient.Signer) -> glclient.scheduler_pb2.RecoveryResponse:
91    def recover(self, signer: Signer) -> schedpb.RecoveryResponse:
92        res = self.inner.recover(signer.inner)
93        return schedpb.RecoveryResponse.FromString(bytes(res))
def authenticate(self, creds: Credentials):
95    def authenticate(self, creds: Credentials):
96        self.creds = creds
97        self.inner = self.inner.authenticate(creds)
98        return self
def export_node(self) -> glclient.scheduler_pb2.ExportNodeResponse:
100    def export_node(self) -> schedpb.ExportNodeResponse:
101        res = schedpb.ExportNodeResponse
102        return res.FromString(bytes(self.inner.export_node()))
def node(self) -> glclient.Node:
104    def node(self) -> "Node":
105        res = self.inner.node()
106        info = schedpb.NodeInfoResponse.FromString(bytes(res))
107        return Node(
108            node_id=self.creds.node_id(),
109            grpc_uri=info.grpc_uri,
110            creds=self.creds,
111        )
def get_invite_codes(self) -> glclient.scheduler_pb2.ListInviteCodesResponse:
113    def get_invite_codes(self) -> schedpb.ListInviteCodesResponse:
114        cls = schedpb.ListInviteCodesResponse
115        return cls.FromString(bytes(self.inner.get_invite_codes()))
def add_outgoing_webhook(self, uri: str) -> glclient.scheduler_pb2.AddOutgoingWebhookResponse:
117    def add_outgoing_webhook(self, uri: str) -> schedpb.AddOutgoingWebhookResponse:
118        res = self.inner.add_outgoing_webhook(uri)
119        return schedpb.AddOutgoingWebhookResponse.FromString(bytes(res))
def list_outgoing_webhooks(self) -> glclient.scheduler_pb2.ListOutgoingWebhooksResponse:
121    def list_outgoing_webhooks(self) -> schedpb.ListOutgoingWebhooksResponse:
122        res = self.inner.list_outgoing_webhooks()
123        return schedpb.ListOutgoingWebhooksResponse.FromString(bytes(res))
def delete_outgoing_webhook(self, webhook_id: int) -> None:
125    def delete_outgoing_webhook(self, webhook_id: int) -> None:
126        res = self.inner.delete_outgoing_webhooks([webhook_id])
def delete_outgoing_webhooks(self, webhook_ids: List[int]) -> None:
128    def delete_outgoing_webhooks(self, webhook_ids: List[int]) -> None:
129        res = self.inner.delete_outgoing_webhooks(webhook_ids)
def rotate_outgoing_webhook_secret(self, webhook_id: int) -> glclient.scheduler_pb2.WebhookSecretResponse:
131    def rotate_outgoing_webhook_secret(self, webhook_id: int) -> schedpb.WebhookSecretResponse:
132        res = self.inner.rotate_outgoing_webhook_secret(webhook_id)
133        return schedpb.WebhookSecretResponse.FromString(bytes(res))
class Node:
136class Node(object):
137
138    def __init__(
139        self, node_id: bytes, grpc_uri: str, creds: Credentials
140    ) -> None:
141        self.creds = creds
142        self.grpc_uri = grpc_uri
143        self.inner = native.Node(
144            node_id=node_id, grpc_uri=grpc_uri, creds=creds
145        )
146        self.logger = logging.getLogger("glclient.Node")
147
148    def get_info(self) -> clnpb.GetinfoResponse:
149        uri = "/cln.Node/Getinfo"
150        req = clnpb.GetinfoRequest().SerializeToString()
151        res = clnpb.GetinfoResponse
152
153        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
154
155    def stop(self) -> None:
156        uri = "/cln.Node/Stop"
157        req = clnpb.StopRequest().SerializeToString()
158
159        try:
160            # This fails, since we just get disconnected, but that's
161            # on purpose, so drop the error silently.
162            self.inner.call(uri, bytes(req))
163        except ValueError as e:
164            self.logger.debug(
165                f"Caught an expected exception: {e}. Don't worry it's expected."
166            )
167
168    def list_funds(
169        self,
170        spent: Optional[bool] = None,
171    ) -> clnpb.ListfundsResponse:
172        uri = "/cln.Node/ListFunds"
173        res = clnpb.ListfundsResponse
174        req = clnpb.ListfundsRequest(
175            spent=spent,
176        ).SerializeToString()
177
178        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
179
180    def list_peers(self) -> clnpb.ListpeersResponse:
181        uri = "/cln.Node/ListPeers"
182        req = clnpb.ListpeersRequest().SerializeToString()
183        res = clnpb.ListpeersResponse
184
185        return res.FromString(
186            bytes(self.inner.call(uri, bytes(req)))
187        )
188
189    def list_peer_channels(
190            self,
191            node_id: Optional[bytes] = None
192    ) -> clnpb.ListpeerchannelsResponse:
193        uri = "/cln.Node/ListPeerChannels"
194        req = clnpb.ListpeerchannelsRequest(
195            id=node_id,
196        ).SerializeToString()
197        res = clnpb.ListpeerchannelsResponse
198        return res.FromString(
199            bytes(self.inner.call(uri, bytes(req)))
200        )
201
202    def list_closed_channels(self) -> clnpb.ListclosedchannelsResponse:
203        uri = "/cln.Node/ListClosedChannels"
204        req = clnpb.ListclosedchannelsRequest().SerializeToString()
205        res = clnpb.ListclosedchannelsResponse
206
207        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
208
209    def list_channels(
210        self,
211        short_channel_id: Optional[str] = None,
212        source: Optional[bytes] = None,
213        destination: Optional[bytes] = None,
214    ) -> clnpb.ListchannelsResponse:
215        uri = "/cln.Node/ListChannels"
216        req = clnpb.ListchannelsRequest(
217            short_channel_id=short_channel_id, source=source, destination=destination
218        ).SerializeToString()
219        res = clnpb.ListchannelsResponse
220
221        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
222
223    def listpays(
224        self,
225        bolt11: Optional[str] = None,
226        payment_hash: Optional[bytes] = None,
227        status: Optional[clnpb.ListpaysRequest.ListpaysStatus.ValueType] = None,
228    ) -> clnpb.ListpaysResponse:
229        uri = "/cln.Node/ListPays"
230        req = clnpb.ListpaysRequest(
231            bolt11=bolt11,
232            payment_hash=payment_hash,
233            status=status,
234        ).SerializeToString()
235        res = clnpb.ListpaysResponse
236
237        return res.FromString(bytes(self.inner.call(uri, req)))
238
239    def list_invoices(
240        self,
241        label: Optional[str] = None,
242        invstring: Optional[str] = None,
243        payment_hash: Optional[bytes] = None,
244        offer_id: Optional[str] = None,
245        index: Optional[clnpb.ListinvoicesRequest.ListinvoicesIndex.ValueType] = None,
246        start: Optional[int] = None,
247        limit: Optional[int] = None,
248    ) -> clnpb.ListinvoicesResponse:
249        uri = "/cln.Node/ListInvoices"
250        res = clnpb.ListinvoicesResponse
251        req = clnpb.ListinvoicesRequest(
252            label=label,
253            invstring=invstring,
254            payment_hash=payment_hash,
255            offer_id=offer_id,
256            index=index,
257            start=start,
258            limit=limit,
259        ).SerializeToString()
260        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
261
262    def connect_peer(
263        self, node_id, host: Optional[str] = None, port: Optional[int] = None
264    ) -> clnpb.ConnectResponse:
265        if len(node_id) == 33:
266            node_id = hexlify(node_id)
267
268        if isinstance(node_id, bytes):
269            node_id = node_id.decode("ASCII")
270
271        uri = "/cln.Node/ConnectPeer"
272        res = clnpb.ConnectResponse
273        req = clnpb.ConnectRequest(
274            id=node_id,
275            host=host,
276            port=port,
277        ).SerializeToString()
278
279        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
280
281    def decode(self, string: str) -> clnpb.DecodeResponse:
282        uri = "/cln.Node/Decode"
283        res = clnpb.DecodeResponse
284        req = clnpb.DecodeRequest(
285            string=string,
286        ).SerializeToString()
287
288        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
289
290    def decodepay(
291        self, bolt11: str, description: Optional[str]
292    ) -> clnpb.DecodepayResponse:
293        uri = "/cln.Node/DecodePay"
294        res = clnpb.DecodepayResponse
295        req = clnpb.DecodepayRequest(
296            bolt11=bolt11,
297            description=description,
298        ).SerializeToString()
299
300        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
301
302    def disconnect_peer(self, peer_id: str, force=False) -> clnpb.DisconnectResponse:
303        uri = "/cln.Node/Disconnect"
304        res = clnpb.DisconnectResponse
305        req = clnpb.DisconnectRequest(
306            id=bytes.fromhex(peer_id),
307            force=force,
308        ).SerializeToString()
309
310        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
311
312    def new_address(self) -> clnpb.NewaddrResponse:
313        uri = "/cln.Node/NewAddr"
314        req = clnpb.NewaddrRequest().SerializeToString()
315        res = clnpb.NewaddrResponse
316
317        return res.FromString(bytes(self.inner.call(uri, req)))
318
319    def withdraw(
320        self, destination, amount: AmountOrAll, minconf: int = 0
321    ) -> clnpb.WithdrawResponse:
322        uri = "/cln.Node/Withdraw"
323        res = clnpb.WithdrawResponse
324        req = clnpb.WithdrawRequest(
325            destination=destination, satoshi=amount, minconf=minconf
326        ).SerializeToString()
327
328        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
329
330    def fund_channel(
331        self,
332        id: bytes,
333        amount,
334        announce: Optional[bool] = False,
335        minconf: Optional[int] = 1,
336    ) -> clnpb.FundchannelResponse:
337
338        if len(id) != 33:
339            raise ValueError("id is not 33 bytes long")
340
341        uri = "/cln.Node/FundChannel"
342        res = clnpb.FundchannelResponse
343        req = clnpb.FundchannelRequest(
344            id=id,
345            amount=amount,
346            announce=announce,
347            minconf=minconf,
348        ).SerializeToString()
349
350        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
351
352    def close(
353        self, id: bytes, unilateraltimeout=None, destination=None
354    ) -> clnpb.CloseResponse:
355        if len(id) != 33:
356            raise ValueError("node_id is not 33 bytes long")
357
358        uri = "/cln.Node/Close"
359        res = clnpb.CloseResponse
360        req = clnpb.CloseRequest(
361            id=id.hex(),
362            unilateraltimeout=unilateraltimeout,
363            destination=destination,
364        ).SerializeToString()
365
366        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
367
368    def invoice(
369        self,
370        amount_msat: clnpb.AmountOrAny,
371        label: str,
372        description: str,
373        expiry: Optional[int] = None,
374        fallbacks: Optional[List[str]] = None,
375        preimage: Optional[bytes] = None,
376        cltv: Optional[int] = None,
377        deschashonly: Optional[bool] = None,
378    ) -> clnpb.InvoiceResponse:
379        if preimage and len(preimage) != 32:
380            raise ValueError("Preimage must be 32 bytes in length")
381
382        uri = "/cln.Node/Invoice"
383        res = clnpb.InvoiceResponse
384        req = clnpb.InvoiceRequest(
385            amount_msat=amount_msat,
386            label=label,
387            description=description,
388            preimage=preimage,
389            expiry=expiry,
390            fallbacks=fallbacks,
391            cltv=cltv,
392            deschashonly=deschashonly,
393        ).SerializeToString()
394
395        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
396
397    def pay(
398        self,
399        bolt11: str,
400        amount_msat: Optional[clnpb.Amount] = None,
401        retry_for: int = 0,
402        maxfee: Optional[clnpb.Amount] = None,
403        maxfeepercent: Optional[float] = None,
404    ) -> clnpb.PayResponse:
405        uri = "/cln.Node/Pay"
406        res = clnpb.PayResponse
407        req = clnpb.PayRequest(
408            bolt11=bolt11,
409            amount_msat=amount_msat,
410            retry_for=retry_for,
411            maxfeepercent=maxfeepercent,
412            maxfee=maxfee,
413        ).SerializeToString()
414
415        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
416
417    def trampoline_pay(
418            self,
419            bolt11: str,
420            trampoline_node_id: bytes,
421            amount_msat: Optional[int] = None,
422            label: Optional[str] = None
423    ):
424        res = self.inner.trampoline_pay(
425            bolt11=bolt11,
426            trampoline_node_id=trampoline_node_id,
427            amount_msat=amount_msat,
428            label=label,
429        )
430        return nodepb.TrampolinePayResponse.FromString(bytes(res))
431
432    def keysend(
433        self,
434        destination: bytes,
435        amount: clnpb.Amount,
436        label: Optional[str] = None,
437        routehints: Optional[clnpb.RoutehintList] = None,
438        extratlvs: Optional[clnpb.TlvStream] = None,
439    ) -> clnpb.KeysendResponse:
440        uri = "/cln.Node/KeySend"
441        res = clnpb.KeysendResponse
442        req = clnpb.KeysendRequest(
443            destination=normalize_node_id(destination, string=False),
444            amount_msat=amount,
445            label=label if label else "",
446            routehints=routehints,
447            extratlvs=extratlvs,
448        ).SerializeToString()
449
450        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
451
452    def stream_log(self):
453        """Stream logs as they get generated on the server side."""
454        stream = self.inner.stream_log(b"")
455        while True:
456            n = stream.next()
457            if n is None:
458                break
459            yield nodepb.LogEntry.FromString(bytes(n))
460
461    def stream_incoming(self):
462        stream = self.inner.stream_incoming(b"")
463        while True:
464            n = stream.next()
465            if n is None:
466                break
467            yield nodepb.IncomingPayment.FromString(bytes(n))
468
469    def stream_custommsg(self):
470        stream = self.inner.stream_custommsg(b"")
471        while True:
472            n = stream.next()
473            if n is None:
474                break
475            yield nodepb.Custommsg.FromString(bytes(n))
476
477    def send_custommsg(self, node_id: str, msg: bytes) -> clnpb.SendcustommsgResponse:
478        uri = "/cln.Node/SendCustomMsg"
479        res = clnpb.SendcustommsgResponse
480        req = clnpb.SendcustommsgRequest(
481            node_id=normalize_node_id(node_id),
482            msg=msg,
483        ).SerializeToString()
484
485        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
486
487    def datastore(self, key, string=None, hex=None, mode=None, generation=None):
488        uri = "/cln.Node/Datastore"
489        req = clnpb.DatastoreRequest(
490            key=key, string=string, hex=hex, mode=mode, generation=generation
491        ).SerializeToString()
492        res = clnpb.DatastoreResponse
493        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
494
495    def del_datastore(self, key, generation=None):
496        uri = "/cln.Node/DelDatastore"
497        req = clnpb.DeldatastoreRequest(
498            key=key, generation=generation
499        ).SerializeToString()
500        res = clnpb.DeldatastoreResponse
501        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
502
503    def list_datastore(self, key=None):
504        uri = "/cln.Node/ListDatastore"
505        req = clnpb.ListdatastoreRequest(key=key).SerializeToString()
506        res = clnpb.ListdatastoreResponse
507        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
508
509    def get_lsp_client(
510        self,
511    ) -> LspClient:
512        native_lsps = self.inner.get_lsp_client()
513        return LspClient(native_lsps)
514
515    def configure(self, close_to_addr: str) -> None:
516        req = nodepb.GlConfig(close_to_addr=close_to_addr).SerializeToString()
517
518        return self.inner.configure(req)
519
520    def wait_blockheight(
521        self,
522        blockheight: int,
523        timeout: Optional[int] = None,
524    ):
525        """Wait until the blockchain has reached the specified blockheight."""
526        uri = "/cln.Node/WaitBlockheight"
527        req = clnpb.WaitblockheightRequest(
528            blockheight=blockheight, timeout=timeout
529        ).SerializeToString()
530        res = clnpb.WaitblockheightResponse
531        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
532
533    def fetch_invoice(
534        self,
535        offer: str,
536        amount_msat: Optional[Amount] = None,
537        quantity: Optional[int] = None,
538        recurrence_counter: Optional[int] = None,
539        recurrence_start: Optional[int] = None,
540        recurrence_label: Optional[str] = None,
541        timeout: Optional[int] = None,
542        payer_note: Optional[str] = None,
543    ) -> clnpb.FetchinvoiceResponse:
544        """Fetch an invoice based on an offer.
545
546        Contact the issuer of an offer to get an actual invoice that
547        can be paid. It highlights any changes between the offer and
548        the returned invoice.
549
550        """
551        uri = "/cln.Node/FetchInvoice"
552        req = clnpb.FetchinvoiceRequest(
553            offer=offer,
554            amount_msat=amount_msat,
555            quantity=quantity,
556            recurrence_counter=recurrence_counter,
557            recurrence_start=recurrence_start,
558            recurrence_label=recurrence_label,
559            timeout=timeout,
560            payer_note=payer_note,
561        ).SerializeToString()
562        res = clnpb.FetchinvoiceResponse
563        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
564
565    def wait(self, subsystem, indexname, nextvalue: int) -> clnpb.WaitResponse:
566        """Wait for the next event in the provided subsystem.
567
568        Returns once the index given by indexname in subsystem reaches
569        or exceeds nextvalue.
570
571        """
572        uri = "/cln.Node/Wait"
573        req = clnpb.WaitRequest(
574            subsystem=subsystem,
575            indexname=indexname,
576            nextvalue=nextvalue,
577        ).SerializeToString()
578        res = clnpb.WaitResponse
579        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
Node(node_id: bytes, grpc_uri: str, creds: Credentials)
138    def __init__(
139        self, node_id: bytes, grpc_uri: str, creds: Credentials
140    ) -> None:
141        self.creds = creds
142        self.grpc_uri = grpc_uri
143        self.inner = native.Node(
144            node_id=node_id, grpc_uri=grpc_uri, creds=creds
145        )
146        self.logger = logging.getLogger("glclient.Node")
def get_info(self) -> node_pb2.GetinfoResponse:
148    def get_info(self) -> clnpb.GetinfoResponse:
149        uri = "/cln.Node/Getinfo"
150        req = clnpb.GetinfoRequest().SerializeToString()
151        res = clnpb.GetinfoResponse
152
153        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def stop(self) -> None:
155    def stop(self) -> None:
156        uri = "/cln.Node/Stop"
157        req = clnpb.StopRequest().SerializeToString()
158
159        try:
160            # This fails, since we just get disconnected, but that's
161            # on purpose, so drop the error silently.
162            self.inner.call(uri, bytes(req))
163        except ValueError as e:
164            self.logger.debug(
165                f"Caught an expected exception: {e}. Don't worry it's expected."
166            )
def list_funds(self, spent: Optional[bool] = None) -> node_pb2.ListfundsResponse:
168    def list_funds(
169        self,
170        spent: Optional[bool] = None,
171    ) -> clnpb.ListfundsResponse:
172        uri = "/cln.Node/ListFunds"
173        res = clnpb.ListfundsResponse
174        req = clnpb.ListfundsRequest(
175            spent=spent,
176        ).SerializeToString()
177
178        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def list_peers(self) -> node_pb2.ListpeersResponse:
180    def list_peers(self) -> clnpb.ListpeersResponse:
181        uri = "/cln.Node/ListPeers"
182        req = clnpb.ListpeersRequest().SerializeToString()
183        res = clnpb.ListpeersResponse
184
185        return res.FromString(
186            bytes(self.inner.call(uri, bytes(req)))
187        )
def list_peer_channels( self, node_id: Optional[bytes] = None) -> node_pb2.ListpeerchannelsResponse:
189    def list_peer_channels(
190            self,
191            node_id: Optional[bytes] = None
192    ) -> clnpb.ListpeerchannelsResponse:
193        uri = "/cln.Node/ListPeerChannels"
194        req = clnpb.ListpeerchannelsRequest(
195            id=node_id,
196        ).SerializeToString()
197        res = clnpb.ListpeerchannelsResponse
198        return res.FromString(
199            bytes(self.inner.call(uri, bytes(req)))
200        )
def list_closed_channels(self) -> node_pb2.ListclosedchannelsResponse:
202    def list_closed_channels(self) -> clnpb.ListclosedchannelsResponse:
203        uri = "/cln.Node/ListClosedChannels"
204        req = clnpb.ListclosedchannelsRequest().SerializeToString()
205        res = clnpb.ListclosedchannelsResponse
206
207        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def list_channels( self, short_channel_id: Optional[str] = None, source: Optional[bytes] = None, destination: Optional[bytes] = None) -> node_pb2.ListchannelsResponse:
209    def list_channels(
210        self,
211        short_channel_id: Optional[str] = None,
212        source: Optional[bytes] = None,
213        destination: Optional[bytes] = None,
214    ) -> clnpb.ListchannelsResponse:
215        uri = "/cln.Node/ListChannels"
216        req = clnpb.ListchannelsRequest(
217            short_channel_id=short_channel_id, source=source, destination=destination
218        ).SerializeToString()
219        res = clnpb.ListchannelsResponse
220
221        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def listpays( self, bolt11: Optional[str] = None, payment_hash: Optional[bytes] = None, status: Optional[int] = None) -> node_pb2.ListpaysResponse:
223    def listpays(
224        self,
225        bolt11: Optional[str] = None,
226        payment_hash: Optional[bytes] = None,
227        status: Optional[clnpb.ListpaysRequest.ListpaysStatus.ValueType] = None,
228    ) -> clnpb.ListpaysResponse:
229        uri = "/cln.Node/ListPays"
230        req = clnpb.ListpaysRequest(
231            bolt11=bolt11,
232            payment_hash=payment_hash,
233            status=status,
234        ).SerializeToString()
235        res = clnpb.ListpaysResponse
236
237        return res.FromString(bytes(self.inner.call(uri, req)))
def list_invoices( self, label: Optional[str] = None, invstring: Optional[str] = None, payment_hash: Optional[bytes] = None, offer_id: Optional[str] = None, index: Optional[int] = None, start: Optional[int] = None, limit: Optional[int] = None) -> node_pb2.ListinvoicesResponse:
239    def list_invoices(
240        self,
241        label: Optional[str] = None,
242        invstring: Optional[str] = None,
243        payment_hash: Optional[bytes] = None,
244        offer_id: Optional[str] = None,
245        index: Optional[clnpb.ListinvoicesRequest.ListinvoicesIndex.ValueType] = None,
246        start: Optional[int] = None,
247        limit: Optional[int] = None,
248    ) -> clnpb.ListinvoicesResponse:
249        uri = "/cln.Node/ListInvoices"
250        res = clnpb.ListinvoicesResponse
251        req = clnpb.ListinvoicesRequest(
252            label=label,
253            invstring=invstring,
254            payment_hash=payment_hash,
255            offer_id=offer_id,
256            index=index,
257            start=start,
258            limit=limit,
259        ).SerializeToString()
260        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def connect_peer( self, node_id, host: Optional[str] = None, port: Optional[int] = None) -> node_pb2.ConnectResponse:
262    def connect_peer(
263        self, node_id, host: Optional[str] = None, port: Optional[int] = None
264    ) -> clnpb.ConnectResponse:
265        if len(node_id) == 33:
266            node_id = hexlify(node_id)
267
268        if isinstance(node_id, bytes):
269            node_id = node_id.decode("ASCII")
270
271        uri = "/cln.Node/ConnectPeer"
272        res = clnpb.ConnectResponse
273        req = clnpb.ConnectRequest(
274            id=node_id,
275            host=host,
276            port=port,
277        ).SerializeToString()
278
279        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def decode(self, string: str) -> node_pb2.DecodeResponse:
281    def decode(self, string: str) -> clnpb.DecodeResponse:
282        uri = "/cln.Node/Decode"
283        res = clnpb.DecodeResponse
284        req = clnpb.DecodeRequest(
285            string=string,
286        ).SerializeToString()
287
288        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def decodepay( self, bolt11: str, description: Optional[str]) -> node_pb2.DecodepayResponse:
290    def decodepay(
291        self, bolt11: str, description: Optional[str]
292    ) -> clnpb.DecodepayResponse:
293        uri = "/cln.Node/DecodePay"
294        res = clnpb.DecodepayResponse
295        req = clnpb.DecodepayRequest(
296            bolt11=bolt11,
297            description=description,
298        ).SerializeToString()
299
300        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def disconnect_peer(self, peer_id: str, force=False) -> node_pb2.DisconnectResponse:
302    def disconnect_peer(self, peer_id: str, force=False) -> clnpb.DisconnectResponse:
303        uri = "/cln.Node/Disconnect"
304        res = clnpb.DisconnectResponse
305        req = clnpb.DisconnectRequest(
306            id=bytes.fromhex(peer_id),
307            force=force,
308        ).SerializeToString()
309
310        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def new_address(self) -> node_pb2.NewaddrResponse:
312    def new_address(self) -> clnpb.NewaddrResponse:
313        uri = "/cln.Node/NewAddr"
314        req = clnpb.NewaddrRequest().SerializeToString()
315        res = clnpb.NewaddrResponse
316
317        return res.FromString(bytes(self.inner.call(uri, req)))
def withdraw( self, destination, amount: primitives_pb2.AmountOrAll, minconf: int = 0) -> node_pb2.WithdrawResponse:
319    def withdraw(
320        self, destination, amount: AmountOrAll, minconf: int = 0
321    ) -> clnpb.WithdrawResponse:
322        uri = "/cln.Node/Withdraw"
323        res = clnpb.WithdrawResponse
324        req = clnpb.WithdrawRequest(
325            destination=destination, satoshi=amount, minconf=minconf
326        ).SerializeToString()
327
328        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def fund_channel( self, id: bytes, amount, announce: Optional[bool] = False, minconf: Optional[int] = 1) -> node_pb2.FundchannelResponse:
330    def fund_channel(
331        self,
332        id: bytes,
333        amount,
334        announce: Optional[bool] = False,
335        minconf: Optional[int] = 1,
336    ) -> clnpb.FundchannelResponse:
337
338        if len(id) != 33:
339            raise ValueError("id is not 33 bytes long")
340
341        uri = "/cln.Node/FundChannel"
342        res = clnpb.FundchannelResponse
343        req = clnpb.FundchannelRequest(
344            id=id,
345            amount=amount,
346            announce=announce,
347            minconf=minconf,
348        ).SerializeToString()
349
350        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def close( self, id: bytes, unilateraltimeout=None, destination=None) -> node_pb2.CloseResponse:
352    def close(
353        self, id: bytes, unilateraltimeout=None, destination=None
354    ) -> clnpb.CloseResponse:
355        if len(id) != 33:
356            raise ValueError("node_id is not 33 bytes long")
357
358        uri = "/cln.Node/Close"
359        res = clnpb.CloseResponse
360        req = clnpb.CloseRequest(
361            id=id.hex(),
362            unilateraltimeout=unilateraltimeout,
363            destination=destination,
364        ).SerializeToString()
365
366        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def invoice( self, amount_msat: primitives_pb2.AmountOrAny, label: str, description: str, expiry: Optional[int] = None, fallbacks: Optional[List[str]] = None, preimage: Optional[bytes] = None, cltv: Optional[int] = None, deschashonly: Optional[bool] = None) -> node_pb2.InvoiceResponse:
368    def invoice(
369        self,
370        amount_msat: clnpb.AmountOrAny,
371        label: str,
372        description: str,
373        expiry: Optional[int] = None,
374        fallbacks: Optional[List[str]] = None,
375        preimage: Optional[bytes] = None,
376        cltv: Optional[int] = None,
377        deschashonly: Optional[bool] = None,
378    ) -> clnpb.InvoiceResponse:
379        if preimage and len(preimage) != 32:
380            raise ValueError("Preimage must be 32 bytes in length")
381
382        uri = "/cln.Node/Invoice"
383        res = clnpb.InvoiceResponse
384        req = clnpb.InvoiceRequest(
385            amount_msat=amount_msat,
386            label=label,
387            description=description,
388            preimage=preimage,
389            expiry=expiry,
390            fallbacks=fallbacks,
391            cltv=cltv,
392            deschashonly=deschashonly,
393        ).SerializeToString()
394
395        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def pay( self, bolt11: str, amount_msat: Optional[primitives_pb2.Amount] = None, retry_for: int = 0, maxfee: Optional[primitives_pb2.Amount] = None, maxfeepercent: Optional[float] = None) -> node_pb2.PayResponse:
397    def pay(
398        self,
399        bolt11: str,
400        amount_msat: Optional[clnpb.Amount] = None,
401        retry_for: int = 0,
402        maxfee: Optional[clnpb.Amount] = None,
403        maxfeepercent: Optional[float] = None,
404    ) -> clnpb.PayResponse:
405        uri = "/cln.Node/Pay"
406        res = clnpb.PayResponse
407        req = clnpb.PayRequest(
408            bolt11=bolt11,
409            amount_msat=amount_msat,
410            retry_for=retry_for,
411            maxfeepercent=maxfeepercent,
412            maxfee=maxfee,
413        ).SerializeToString()
414
415        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def trampoline_pay( self, bolt11: str, trampoline_node_id: bytes, amount_msat: Optional[int] = None, label: Optional[str] = None):
417    def trampoline_pay(
418            self,
419            bolt11: str,
420            trampoline_node_id: bytes,
421            amount_msat: Optional[int] = None,
422            label: Optional[str] = None
423    ):
424        res = self.inner.trampoline_pay(
425            bolt11=bolt11,
426            trampoline_node_id=trampoline_node_id,
427            amount_msat=amount_msat,
428            label=label,
429        )
430        return nodepb.TrampolinePayResponse.FromString(bytes(res))
def keysend( self, destination: bytes, amount: primitives_pb2.Amount, label: Optional[str] = None, routehints: Optional[primitives_pb2.RoutehintList] = None, extratlvs: Optional[primitives_pb2.TlvStream] = None) -> node_pb2.KeysendResponse:
432    def keysend(
433        self,
434        destination: bytes,
435        amount: clnpb.Amount,
436        label: Optional[str] = None,
437        routehints: Optional[clnpb.RoutehintList] = None,
438        extratlvs: Optional[clnpb.TlvStream] = None,
439    ) -> clnpb.KeysendResponse:
440        uri = "/cln.Node/KeySend"
441        res = clnpb.KeysendResponse
442        req = clnpb.KeysendRequest(
443            destination=normalize_node_id(destination, string=False),
444            amount_msat=amount,
445            label=label if label else "",
446            routehints=routehints,
447            extratlvs=extratlvs,
448        ).SerializeToString()
449
450        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def stream_log(self):
452    def stream_log(self):
453        """Stream logs as they get generated on the server side."""
454        stream = self.inner.stream_log(b"")
455        while True:
456            n = stream.next()
457            if n is None:
458                break
459            yield nodepb.LogEntry.FromString(bytes(n))

Stream logs as they get generated on the server side.

def stream_incoming(self):
461    def stream_incoming(self):
462        stream = self.inner.stream_incoming(b"")
463        while True:
464            n = stream.next()
465            if n is None:
466                break
467            yield nodepb.IncomingPayment.FromString(bytes(n))
def stream_custommsg(self):
469    def stream_custommsg(self):
470        stream = self.inner.stream_custommsg(b"")
471        while True:
472            n = stream.next()
473            if n is None:
474                break
475            yield nodepb.Custommsg.FromString(bytes(n))
def send_custommsg(self, node_id: str, msg: bytes) -> node_pb2.SendcustommsgResponse:
477    def send_custommsg(self, node_id: str, msg: bytes) -> clnpb.SendcustommsgResponse:
478        uri = "/cln.Node/SendCustomMsg"
479        res = clnpb.SendcustommsgResponse
480        req = clnpb.SendcustommsgRequest(
481            node_id=normalize_node_id(node_id),
482            msg=msg,
483        ).SerializeToString()
484
485        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def datastore(self, key, string=None, hex=None, mode=None, generation=None):
487    def datastore(self, key, string=None, hex=None, mode=None, generation=None):
488        uri = "/cln.Node/Datastore"
489        req = clnpb.DatastoreRequest(
490            key=key, string=string, hex=hex, mode=mode, generation=generation
491        ).SerializeToString()
492        res = clnpb.DatastoreResponse
493        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def del_datastore(self, key, generation=None):
495    def del_datastore(self, key, generation=None):
496        uri = "/cln.Node/DelDatastore"
497        req = clnpb.DeldatastoreRequest(
498            key=key, generation=generation
499        ).SerializeToString()
500        res = clnpb.DeldatastoreResponse
501        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def list_datastore(self, key=None):
503    def list_datastore(self, key=None):
504        uri = "/cln.Node/ListDatastore"
505        req = clnpb.ListdatastoreRequest(key=key).SerializeToString()
506        res = clnpb.ListdatastoreResponse
507        return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def get_lsp_client(self) -> glclient.lsps.LspClient:
509    def get_lsp_client(
510        self,
511    ) -> LspClient:
512        native_lsps = self.inner.get_lsp_client()
513        return LspClient(native_lsps)
def configure(self, close_to_addr: str) -> None:
515    def configure(self, close_to_addr: str) -> None:
516        req = nodepb.GlConfig(close_to_addr=close_to_addr).SerializeToString()
517
518        return self.inner.configure(req)
def wait_blockheight(self, blockheight: int, timeout: Optional[int] = None):
520    def wait_blockheight(
521        self,
522        blockheight: int,
523        timeout: Optional[int] = None,
524    ):
525        """Wait until the blockchain has reached the specified blockheight."""
526        uri = "/cln.Node/WaitBlockheight"
527        req = clnpb.WaitblockheightRequest(
528            blockheight=blockheight, timeout=timeout
529        ).SerializeToString()
530        res = clnpb.WaitblockheightResponse
531        return res.FromString(bytes(self.inner.call(uri, bytes(req))))

Wait until the blockchain has reached the specified blockheight.

def fetch_invoice( self, offer: str, amount_msat: Optional[primitives_pb2.Amount] = None, quantity: Optional[int] = None, recurrence_counter: Optional[int] = None, recurrence_start: Optional[int] = None, recurrence_label: Optional[str] = None, timeout: Optional[int] = None, payer_note: Optional[str] = None) -> node_pb2.FetchinvoiceResponse:
533    def fetch_invoice(
534        self,
535        offer: str,
536        amount_msat: Optional[Amount] = None,
537        quantity: Optional[int] = None,
538        recurrence_counter: Optional[int] = None,
539        recurrence_start: Optional[int] = None,
540        recurrence_label: Optional[str] = None,
541        timeout: Optional[int] = None,
542        payer_note: Optional[str] = None,
543    ) -> clnpb.FetchinvoiceResponse:
544        """Fetch an invoice based on an offer.
545
546        Contact the issuer of an offer to get an actual invoice that
547        can be paid. It highlights any changes between the offer and
548        the returned invoice.
549
550        """
551        uri = "/cln.Node/FetchInvoice"
552        req = clnpb.FetchinvoiceRequest(
553            offer=offer,
554            amount_msat=amount_msat,
555            quantity=quantity,
556            recurrence_counter=recurrence_counter,
557            recurrence_start=recurrence_start,
558            recurrence_label=recurrence_label,
559            timeout=timeout,
560            payer_note=payer_note,
561        ).SerializeToString()
562        res = clnpb.FetchinvoiceResponse
563        return res.FromString(bytes(self.inner.call(uri, bytes(req))))

Fetch an invoice based on an offer.

Contact the issuer of an offer to get an actual invoice that can be paid. It highlights any changes between the offer and the returned invoice.

def wait(self, subsystem, indexname, nextvalue: int) -> node_pb2.WaitResponse:
565    def wait(self, subsystem, indexname, nextvalue: int) -> clnpb.WaitResponse:
566        """Wait for the next event in the provided subsystem.
567
568        Returns once the index given by indexname in subsystem reaches
569        or exceeds nextvalue.
570
571        """
572        uri = "/cln.Node/Wait"
573        req = clnpb.WaitRequest(
574            subsystem=subsystem,
575            indexname=indexname,
576            nextvalue=nextvalue,
577        ).SerializeToString()
578        res = clnpb.WaitResponse
579        return res.FromString(bytes(self.inner.call(uri, bytes(req))))

Wait for the next event in the provided subsystem.

Returns once the index given by indexname in subsystem reaches or exceeds nextvalue.

def normalize_node_id(node_id, string=False):
582def normalize_node_id(node_id, string=False):
583    if len(node_id) == 66:
584        node_id = unhexlify(node_id)
585
586    if len(node_id) != 33:
587        raise ValueError("node_id is not 33 (binary) or 66 (hex) bytes long")
588
589    if isinstance(node_id, str):
590        node_id = node_id.encode("ASCII")
591    return node_id if not string else hexlify(node_id).encode("ASCII")