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
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))
def
register( self, signer: glclient.Signer, invite_code: Optional[str] = None) -> glclient.scheduler_pb2.RegistrationResponse:
def
rotate_outgoing_webhook_secret(self, webhook_id: int) -> glclient.scheduler_pb2.WebhookSecretResponse:
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))))
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_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_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
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
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
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
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")