Esempio n. 1
0
class InvoiceSubscription(graphene.ObjectType):
    invoice_subscription = graphene.Field(
        InvoiceSubPayload,
        description=process_lnd_doc_string(
            lnrpc.LightningServicer.SubscribeInvoices.__doc__),
        add_index=graphene.Int(),
        settle_index=graphene.Int())

    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)
Esempio n. 2
0
class ListChannelsQuery(graphene.ObjectType):
    ln_list_channels = graphene.Field(
        ListChannelsPayload,
        description=process_lnd_doc_string(
            lnrpc.LightningServicer.ListChannels.__doc__))

    def resolve_ln_list_channels(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 list_channels_query(res.first())
Esempio n. 3
0
class ListPeersQuery(graphene.ObjectType):
    ln_list_peers = graphene.Field(
        ListPeersPayload,
        description=process_lnd_doc_string(
            lnrpc.LightningServicer.ListPeers.__doc__))

    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())
Esempio n. 4
0
class NewAddressQuery(graphene.ObjectType):
    ln_new_address = graphene.Field(
        NewAddressPayload,
        description=process_lnd_doc_string(
            lnrpc.LightningServicer.NewAddress.__doc__),
        address_type=graphene.String(default_value="np2wkh",
                                     description="""
- p2wkh:  Pay to witness key hash
- np2wkh: Pay to nested witness key hash (default)
"""))

    def resolve_ln_new_address(self, info, address_type):
        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(), address_type)
 def description():
     """Returns the description for this mutation. 
     The String is fetched directly from the lnd grpc package
     """
     return process_lnd_doc_string(
         lnrpc.LightningServicer.DisconnectPeer.__doc__)
Esempio n. 6
0
class OpenChannelSubscription(graphene.ObjectType):

    open_channel_subscription = graphene.Field(
        OpenChannelSubPayload,
        description=process_lnd_doc_string(
            lnrpc.LightningServicer.OpenChannel.__doc__),
        node_pubkey=graphene.String(
            description=
            "The hex encoded pubkey of the node to open a channel with"),
        local_funding_amount=graphene.Int(
            description=
            "The number of satoshis the wallet should commit to the channel"),
        push_sat=graphene.Int(
            description=
            "The number of satoshis to push to the remote side as part of the initial commitment state"
        ),
        target_conf=graphene.Int(
            description=
            "The target number of blocks that the funding transaction should be confirmed by."
        ),
        sat_per_byte=graphene.Int(
            description=
            "A manual fee rate set in sat/byte that should be used when crafting the funding transaction."
        ),
        private=graphene.Boolean(
            description=
            "Whether this channel should be private, not announced to the greater network."
        ),
        min_htlc_msat=graphene.Int(
            description=
            "The minimum value in millisatoshi we will require for incoming HTLCs on the channel."
        ),
        remote_csv_delay=graphene.Int(
            description=
            "The delay we require on the remote’s commitment transaction. If this is not set, it will be scaled automatically with the channel size."
        ),
        min_confs=graphene.Int(
            description=
            "The minimum number of confirmations each one of your outputs used for the funding transaction must satisfy."
        ),
        spend_unconfirmed=graphene.Boolean(
            description=
            "Whether unconfirmed outputs should be used as inputs for the funding transaction."
        ))

    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)
Esempio n. 7
0
class ListPaymentsQuery(graphene.ObjectType):
    ln_list_payments = graphene.Field(
        ListPaymentsResponse,
        description=process_lnd_doc_string(
            lnrpc.LightningServicer.ListPayments.__doc__),
        index_offset=graphene.Int(
            default_value=0,
            description=
            "The index of an invoice that will be used as either the start or end of a query to determine which invoices should be returned in the response."
        ),
        num_max_payments=graphene.Int(
            default_value=100,
            description=
            "The max number of payments to return in the response to this query."
        ),
        reverse=graphene.Boolean(
            default_value=True,
            description=
            "If set, the payments returned will result from seeking backwards from the specified index offset. This can be used to paginate backwards."
        ),
    )

    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)
Esempio n. 8
0
class ListInvoicesQuery(graphene.ObjectType):
    ln_list_invoices = graphene.Field(
        ListInvoicesResponse,
        description=process_lnd_doc_string(
            lnrpc.LightningServicer.ListInvoices.__doc__),
        pending_only=graphene.Boolean(
            default_value=False,
            description=
            "If set, only unsettled invoices will be returned in the response."
        ),
        index_offset=graphene.Int(
            default_value=0,
            description=
            "The index of an invoice that will be used as either the start or end of a query to determine which invoices should be returned in the response."
        ),
        num_max_invoices=graphene.Int(
            default_value=100,
            description=
            "The max number of invoices to return in the response to this query."
        ),
        reverse=graphene.Boolean(
            default_value=True,
            description=
            "If set, the invoices returned will result from seeking backwards from the specified index offset. This can be used to paginate backwards."
        ),
    )

    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)
Esempio n. 9
0
class CloseChannelSubscription(graphene.ObjectType):

    close_channel_subscription = graphene.Field(
        CloseChannelSubPayload,
        description=process_lnd_doc_string(
            lnrpc.LightningServicer.CloseChannel.__doc__),
        funding_txid=graphene.String(
            required=True,
            description=
            "Hex-encoded string representing the funding transaction"),
        output_index=graphene.Int(
            required=True,
            description="The index of the output of the funding transaction"),
        force=graphene.Boolean(
            description=
            "If true, then the channel will be closed forcibly. This means the current commitment transaction will be signed and broadcast."
        ),
        target_conf=graphene.Int(
            required=False,
            description=
            "The target number of blocks that the closure transaction should be confirmed by."
        ),
        sat_per_byte=graphene.Int(
            required=False,
            description=
            "A manual fee rate set in sat/byte that should be used when crafting the closure transaction."
        ),
    )

    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)
Esempio n. 10
0
 def description():
     """Returns the description for this mutation. 
     The String is fetched directly from the lnd grpc package
     """
     return process_lnd_doc_string(
         lnrpc.WalletUnlockerServicer.InitWallet.__doc__)