async def resolve_invoice_subscription(self, info, add_index=None, settle_index=None): try: if not info.context["user"].is_authenticated: yield Unauthenticated() except AttributeError as exc: print(exc) yield ServerError( "A server internal error (AttributeError) has occurred. :-(") res = LNDWallet.objects.filter(owner=info.context["user"]) if not res: yield WalletInstanceNotFound() cfg = build_lnd_wallet_config(res.first().pk) channel_data = build_grpc_channel_manual( rpc_server="127.0.0.1", rpc_port=cfg.rpc_listen_port_ipv4, cert_path=cfg.tls_cert_path, macaroon_path=cfg.admin_macaroon_path, is_async=True) if channel_data.error is not None: yield ServerError(error_message=channel_data.error) try: stub = lnrpc.LightningStub(channel_data.channel) request = ln.InvoiceSubscription( add_index=add_index, settle_index=settle_index, ) except Exception as exc: print(exc) yield ServerError(error_message=exc) try: async for response in stub.SubscribeInvoices( request, metadata=[('macaroon', channel_data.macaroon)]): if not info.context["user"].is_authenticated: yield Unauthenticated() else: json_data = json.loads(MessageToJson(response)) invoice = InvoiceSubSuccess(LnInvoice(json_data)) yield invoice except Exception as exc: print(exc) yield ServerError(error_message=exc)
def mutate(self, info, wallet_password: str, cipher_seed_mnemonic: [] = None, aezeed_passphrase: str = None, recovery_window: int = None): """https://api.lightning.community/?python#initwallet""" if not info.context.user.is_authenticated: return Unauthenticated() if len(wallet_password) < 8: return InitWalletPasswordToShortError() res: QuerySet = LNDWallet.objects.filter(owner=info.context.user) if not res: return InitWalletInstanceNotFound() wallet: LNDWallet = res.first() if wallet.initialized: return InitWalletIsInitialized() res = init_wallet_mutation(wallet, wallet_password, cipher_seed_mnemonic, aezeed_passphrase, recovery_window) if isinstance(res, InitWalletSuccess): wallet.initialized = True wallet.save() return res
def resolve_ln_list_payments(self, info, index_offset, num_max_payments, reverse): """https://api.lightning.community/?python#listpayments LND does not have paging in this call yet. Every gRPC call will always return the complete payments dataset. The paging functionality is implemented on top of the full dataset we get from the LND daemon. """ if not info.context.user.is_authenticated: return Unauthenticated() res = LNDWallet.objects.filter(owner=info.context.user) if not res: return WalletInstanceNotFound() cfg = build_lnd_wallet_config(res.first().pk) channel_data = build_grpc_channel_manual( rpc_server="127.0.0.1", rpc_port=cfg.rpc_listen_port_ipv4, cert_path=cfg.tls_cert_path, macaroon_path=cfg.admin_macaroon_path) if channel_data.error is not None: return channel_data.error stub = lnrpc.LightningStub(channel_data.channel) request = ln.ListPaymentsRequest() try: response = stub.ListPayments(request, metadata=[('macaroon', channel_data.macaroon)]) except grpc.RpcError as exc: # pylint: disable=E1101 print(exc) return ServerError.generic_rpc_error(exc.code(), exc.details()) json_data = json.loads( MessageToJson( response, preserving_proto_field_name=True, including_default_value_fields=True, )) if reverse: # reverse the list rev = json_data["payments"][::-1] payments = rev[index_offset:index_offset + num_max_payments] else: payments = json_data["payments"][index_offset:index_offset + num_max_payments] return ListPaymentsSuccess(index_offset, index_offset + num_max_payments, payments)
def resolve_ln_get_info(self, info, **kwargs): if not info.context.user.is_authenticated: return Unauthenticated() res = LNDWallet.objects.filter(owner=info.context.user) if not res: return WalletInstanceNotFound() return get_info_query(res.first())
def resolve_ln_list_peers(self, info, **kwargs): """https://api.lightning.community/#listpeers""" if not info.context.user.is_authenticated: return Unauthenticated() res = LNDWallet.objects.filter(owner=info.context.user) if not res: return WalletInstanceNotFound() return list_peers_query(info, res.first())
def mutate(self, info, pubkey: str, host: str, perm: bool): """https://api.lightning.community/?python#connectpeer""" if not info.context.user.is_authenticated: return Unauthenticated() res: QuerySet = LNDWallet.objects.filter(owner=info.context.user) if not res: return WalletInstanceNotFound() wallet: LNDWallet = res.first() cfg: LNDWalletConfig = build_lnd_wallet_config(wallet.pk) channel_data = build_grpc_channel_manual( rpc_server="127.0.0.1", rpc_port=cfg.rpc_listen_port_ipv4, cert_path=cfg.tls_cert_path, macaroon_path=cfg.admin_macaroon_path, ) if channel_data.error is not None: return channel_data.error stub = lnrpc.LightningStub(channel_data.channel) request = ln.ConnectPeerRequest(addr={ "pubkey": pubkey, "host": host }, perm=perm) try: response = stub.ConnectPeer(request, metadata=[('macaroon', channel_data.macaroon)]) except grpc.RpcError as exc: # pylint: disable=E1101 print(exc) if "dial tcp {}: connect: connection refused".format( host) in exc.details(): return ConnectPeerError( error_message="Peer refused to connect", pubkey=pubkey, host=host) if "already connected to peer: {}".format(host) in exc.details(): return ConnectPeerError( error_message="Already connected to peer", pubkey=pubkey, host=host) return ServerError.generic_rpc_error(exc.code(), exc.details()) return ConnectPeerSuccess(pubkey=pubkey)
def mutate(self, info, payment_raw: LnRawPaymentInput = None, payment_request: str = "", final_cltv_delta: int = 0, fee_limit: LnFeeLimit = None): if not info.context.user.is_authenticated: return SendPaymentMutation(payment_result=Unauthenticated()) res = LNDWallet.objects.filter(owner=info.context.user) if not res: return SendPaymentMutation(payment_result=WalletInstanceNotFound()) cfg = build_lnd_wallet_config(res.first().pk) channel_data = build_grpc_channel_manual( rpc_server="127.0.0.1", rpc_port=cfg.rpc_listen_port_ipv4, cert_path=cfg.tls_cert_path, macaroon_path=cfg.admin_macaroon_path, ) if channel_data.error is not None: return SendPaymentMutation(payment_result=channel_data.error) stub = lnrpc.LightningStub(channel_data.channel) request = ln.SendRequest(payment_request=payment_request) try: response = stub.SendPaymentSync(request, metadata=[('macaroon', channel_data.macaroon)]) except RpcError as exc: # pylint: disable=E1101 print(exc) if exc.details().startswith("invoice expired"): return SendPaymentMutation( payment_result=SendPaymentError(exc.details())) return SendPaymentMutation( payment_result=ServerError.generic_rpc_error( exc.code(), exc.details())) json_data = json.loads(MessageToJson(response)) if response.payment_error: err = SendPaymentError(payment_error=response.payment_error) return SendPaymentMutation(payment_result=err) res = SendPaymentSuccess(payment_preimage=response.payment_preimage, payment_route=LnRoute( json_data["payment_route"])) return SendPaymentMutation(payment_result=res)
def mutate( self, info, ): """https://api.lightning.community/?python#stopdaemon""" if not info.context.user.is_authenticated: return Unauthenticated() res: QuerySet = LNDWallet.objects.filter(owner=info.context.user) if not res: return WalletInstanceNotFound() wallet: LNDWallet = res.first() cfg: LNDWalletConfig = build_lnd_wallet_config(wallet.pk) channel_data = build_grpc_channel_manual( rpc_server="127.0.0.1", rpc_port=cfg.rpc_listen_port_ipv4, cert_path=cfg.tls_cert_path, macaroon_path=cfg.admin_macaroon_path, ) if channel_data.error is not None: return channel_data.error # stop daemon stub = lnrpc.LightningStub(channel_data.channel) request = ln.StopRequest() try: response = stub.StopDaemon(request, metadata=[('macaroon', channel_data.macaroon)]) except grpc.RpcError as exc: # pylint: disable=E1101 print(exc) return ServerError.generic_rpc_error(exc.code(), exc.details()) start = time.time() while True: if not lnd_instance_is_running(cfg): return StopDaemonSuccess() if time.time() - start >= 10: return StopDaemonError( error_message= "Unable to shutdown the process within 10 seconds") time.sleep(1)
def resolve_get_ln_wallet_status(self, info, **kwargs): """retrieve the current state of the wallet""" if not info.context.user.is_authenticated: return Unauthenticated() res = LNDWallet.objects.filter(owner=info.context.user) # LND instance is not yet created. # User should call createWallet if not res: return WalletInstanceNotFound() # Wallet database object was created but # it is not yet initialized if not res.first().initialized: return GetLnWalletStatusNotInitialized() cfg = build_lnd_wallet_config(res.first().pk) channel_data = build_grpc_channel_manual( rpc_server="127.0.0.1", rpc_port=cfg.rpc_listen_port_ipv4, cert_path=cfg.tls_cert_path, macaroon_path=cfg.admin_macaroon_path) if channel_data.error is not None: return channel_data.error stub = lnrpc.LightningStub(channel_data.channel) request = ln.GetInfoRequest() try: stub.GetInfo(request, metadata=[('macaroon', channel_data.macaroon)]) except grpc.RpcError as exc: # pylint: disable=E1101 # This is a wanted exception. # When GetInfo returns UNIMPLEMENTED the wallet is likely locked if exc.code() == grpc.StatusCode.UNIMPLEMENTED: return GetLnWalletStatusLocked() # Unexpected exception LOGGER.exception(exc) return ServerError.generic_rpc_error(exc.code(), exc.details()) return GetLnWalletStatusOperational()
def resolve_ln_list_invoices(self, info, pending_only, index_offset, num_max_invoices, reverse): """https://api.lightning.community/?python#listinvoices""" if not info.context.user.is_authenticated: return Unauthenticated() res = LNDWallet.objects.filter(owner=info.context.user) if not res: return WalletInstanceNotFound() cfg = build_lnd_wallet_config(res.first().pk) channel_data = build_grpc_channel_manual( rpc_server="127.0.0.1", rpc_port=cfg.rpc_listen_port_ipv4, cert_path=cfg.tls_cert_path, macaroon_path=cfg.admin_macaroon_path) if channel_data.error is not None: return channel_data.error stub = lnrpc.LightningStub(channel_data.channel) request = ln.ListInvoiceRequest( pending_only=pending_only, index_offset=index_offset, num_max_invoices=num_max_invoices, reversed=reverse, ) try: response = stub.ListInvoices(request, metadata=[('macaroon', channel_data.macaroon)]) except grpc.RpcError as exc: # pylint: disable=E1101 print(exc) return ServerError.generic_rpc_error(exc.code(), exc.details()) json_data = json.loads( MessageToJson( response, preserving_proto_field_name=True, including_default_value_fields=True, )) return ListInvoicesSuccess(json_data)
def resolve_ln_gen_seed(self, info, aezeed_passphrase=None, seed_entropy=None): """https://api.lightning.community/?python#genseed""" if not info.context.user.is_authenticated: return Unauthenticated() res = LNDWallet.objects.filter(owner=info.context.user) if not res: return GenSeedWalletInstanceNotFound() # we currently only allow one wallet per user anyway, # so just get the first one return gen_seed_query(res.first(), aezeed_passphrase=aezeed_passphrase, seed_entropy=seed_entropy)
def mutate(self, info, autopilot: bool, wallet_password: str, recovery_window: int): """Starts the LND process and unlocks the wallet if user provides the password""" if not info.context.user.is_authenticated: return Unauthenticated() res: QuerySet = LNDWallet.objects.filter(owner=info.context.user) if not res: return StartDaemonInstanceNotFound() wallet: LNDWallet = res.first() return start_daemon_mutation( wallet, autopilot, wallet_password, )
def mutate(self, info, pubkey: str): """https://api.lightning.community/?python#disconnectpeer""" if not info.context.user.is_authenticated: return Unauthenticated() res: QuerySet = LNDWallet.objects.filter(owner=info.context.user) if not res: return WalletInstanceNotFound() wallet: LNDWallet = res.first() cfg: LNDWalletConfig = build_lnd_wallet_config(wallet.pk) channel_data = build_grpc_channel_manual( rpc_server="127.0.0.1", rpc_port=cfg.rpc_listen_port_ipv4, cert_path=cfg.tls_cert_path, macaroon_path=cfg.admin_macaroon_path, ) if channel_data.error is not None: return channel_data.error stub = lnrpc.LightningStub(channel_data.channel) request = ln.DisconnectPeerRequest(pub_key=pubkey) try: stub.DisconnectPeer(request, metadata=[('macaroon', channel_data.macaroon)]) except grpc.RpcError as exc: # pylint: disable=E1101 print(exc) if "unable to disconnect peer: peer {} is not connected".format( pubkey) in exc.details(): return DisconnectPeerError(error_message="Peer not connected", pubkey=pubkey) return ServerError.generic_rpc_error(exc.code(), exc.details()) return DisconnectPeerSuccess(pubkey=pubkey)
def mutate(self, info, value, memo: str = ""): if not info.context.user.is_authenticated: return AddInvoiceMutation(result=Unauthenticated()) res = LNDWallet.objects.filter(owner=info.context.user) if not res: return AddInvoiceMutation(result=WalletInstanceNotFound()) cfg = build_lnd_wallet_config(res.first().pk) channel_data = build_grpc_channel_manual( rpc_server="127.0.0.1", rpc_port=cfg.rpc_listen_port_ipv4, cert_path=cfg.tls_cert_path, macaroon_path=cfg.admin_macaroon_path, ) if channel_data.error is not None: return AddInvoiceMutation(result=channel_data.error) stub = lnrpc.LightningStub(channel_data.channel) request = ln.Invoice( value=value, memo=memo, add_index=1, ) try: response = stub.AddInvoice(request, metadata=[('macaroon', channel_data.macaroon)]) except RpcError as exc: # pylint: disable=E1101 print(exc) return AddInvoiceMutation(result=ServerError.generic_rpc_error( exc.code(), exc.details())) json_data = json.loads(MessageToJson(response)) return AddInvoiceMutation( result=AddInvoiceSuccess(LnAddInvoiceResponse(json_data)))
async def resolve_open_channel_subscription(self, info, node_pubkey, local_funding_amount, push_sat, private, min_htlc_msat, min_confs, spend_unconfirmed, sat_per_byte=None, remote_csv_delay=None, target_conf=None): try: if not info.context["user"].is_authenticated: yield Unauthenticated() except AttributeError as exc: print(exc) yield ServerError( "A server internal error (AttributeError) has occurred. :-(") res = LNDWallet.objects.filter(owner=info.context["user"]) if not res: yield WalletInstanceNotFound() cfg = build_lnd_wallet_config(res.first().pk) channel_data = build_grpc_channel_manual( rpc_server="127.0.0.1", rpc_port=cfg.rpc_listen_port_ipv4, cert_path=cfg.tls_cert_path, macaroon_path=cfg.admin_macaroon_path, is_async=True) if channel_data.error is not None: yield channel_data.error try: stub = lnrpc.LightningStub(channel_data.channel) request = ln.OpenChannelRequest( node_pubkey=codecs.decode(node_pubkey, 'hex'), local_funding_amount=local_funding_amount, push_sat=push_sat, target_conf=target_conf, sat_per_byte=sat_per_byte, private=private, min_htlc_msat=min_htlc_msat, remote_csv_delay=remote_csv_delay, min_confs=min_confs, # spend_unconfirmed=spend_unconfirmed ) except Exception as exc: print(exc) yield ServerError(error_message=str(exc)) return try: async for response in stub.OpenChannel(request, metadata=[ ('macaroon', channel_data.macaroon) ]): if not info.context["user"].is_authenticated: yield Unauthenticated() else: json_data = json.loads(MessageToJson(response)) if "chan_pending" in json_data: cp = json_data["chan_pending"] txid = cp["txid"] if "txid" in cp else "" output_index = cp[ "output_index"] if "output_index" in cp else 0 yield ChannelPendingUpdate( channel_point=ChannelPoint(txid, output_index)) elif "confirmation" in json_data: conf = json_data["confirmation"] block_sha = conf[ "block_sha"] if "block_sha" in conf else "" block_height = conf[ "block_height"] if "block_height" in conf else 0 num_confs_left = conf[ "num_confs_left"] if "num_confs_left" in conf else 0 yield ChannelConfirmationUpdate( block_sha=block_sha, block_height=block_height, num_confs_left=num_confs_left) elif "chan_open" in json_data: co = json_data["chan_open"]["channel_point"] txid = co[ "funding_txid_bytes"] if "funding_txid_bytes" in co else "" output_index = cp[ "output_index"] if "output_index" in co else 0 yield ChannelOpenUpdate( channel_point=ChannelPoint(txid, output_index)) except RpcError as grpc_error: # pylint: disable=E1101 print(grpc_error) print(grpc_error.details()) yield OpenChannelError(grpc_error.details()) except Exception as exc: print(exc) yield ServerError(error_message=exc)
def mutate(self, info, autopilot: bool, name: str, public_alias: str): if not info.context.user.is_authenticated: return Unauthenticated() return create_wallet_mutation(autopilot, name, public_alias, info.context.user)
def resolve_get_current_user(self, info, **kwargs): if not info.context.user.is_authenticated: return Unauthenticated() return GetCurrentUserSuccess(user=info.context.user)
async def resolve_close_channel_subscription(self, info, funding_txid, output_index, force, target_conf=None, sat_per_byte=None): try: if not info.context["user"].is_authenticated: yield Unauthenticated() except AttributeError as exc: print(exc) yield ServerError( "A server internal error (AttributeError) has occurred. :-(") res = LNDWallet.objects.filter(owner=info.context["user"]) if not res: yield WalletInstanceNotFound() cfg = build_lnd_wallet_config(res.first().pk) channel_data = build_grpc_channel_manual( rpc_server="127.0.0.1", rpc_port=cfg.rpc_listen_port_ipv4, cert_path=cfg.tls_cert_path, macaroon_path=cfg.admin_macaroon_path, is_async=True) if channel_data.error is not None: yield channel_data.error try: stub = lnrpc.LightningStub(channel_data.channel) request = ln.CloseChannelRequest( channel_point={ "funding_txid_str": funding_txid, "output_index": output_index }, force=force, target_conf=target_conf, sat_per_byte=sat_per_byte, ) except Exception as exc: print(exc) yield ServerError(error_message=str(exc)) try: async for response in stub.CloseChannel( request, metadata=[('macaroon', channel_data.macaroon)]): if not info.context["user"].is_authenticated: yield Unauthenticated() else: json_data = json.loads(MessageToJson(response)) if "close_pending" in json_data: cp = json_data["close_pending"] txid = cp["txid"] if "txid" in cp else "" output_index = cp[ "output_index"] if "output_index" in cp else 0 yield ChannelClosePendingUpdate(txid=txid) elif "chan_close" in json_data: cc = json_data["chan_close"] txid = cc[ "closing_txid"] if "closing_txid" in cc else "" success = cc["success"] if "success" in cc else False yield ChannelCloseUpdate(closing_txid=txid, success=success) else: msg = "Unknown update from LND: {}".format(json_data) print(msg) yield ServerError(error_message=msg) except RpcError as grpc_error: # pylint: disable=E1101 print(grpc_error) print(grpc_error.details()) yield CloseChannelError(grpc_error.details()) except Exception as exc: print(exc) yield ServerError(error_message=exc)