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(self, bolt11: str, trampoline_node_id: bytes, amount_msat: Optional[int] = None, label: Optional[str] = None): 417 res = self.inner.trampoline_pay( 418 bolt11=bolt11, 419 trampoline_node_id=trampoline_node_id, 420 amount_msat=amount_msat, 421 label=label, 422 ) 423 return nodepb.TrampolinePayResponse.FromString(bytes(res)) 424 425 def keysend( 426 self, 427 destination: bytes, 428 amount: clnpb.Amount, 429 label: Optional[str] = None, 430 routehints: Optional[clnpb.RoutehintList] = None, 431 extratlvs: Optional[clnpb.TlvStream] = None, 432 ) -> clnpb.KeysendResponse: 433 uri = "/cln.Node/KeySend" 434 res = clnpb.KeysendResponse 435 req = clnpb.KeysendRequest( 436 destination=normalize_node_id(destination, string=False), 437 amount_msat=amount, 438 label=label if label else "", 439 routehints=routehints, 440 extratlvs=extratlvs, 441 ).SerializeToString() 442 443 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 444 445 def stream_log(self): 446 """Stream logs as they get generated on the server side.""" 447 stream = self.inner.stream_log(b"") 448 while True: 449 n = stream.next() 450 if n is None: 451 break 452 yield nodepb.LogEntry.FromString(bytes(n)) 453 454 def stream_incoming(self): 455 stream = self.inner.stream_incoming(b"") 456 while True: 457 n = stream.next() 458 if n is None: 459 break 460 yield nodepb.IncomingPayment.FromString(bytes(n)) 461 462 def stream_custommsg(self): 463 stream = self.inner.stream_custommsg(b"") 464 while True: 465 n = stream.next() 466 if n is None: 467 break 468 yield nodepb.Custommsg.FromString(bytes(n)) 469 470 def send_custommsg(self, node_id: str, msg: bytes) -> clnpb.SendcustommsgResponse: 471 uri = "/cln.Node/SendCustomMsg" 472 res = clnpb.SendcustommsgResponse 473 req = clnpb.SendcustommsgRequest( 474 node_id=normalize_node_id(node_id), 475 msg=msg, 476 ).SerializeToString() 477 478 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 479 480 def datastore(self, key, string=None, hex=None, mode=None, generation=None): 481 uri = "/cln.Node/Datastore" 482 req = clnpb.DatastoreRequest( 483 key=key, string=string, hex=hex, mode=mode, generation=generation 484 ).SerializeToString() 485 res = clnpb.DatastoreResponse 486 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 487 488 def del_datastore(self, key, generation=None): 489 uri = "/cln.Node/DelDatastore" 490 req = clnpb.DeldatastoreRequest( 491 key=key, generation=generation 492 ).SerializeToString() 493 res = clnpb.DeldatastoreResponse 494 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 495 496 def list_datastore(self, key=None): 497 uri = "/cln.Node/ListDatastore" 498 req = clnpb.ListdatastoreRequest(key=key).SerializeToString() 499 res = clnpb.ListdatastoreResponse 500 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 501 502 def get_lsp_client( 503 self, 504 ) -> LspClient: 505 native_lsps = self.inner.get_lsp_client() 506 return LspClient(native_lsps) 507 508 def configure(self, close_to_addr: str) -> None: 509 req = nodepb.GlConfig(close_to_addr=close_to_addr).SerializeToString() 510 511 return self.inner.configure(req) 512 513 def wait_blockheight( 514 self, 515 blockheight: int, 516 timeout: Optional[int] = None, 517 ): 518 """Wait until the blockchain has reached the specified blockheight.""" 519 uri = "/cln.Node/WaitBlockheight" 520 req = clnpb.WaitblockheightRequest( 521 blockheight=blockheight, timeout=timeout 522 ).SerializeToString() 523 res = clnpb.WaitblockheightResponse 524 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 525 526 def fetch_invoice( 527 self, 528 offer: str, 529 amount_msat: Optional[Amount] = None, 530 quantity: Optional[int] = None, 531 recurrence_counter: Optional[int] = None, 532 recurrence_start: Optional[int] = None, 533 recurrence_label: Optional[str] = None, 534 timeout: Optional[int] = None, 535 payer_note: Optional[str] = None, 536 ) -> clnpb.FetchinvoiceResponse: 537 """Fetch an invoice based on an offer. 538 539 Contact the issuer of an offer to get an actual invoice that 540 can be paid. It highlights any changes between the offer and 541 the returned invoice. 542 543 """ 544 uri = "/cln.Node/FetchInvoice" 545 req = clnpb.FetchinvoiceRequest( 546 offer=offer, 547 amount_msat=amount_msat, 548 quantity=quantity, 549 recurrence_counter=recurrence_counter, 550 recurrence_start=recurrence_start, 551 recurrence_label=recurrence_label, 552 timeout=timeout, 553 payer_note=payer_note, 554 ).SerializeToString() 555 res = clnpb.FetchinvoiceResponse 556 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 557 558 def wait(self, subsystem, indexname, nextvalue: int) -> clnpb.WaitResponse: 559 """Wait for the next event in the provided subsystem. 560 561 Returns once the index given by indexname in subsystem reaches 562 or exceeds nextvalue. 563 564 """ 565 uri = "/cln.Node/Wait" 566 req = clnpb.WaitRequest( 567 subsystem=subsystem, 568 indexname=indexname, 569 nextvalue=nextvalue, 570 ).SerializeToString() 571 res = clnpb.WaitResponse 572 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 573 574 575def normalize_node_id(node_id, string=False): 576 if len(node_id) == 66: 577 node_id = unhexlify(node_id) 578 579 if len(node_id) != 33: 580 raise ValueError("node_id is not 33 (binary) or 66 (hex) bytes long") 581 582 if isinstance(node_id, str): 583 node_id = node_id.encode("ASCII") 584 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(self, bolt11: str, trampoline_node_id: bytes, amount_msat: Optional[int] = None, label: Optional[str] = None): 418 res = self.inner.trampoline_pay( 419 bolt11=bolt11, 420 trampoline_node_id=trampoline_node_id, 421 amount_msat=amount_msat, 422 label=label, 423 ) 424 return nodepb.TrampolinePayResponse.FromString(bytes(res)) 425 426 def keysend( 427 self, 428 destination: bytes, 429 amount: clnpb.Amount, 430 label: Optional[str] = None, 431 routehints: Optional[clnpb.RoutehintList] = None, 432 extratlvs: Optional[clnpb.TlvStream] = None, 433 ) -> clnpb.KeysendResponse: 434 uri = "/cln.Node/KeySend" 435 res = clnpb.KeysendResponse 436 req = clnpb.KeysendRequest( 437 destination=normalize_node_id(destination, string=False), 438 amount_msat=amount, 439 label=label if label else "", 440 routehints=routehints, 441 extratlvs=extratlvs, 442 ).SerializeToString() 443 444 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 445 446 def stream_log(self): 447 """Stream logs as they get generated on the server side.""" 448 stream = self.inner.stream_log(b"") 449 while True: 450 n = stream.next() 451 if n is None: 452 break 453 yield nodepb.LogEntry.FromString(bytes(n)) 454 455 def stream_incoming(self): 456 stream = self.inner.stream_incoming(b"") 457 while True: 458 n = stream.next() 459 if n is None: 460 break 461 yield nodepb.IncomingPayment.FromString(bytes(n)) 462 463 def stream_custommsg(self): 464 stream = self.inner.stream_custommsg(b"") 465 while True: 466 n = stream.next() 467 if n is None: 468 break 469 yield nodepb.Custommsg.FromString(bytes(n)) 470 471 def send_custommsg(self, node_id: str, msg: bytes) -> clnpb.SendcustommsgResponse: 472 uri = "/cln.Node/SendCustomMsg" 473 res = clnpb.SendcustommsgResponse 474 req = clnpb.SendcustommsgRequest( 475 node_id=normalize_node_id(node_id), 476 msg=msg, 477 ).SerializeToString() 478 479 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 480 481 def datastore(self, key, string=None, hex=None, mode=None, generation=None): 482 uri = "/cln.Node/Datastore" 483 req = clnpb.DatastoreRequest( 484 key=key, string=string, hex=hex, mode=mode, generation=generation 485 ).SerializeToString() 486 res = clnpb.DatastoreResponse 487 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 488 489 def del_datastore(self, key, generation=None): 490 uri = "/cln.Node/DelDatastore" 491 req = clnpb.DeldatastoreRequest( 492 key=key, generation=generation 493 ).SerializeToString() 494 res = clnpb.DeldatastoreResponse 495 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 496 497 def list_datastore(self, key=None): 498 uri = "/cln.Node/ListDatastore" 499 req = clnpb.ListdatastoreRequest(key=key).SerializeToString() 500 res = clnpb.ListdatastoreResponse 501 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 502 503 def get_lsp_client( 504 self, 505 ) -> LspClient: 506 native_lsps = self.inner.get_lsp_client() 507 return LspClient(native_lsps) 508 509 def configure(self, close_to_addr: str) -> None: 510 req = nodepb.GlConfig(close_to_addr=close_to_addr).SerializeToString() 511 512 return self.inner.configure(req) 513 514 def wait_blockheight( 515 self, 516 blockheight: int, 517 timeout: Optional[int] = None, 518 ): 519 """Wait until the blockchain has reached the specified blockheight.""" 520 uri = "/cln.Node/WaitBlockheight" 521 req = clnpb.WaitblockheightRequest( 522 blockheight=blockheight, timeout=timeout 523 ).SerializeToString() 524 res = clnpb.WaitblockheightResponse 525 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 526 527 def fetch_invoice( 528 self, 529 offer: str, 530 amount_msat: Optional[Amount] = None, 531 quantity: Optional[int] = None, 532 recurrence_counter: Optional[int] = None, 533 recurrence_start: Optional[int] = None, 534 recurrence_label: Optional[str] = None, 535 timeout: Optional[int] = None, 536 payer_note: Optional[str] = None, 537 ) -> clnpb.FetchinvoiceResponse: 538 """Fetch an invoice based on an offer. 539 540 Contact the issuer of an offer to get an actual invoice that 541 can be paid. It highlights any changes between the offer and 542 the returned invoice. 543 544 """ 545 uri = "/cln.Node/FetchInvoice" 546 req = clnpb.FetchinvoiceRequest( 547 offer=offer, 548 amount_msat=amount_msat, 549 quantity=quantity, 550 recurrence_counter=recurrence_counter, 551 recurrence_start=recurrence_start, 552 recurrence_label=recurrence_label, 553 timeout=timeout, 554 payer_note=payer_note, 555 ).SerializeToString() 556 res = clnpb.FetchinvoiceResponse 557 return res.FromString(bytes(self.inner.call(uri, bytes(req)))) 558 559 def wait(self, subsystem, indexname, nextvalue: int) -> clnpb.WaitResponse: 560 """Wait for the next event in the provided subsystem. 561 562 Returns once the index given by indexname in subsystem reaches 563 or exceeds nextvalue. 564 565 """ 566 uri = "/cln.Node/Wait" 567 req = clnpb.WaitRequest( 568 subsystem=subsystem, 569 indexname=indexname, 570 nextvalue=nextvalue, 571 ).SerializeToString() 572 res = clnpb.WaitResponse 573 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(self, bolt11: str, trampoline_node_id: bytes, amount_msat: Optional[int] = None, label: Optional[str] = None): 418 res = self.inner.trampoline_pay( 419 bolt11=bolt11, 420 trampoline_node_id=trampoline_node_id, 421 amount_msat=amount_msat, 422 label=label, 423 ) 424 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:
426 def keysend( 427 self, 428 destination: bytes, 429 amount: clnpb.Amount, 430 label: Optional[str] = None, 431 routehints: Optional[clnpb.RoutehintList] = None, 432 extratlvs: Optional[clnpb.TlvStream] = None, 433 ) -> clnpb.KeysendResponse: 434 uri = "/cln.Node/KeySend" 435 res = clnpb.KeysendResponse 436 req = clnpb.KeysendRequest( 437 destination=normalize_node_id(destination, string=False), 438 amount_msat=amount, 439 label=label if label else "", 440 routehints=routehints, 441 extratlvs=extratlvs, 442 ).SerializeToString() 443 444 return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def
stream_log(self):
446 def stream_log(self): 447 """Stream logs as they get generated on the server side.""" 448 stream = self.inner.stream_log(b"") 449 while True: 450 n = stream.next() 451 if n is None: 452 break 453 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:
471 def send_custommsg(self, node_id: str, msg: bytes) -> clnpb.SendcustommsgResponse: 472 uri = "/cln.Node/SendCustomMsg" 473 res = clnpb.SendcustommsgResponse 474 req = clnpb.SendcustommsgRequest( 475 node_id=normalize_node_id(node_id), 476 msg=msg, 477 ).SerializeToString() 478 479 return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def
datastore(self, key, string=None, hex=None, mode=None, generation=None):
481 def datastore(self, key, string=None, hex=None, mode=None, generation=None): 482 uri = "/cln.Node/Datastore" 483 req = clnpb.DatastoreRequest( 484 key=key, string=string, hex=hex, mode=mode, generation=generation 485 ).SerializeToString() 486 res = clnpb.DatastoreResponse 487 return res.FromString(bytes(self.inner.call(uri, bytes(req))))
def
wait_blockheight(self, blockheight: int, timeout: Optional[int] = None):
514 def wait_blockheight( 515 self, 516 blockheight: int, 517 timeout: Optional[int] = None, 518 ): 519 """Wait until the blockchain has reached the specified blockheight.""" 520 uri = "/cln.Node/WaitBlockheight" 521 req = clnpb.WaitblockheightRequest( 522 blockheight=blockheight, timeout=timeout 523 ).SerializeToString() 524 res = clnpb.WaitblockheightResponse 525 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:
527 def fetch_invoice( 528 self, 529 offer: str, 530 amount_msat: Optional[Amount] = None, 531 quantity: Optional[int] = None, 532 recurrence_counter: Optional[int] = None, 533 recurrence_start: Optional[int] = None, 534 recurrence_label: Optional[str] = None, 535 timeout: Optional[int] = None, 536 payer_note: Optional[str] = None, 537 ) -> clnpb.FetchinvoiceResponse: 538 """Fetch an invoice based on an offer. 539 540 Contact the issuer of an offer to get an actual invoice that 541 can be paid. It highlights any changes between the offer and 542 the returned invoice. 543 544 """ 545 uri = "/cln.Node/FetchInvoice" 546 req = clnpb.FetchinvoiceRequest( 547 offer=offer, 548 amount_msat=amount_msat, 549 quantity=quantity, 550 recurrence_counter=recurrence_counter, 551 recurrence_start=recurrence_start, 552 recurrence_label=recurrence_label, 553 timeout=timeout, 554 payer_note=payer_note, 555 ).SerializeToString() 556 res = clnpb.FetchinvoiceResponse 557 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:
559 def wait(self, subsystem, indexname, nextvalue: int) -> clnpb.WaitResponse: 560 """Wait for the next event in the provided subsystem. 561 562 Returns once the index given by indexname in subsystem reaches 563 or exceeds nextvalue. 564 565 """ 566 uri = "/cln.Node/Wait" 567 req = clnpb.WaitRequest( 568 subsystem=subsystem, 569 indexname=indexname, 570 nextvalue=nextvalue, 571 ).SerializeToString() 572 res = clnpb.WaitResponse 573 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):
576def normalize_node_id(node_id, string=False): 577 if len(node_id) == 66: 578 node_id = unhexlify(node_id) 579 580 if len(node_id) != 33: 581 raise ValueError("node_id is not 33 (binary) or 66 (hex) bytes long") 582 583 if isinstance(node_id, str): 584 node_id = node_id.encode("ASCII") 585 return node_id if not string else hexlify(node_id).encode("ASCII")