Skip to content

Commit 480c4ff

Browse files
Lightspark Engjklein24
authored andcommitted
Project import generated by Copybara.
GitOrigin-RevId: db199612a3e3f3a769e99c89e4fa15754a1518ca
1 parent 978d0de commit 480c4ff

83 files changed

Lines changed: 2025 additions & 312 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Pipfile.lock

Lines changed: 197 additions & 180 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,12 @@ Open the file `example.py` and make sure to update the variables at the top of t
1515
pipenv install
1616
pipenv run python -m examples.example
1717
```
18+
19+
There are also a few examples of webservers for demonstrating webhooks and LNURLs. These can similarly be run through Flask:
20+
21+
```python
22+
pipenv install -d
23+
pipenv run flask --app examples.flask_lnurl_server run
24+
```
25+
26+
Note that Flask requires Python >= 3.8, so these examples will not work if running Python 3.7.

copy.bara.sky

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,14 @@ core.workflow(
6868
),
6969
destination = git.github_destination(
7070
url = "https://github.com/lightsparkdev/go-sdk.git",
71-
push = "main",
71+
# TODO: Move this back to main when we're ready to release.
72+
push = "rc/remote-signing",
7273
),
7374
# Switch to ITERATIVE mode to import each commit separately.
7475
mode = "SQUASH",
7576

7677
origin_files = glob(
77-
["go-sdk/**", "copy.bara.sky"],
78+
["go-sdk/**"],
7879
),
7980

8081
authoring = authoring.pass_thru("Lightspark Eng <engineering@lightspark.com>"),

examples/flask_lnurl_server.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import json
2+
import os
3+
from dataclasses import dataclass
4+
from typing import Any
5+
from uuid import UUID
6+
7+
from flask import Flask, abort, request
8+
from flask.typing import ResponseReturnValue
9+
10+
from lightspark.lightspark_client import LightsparkSyncClient
11+
12+
app = Flask(__name__)
13+
14+
#################################################################
15+
## MODIFY THOSE VARIABLES BEFORE RUNNING THE EXAMPLE
16+
#################################################################
17+
##
18+
## We defined those variables as environment variables, but if you are just
19+
## running the example locally, feel free to just set the values in Python.
20+
##
21+
## First, initialize your client ID and client secret. Those are available
22+
## in your account at https://app.lightspark.com/api_config
23+
##
24+
## export LIGHTSPARK_API_TOKEN_CLIENT_ID=<client_id>
25+
## export LIGHTSPARK_API_TOKEN_CLIENT_SECRET=<client_secret>
26+
API_TOKEN_CLIENT_ID = os.environ.get("LIGHTSPARK_API_TOKEN_CLIENT_ID")
27+
API_TOKEN_CLIENT_SECRET = os.environ.get("LIGHTSPARK_API_TOKEN_CLIENT_SECRET")
28+
##
29+
## This example also assumes you already know your node UUID. Generally, an LNURL API would serve
30+
## many different usernames while maintaining some internal mapping from username to node UUID. For
31+
## simplicity, this example works with a single username and node UUID.
32+
##
33+
## export LIGHTSPARK_LNURL_NODE_UUID=0187c4d6-704b-f96b-0000-a2e8145bc1f9
34+
LNURL_NODE_UUID = os.environ.get("LIGHTSPARK_LNURL_NODE_UUID")
35+
LNURL_USERNAME = os.environ.get("LIGHTSPARK_LNURL_USERNAME", "ls_test")
36+
##
37+
## To run the webserver, run this command from the root of the SDK folder:
38+
## $ pipenv run flask --app examples.flask_lnurl_server run
39+
##
40+
## By default the server will run on port 5000. You can make a request to the API through
41+
## curl to make sure the server is working properly (replace ls_test with the username you have
42+
## configured):
43+
##
44+
## $ curl http://127.0.0.1:7120/.well-known/lnurlp/ls_test
45+
#################################################################
46+
47+
assert API_TOKEN_CLIENT_ID
48+
assert API_TOKEN_CLIENT_SECRET
49+
assert LNURL_NODE_UUID
50+
assert LNURL_USERNAME
51+
52+
53+
@dataclass
54+
class User:
55+
uuid: UUID
56+
username: str
57+
node_uuid: str
58+
59+
60+
LS_TEST_USER = User(
61+
# Static UUID so that callback URLs are always the same.
62+
UUID("4b41ae03-01b8-4974-8d26-26a35d28851b"),
63+
LNURL_USERNAME,
64+
f"LightsparkNode:{LNURL_NODE_UUID}",
65+
)
66+
67+
68+
def _generate_callback_for_user(user: User) -> str:
69+
return f"{request.url_root}api/lnurl/payreq/{user.uuid}"
70+
71+
72+
def _generate_metadata_for_user(user: User) -> str:
73+
return json.dumps(
74+
[
75+
["text/plain", f"Pay to domain.org user {user.username}"],
76+
["text/identifier", f"{user.username}@domain.org"],
77+
]
78+
)
79+
80+
81+
@app.route("/.well-known/lnurlp/<username>")
82+
def well_known_lnurlp_username(username: str) -> ResponseReturnValue:
83+
if username != LS_TEST_USER.username:
84+
abort(404, description=f"User not found: {username}")
85+
86+
user = LS_TEST_USER
87+
callback = _generate_callback_for_user(user)
88+
metadata = _generate_metadata_for_user(user)
89+
90+
return {
91+
"callback": callback,
92+
"maxSendable": 10_000_000,
93+
"minSendable": 1_000,
94+
"metadata": metadata,
95+
"tag": "payRequest",
96+
}
97+
98+
99+
@app.route("/api/lnurl/payreq/<uuid>")
100+
def lnurl_payreq(uuid: str) -> ResponseReturnValue:
101+
if uuid != str(LS_TEST_USER.uuid):
102+
abort(404, description=f"User not found: {uuid}")
103+
104+
user = LS_TEST_USER
105+
amount_msats = int(request.args["amount"])
106+
107+
ls_client = LightsparkSyncClient(
108+
api_token_client_id=API_TOKEN_CLIENT_ID,
109+
api_token_client_secret=API_TOKEN_CLIENT_SECRET,
110+
)
111+
112+
invoice = ls_client.create_lnurl_invoice(
113+
user.node_uuid,
114+
amount_msats,
115+
_generate_metadata_for_user(user),
116+
)
117+
118+
return {"pr": invoice.data.encoded_payment_request, "routes": []}
119+
120+
121+
@app.errorhandler(404)
122+
def error_not_found(e: Any) -> ResponseReturnValue:
123+
return {"status": "ERROR", "reason": e.description}, 404

examples/flask_webhook_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
Install Flask (pip install flask) and then run this like:
55
6-
flask --app flask_webhook_server run --port 5001
6+
pipenv run flask --app examples.flask_webhook_server run --port 5001
77
"""
88

99
import lightspark

lightspark/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
2424
from lightspark.objects.ChannelToTransactionsConnection import (
2525
ChannelToTransactionsConnection,
2626
)
27+
from lightspark.objects.Connection import Connection
2728
from lightspark.objects.CreateApiTokenInput import CreateApiTokenInput
2829
from lightspark.objects.CreateApiTokenOutput import CreateApiTokenOutput
2930
from lightspark.objects.CreateInvoiceInput import CreateInvoiceInput
3031
from lightspark.objects.CreateInvoiceOutput import CreateInvoiceOutput
32+
from lightspark.objects.CreateLnurlInvoiceInput import CreateLnurlInvoiceInput
3133
from lightspark.objects.CreateNodeWalletAddressInput import CreateNodeWalletAddressInput
3234
from lightspark.objects.CreateNodeWalletAddressOutput import (
3335
CreateNodeWalletAddressOutput,
@@ -36,6 +38,9 @@
3638
from lightspark.objects.CreateTestModeInvoiceOutput import CreateTestModeInvoiceOutput
3739
from lightspark.objects.CreateTestModePaymentInput import CreateTestModePaymentInput
3840
from lightspark.objects.CreateTestModePaymentoutput import CreateTestModePaymentoutput
41+
from lightspark.objects.CryptoSanctionsScreeningProvider import (
42+
CryptoSanctionsScreeningProvider,
43+
)
3944
from lightspark.objects.CurrencyAmount import CurrencyAmount
4045
from lightspark.objects.CurrencyUnit import CurrencyUnit
4146
from lightspark.objects.DeleteApiTokenInput import DeleteApiTokenInput
@@ -66,6 +71,7 @@
6671
from lightspark.objects.LightningFeeEstimateOutput import LightningFeeEstimateOutput
6772
from lightspark.objects.LightningTransaction import LightningTransaction
6873
from lightspark.objects.LightsparkNode import LightsparkNode
74+
from lightspark.objects.LightsparkNodeOwner import LightsparkNodeOwner
6975
from lightspark.objects.LightsparkNodePurpose import LightsparkNodePurpose
7076
from lightspark.objects.LightsparkNodeStatus import LightsparkNodeStatus
7177
from lightspark.objects.LightsparkNodeToChannelsConnection import (
@@ -96,10 +102,13 @@
96102
from lightspark.objects.RequestWithdrawalInput import RequestWithdrawalInput
97103
from lightspark.objects.RequestWithdrawalOutput import RequestWithdrawalOutput
98104
from lightspark.objects.RichText import RichText
105+
from lightspark.objects.RiskRating import RiskRating
99106
from lightspark.objects.RoutingTransaction import RoutingTransaction
100107
from lightspark.objects.RoutingTransactionFailureReason import (
101108
RoutingTransactionFailureReason,
102109
)
110+
from lightspark.objects.ScreenBitcoinAddressesInput import ScreenBitcoinAddressesInput
111+
from lightspark.objects.ScreenBitcoinAddressesOutput import ScreenBitcoinAddressesOutput
103112
from lightspark.objects.Secret import Secret
104113
from lightspark.objects.SendPaymentInput import SendPaymentInput
105114
from lightspark.objects.SendPaymentOutput import SendPaymentOutput
@@ -108,6 +117,13 @@
108117
from lightspark.objects.TransactionStatus import TransactionStatus
109118
from lightspark.objects.TransactionType import TransactionType
110119
from lightspark.objects.Wallet import Wallet
120+
from lightspark.objects.WalletStatus import WalletStatus
121+
from lightspark.objects.WalletToPaymentRequestsConnection import (
122+
WalletToPaymentRequestsConnection,
123+
)
124+
from lightspark.objects.WalletToTransactionsConnection import (
125+
WalletToTransactionsConnection,
126+
)
111127
from lightspark.objects.WebhookEventType import WebhookEventType
112128
from lightspark.objects.Withdrawal import Withdrawal
113129
from lightspark.objects.WithdrawalMode import WithdrawalMode

lightspark/lightspark_client.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright ©, 2022-present, Lightspark Group, Inc. - All Rights Reserved
22

3+
from hashlib import sha256
34
import logging
45
from dataclasses import dataclass
56
from datetime import datetime, timedelta, timezone
@@ -161,6 +162,59 @@ def create_invoice(
161162

162163
return Invoice_from_json(self._requester, json["create_invoice"]["invoice"])
163164

165+
def create_lnurl_invoice(
166+
self,
167+
node_id: str,
168+
amount_msats: int,
169+
metadata: str,
170+
) -> Invoice:
171+
"""Generates a Lightning Invoice (follows the Bolt 11 specification) to request a payment
172+
from another Lightning Node. This should only be used for generating invoices for LNURLs,
173+
with `create_invoice` preferred in the general case.
174+
175+
Args:
176+
node_id (str): The ID of the node from which to create the invoice.
177+
amount_sats (int): The amount for which the invoice should be created, in millisatoshis.
178+
will be added.
179+
metadata (str): The LNURL metadata payload field in the initial payreq response. This
180+
will be hashed and present in the h-tag (SHA256 purpose of payment) of the resulting
181+
Bolt 11 invoice.
182+
183+
Returns:
184+
Invoice: An `Invoice` object representing the generated invoice.
185+
"""
186+
logger.info("Creating an invoice for node %s.", node_id)
187+
json = self._requester.execute_graphql(
188+
f"""
189+
mutation CreateLnurlInvoice(
190+
$node_id: ID!
191+
$amount_msats: Long!
192+
$metadata_hash: String!
193+
) {{
194+
create_lnurl_invoice(input: {{
195+
node_id: $node_id
196+
amount_msats: $amount_msats
197+
metadata_hash: $metadata_hash
198+
}}) {{
199+
invoice {{
200+
...InvoiceFragment
201+
}}
202+
}}
203+
}}
204+
205+
{InvoiceFragment}
206+
""",
207+
{
208+
"amount_msats": amount_msats,
209+
"node_id": node_id,
210+
"metadata_hash": sha256(metadata.encode("utf-8")).hexdigest(),
211+
},
212+
)
213+
214+
return Invoice_from_json(
215+
self._requester, json["create_lnurl_invoice"]["invoice"]
216+
)
217+
164218
def create_node_wallet_address(
165219
self,
166220
node_id: str,

0 commit comments

Comments
 (0)