lnurl / luds Goto Github PK
View Code? Open in Web Editor NEWlnurl specifications
lnurl specifications
LUD-10 doesn't clearly state what the expected structure of the decrypted content should look like. But it does say:
For
message
, a toaster or popup is sufficient. Forurl
, the wallet should give the user a popup which displaysdescription
,url
, and a 'open' button to open theurl
in a new browser tab.
From this, I infer that the decrypted content should be a proper successAction
JSON object. For example, the cleartext could be:
{
"tag": "message",
"message": "Speakeasy passphrase: The duck quacks at daybreak."
}
However, in the example code, the cleartext is just the string: "Secret data".
Can we specify the expected structure of the cleartext ? And can we provide examples for message
and url
?
In the table of REAMD.md, why isn't there 'wallet of satoshi' at 12th category, "Comments in payRequest"??
In order to avoid ambiguities we should check all documents and fix these things:
Somewhat related to #78 which contained multiple such errors.
Title pretty much says it all. I can't find any mention in the lnurl withdrawal docs that both errors and successes should return an http 200 code. Can this be added?
I went to implement LNURL-16 figuring it would be quite simple (ie request-invoice - get-invoice) but instead it requires a few hoops without any motivation for why. Specifically, the multiple requests to get an invoice may be fine, but multiple steps with server-side state required is a bunch of complication (not to mention DoS risk). Worse, using description_hash instead of description makes LNURL-payRequest/LNURL-16 harder to integrate with some standard tooling, and there doesn't seem to be any motivation listed for why the description_hash needs to match the first request (there doesn't appear to be any security motivation for it?) and without the metadata being passed back to the server.
Can we define an LNURL-21 that is just GET /.well-known/lnurlq/ that just returns an invoice. No JSON, no complexity, no server-side state, just an invoice? Or maybe remove the description_hash-matching requirement from the LNURL-payRequest requirements?
We don't know the future how this protocol will evolve. It might occur that a change needs to be made that older clients would not understand but the server would be able to send conditional responses based on what the client supports. Without knowing if it's the case it can either not support the old clients or new features.
This problem occurred recently with allowing zero amounts in withdraw protocol.
Introduce client-side feature flags. A client sends them proactively in the request to inform the server of the supported features. A server can determine how to respond based on presence or absence of flags.
Each flag is reasonably short, URL-safe, human-readable string. Protocols use special p-
prefix and features that are not protocols MUST NOT use this prefix.
Feature flags are not intended to be added often. An effort must be made to find solutions to problems without introducing new flag when implementing a feature. Wallet that claims to implement a feature by sending a flag MUST implement it in it's entirety, exactly according to the spec. The flag MUST not be present if the wallet is unable to support the feature. Failing to do so may lead to various safety hazards and lost sats.
At the time of writing, these are known features (protocols):
p-channel
p-withdraw
p-pay
p-auth
Expected features to be added after accepting this proposal:
balance-check
- this mandates that the wallet implements balance check and correctly handles zero amount as "no money available for withdraw yet"If no flags are present the server MUST assume the wallet implements LNURL older than this spec. The client features can not be known and thus the server needs to fallback to whatever is reasonable in the context.
The wallet MAY omit p-withdraw
if the inbound liquidity is zero, hinting the server that withdraw is impossible. However wallets SHOULD NOT change the features sent based on any other state o the wallet. Changing the fields can leak private information otherwise.
The question remains how to actually send the features. These possibilities seem reasonable:
&feat=p-channel,p-withdraw
- probably simplest to implementX-LNURL-features: p-channel,p-withdraw
- most REST-idiomatic? Headers APIs may be bulkier and less-known{"features": ["p-channel", "p-withdraw"]}
- maybe good since client supports Json bodies alreadyI sorted them in order of my preference but I don't feel strongly about it, discussion is needed.
This decreases anonset of users, as the different LNURL implementations may be fingerprinted.
This should not be an issue in practice as long as the wallet used is reasonably popular. Further, differences in implementations probably exist today - different (order of) headers, fields in Json.
There are concerns that wallets could just omit implementing some features leading to bad UX.
Firstly this is already possible:
Secondly, not having feature flags forces the server side to either drop support for old wallets if the new feature is more involved or not implementing new feature at all, leading to similar situation that "forcing" intended to avoid.
Thirdly, requiring a lot of features from compliant wallets makes implementation costly, leading to disincentive to implement at least a subset of LNURL. A well-defined subset is better than nothing or ill-defined subset. On the other hand, if a wallet developer has option to just write simple impl to see how well it works, he'll be more willing to do it and maybe add support later.
Finally, the surest way to add support of LNURL (or its new feature) to another wallet is by sending a PR. If the PR is ignored or rejected, the project may be dead or unreasonable and should be avoided.
It seems like LUD-16 isn't complete without also being able to specify static invoice/invoiceless information in the LUD-6 object. It could be added easily, like so:
{
"callback": "https://api.example.com/v1/lnurlp/dave/pay",
"minSendable": 1000,
"maxSendable": 1000000000000,
"tag": "payRequest",
"metadata": "[[\"text/plain\",\"Pay Dave Jones (@dave).\"]]",
"staticPayment": {
"method": "keysend",
"address": "032f4ffbbafffbe51726ad3c164a3d0d37ec27bc67b29a159b0f49ae8ac21b8508",
"customKey": "112111100",
"customValue": "wal_ZmqFg135h71oek"
}
}
This would allow for generating invoices, as normal, but also gives the staticPayment
info in case the receiver also allows for static invoicing (keysend/amp/bolt12). The method
would dictate the type of static info the object is describing. This is modeled off of the Value4Value spec in the podcast namespace:
https://github.com/Podcastindex-org/podcast-namespace/blob/main/value/value.md#lightning
...which has worked extremely well. Being able to use Lightning Addresses in the v4v payment blocks would need something like I'm describing here.
We've implemented the LNURL fallback scheme with URLs as outlined in LUD-01 but it seems like a number of wallets still haven't implemented this and there's no signal for which ones do.
Should we incorporate this sort of data into the compatibility table somehow as a signal to ones who don't support it yet that they should?
From my testing with the wallets I have access to I came up with the following:
Wallet Name | Supports LUD-01 Fallbacks |
---|---|
Alby | ✅ Yes |
Blink (Bitcoin Beach Wallet) | ✅ Yes |
Blixt | ✅ Yes |
BlueWallet | ✅ Yes |
Breez | ✅ Yes |
coinos | ❌ No (opened issue) |
lnbits | ❌ No (opened issue) |
Muun | ❌ No (opened issue) |
OBW | ✅ Yes |
Phoenix iOS | ✅ Yes |
Phoenix Android | ❌ No (opened issue) |
Simple Bitcoin Wallet | ✅ Yes |
Valet | ✅ Yes |
Wallet of Satoshi | ✅ Yes (tweeted, fixed) |
Zap Android | ❌ No (opened issue) |
Zeus | ✅ Yes |
I am trying to implement this but without luck. Found this JS implementation:
https://github.com/sipa/bech32/blob/master/ref/javascript/bech32.js
but it doesn't seem to work. I am comparing to the bech32 library and I am not receiving the same output with the same inputs.
Looking at the bottom off this python file: https://github.com/fiatjaf/bech32/blob/master/bech32/__init__.py, seems like I need to convert the data into a 5-bit array? Which isn't done by bech32.js
Found this code that should be able to convert to a 5-bit array... https://github.com/bitcoincashjs/cashaddrjs/blob/master/src/convertBits.js
It's all a mess and I'm not sure I can do anything on my own, I am not very familiar with bech32 and bit-operations in general.
I am not sure how your example works (https://gist.github.com/btcontract/a917d9b8b9b8fd53a87a81528e1274c5) because I am using the same library (I think). Installed with npm install bech32
Following the code... This line gives:
bech32.encode('LNURL', 'https://satoshis.games/withdrawal/confirmation?q=')
'lnurl1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqesxn47'
Help appreciated!
Hi,
while implementing lud-16 I noticed that ln.tips and lntxbot.com both return a valid response no matter what name you input there.
zbd.gg returns an error stating "User not found".
My PR (#105 ) where it says that it should throw an error in the first json response was merged after those services implemented it.
ln.tips failed later when actually trying to make the payment.
But when sending 100 sats to a completely random lntxbot.com address the payment went through.
What do you think, should we contact them to change the behavior?
6 years ago, there was SQRL, and it was a beautiful thing which worked exactly like lnurl-auth today. Server would show a QR code with an URL containing a secret nonce, clients would scan or click, compute a public key specific for that domain, send a request to it containing the public key, the nonce and the signature.
Today, while I was reading that I couldn't help but to think lnurl-auth should actually be removed and BLW should become an SQRL client. The required changes would be minimal. The biggest contribution of BLW to the SQRL ecosystem would be linkage between the Bitcoin seed and the auth seed, which would solve a ton of problems, frankly.
However, the SQRL protocol has changed. Now it is a completely different thing with 80 pages of documentation and hundreds of different options and commands every client must send and servers must understand, it's completely crazy to expect anyone is going to implement that shit.
However, lnurl-auth is still a workable thing that could be useful in many many places, it's very easy to implement both the server and the client part, much easier than passwords.
But it shouldn't be tied to Lightning. It should be an open thing with no special restrictions on how the signing keys are to be created. They could be created from anything. There could be guidelines specific to Lightning or Bitcoin seeds, but these are not necessary for the protocol to work and shouldn't be enforceable or emphasized.
Having it in this repository is confusing, specially now that lnurl-withdraw and lnurl-login don't use the identities provided by lnurl-auth anyway (before it could make sense to keep it here) and that lnurl is going to be incorporated into the BOLTs and disappear.
Maybe it should be named "seed-auth" or something like that. Anyway, the name is not actually important as long as it doesn't imply it's part of something bigger ("lnurl") or strictly related to Lightning ("ln").
Problem
On iOS, if there are multiple wallets that support the lightning: scheme or LUD-17 then there is no mechanism for apps or games to offer a choice to the user on which wallet to open. The wallet that would open is undefined.
Idea
Wallets could publish their scheme for opening their wallet e.g. zebedee:
and then apps/games can detect if the wallet exists.
If the user of the app/game wishes to open ZEBEDEE with the lnurl, the developer would be open the wallet with the following format:
zebedee:lnurlw:<insert lnurl here>
[side note, this format already works in ZEBEDEE wallet, not sure if this is by design]
The wallet could then process the lnurlw scheme as if it had been opened via lnurlw:<insert lnurl here>
This would be optional for wallets if they want to support it of course.
By recommending that lightning:
is used to open LNURLs for apps, it causes a clash with apps that don't or never will support LNURL, but use the lightning:
scheme.
For example, Bottle Pay probably won't support LNURL (at least initially). And on iOS, if Bottle Pay is the last app that is installed with the lightning:
scheme, any web pages or apps on the device will redirect to Bottle Pay by default, with no option to change the default behaviour in the operating system.
On Android the user can change default behaviour, but its buried in the OS settings and is a customer support issue that could be avoided.
Perhaps the scheme should be lnurl:
to avoid this.
I'm trying to implement this via ruby on rails. For testing, I'd like to use my laptops internal IP address. The wallets I've used so far, seem to view IP addresses as invalid LNURLs
Is there a lnurl-auth wallet I can use for IP addresses in place of domain names for development purposes?
Bluewallet doesn't seem to be supporting lud12. Can someone confirm? Will open a PR to remove it if so
Here are my thoughts on how this should look like before getting to PR:
SPEC SIDE:
Recurrence points may happen MONTHLY (1st/2nd/3rd/last WEEKDAY) or YEARLY (1st/2nd/3rd/last WEEKDAY of MONTH), this is a subset of RRULE of iCal format.
LNURL-PAY has an optional RECURRENCE_FIELD which contains "MONTHLY/YEARLY; PATIENCE_PERIOD_DAYS; SERVER_TIMEZONE" data. Recurrence point is determined by both WALLET and SERVICE on the day of the first successful payment (SERVER_TIMEZONE is provided to ensure that point is the same for server and client). PATIENCE_PERIOD_DAYS means "you have this many days to pay since next recurrence point until I do something about it on my side", what that something exactly is specific to service, wallet mainly needs this for grouping of events into batches and maybe warnings.
metadata may contain a THIS_OCCURRENCE_NOTE field relevant for this particular occurrence which may contain free form text ("we're raising amount for you this time only because..." or "next payments will be happening annually" accompanied with MONTHLY -> YEARLY change in RECURRENCE_FIELD).
Pay Json response may have an optional currentBalance
field (similar to #76). If for example user has paid 3x of what was requested last time for some reason, it's up to wallet how to react to this, for example if pay Json returns 500 as payment amount but balance is 1500 then wallet may inform user that next payment is not due and allow to uncheck it for this one time. Or if user has tapped a stored pay link with recurrence, wallet may show server response along with warning that next payment is not due yet accompanied with balance, but allow user to pay anyway if user insists.
Overall it is expected that WALLET re-queries a static LNURL-PAY after each recurrence point, how much is to be paid this time is defined by SERVICE each time (along with explanations in THIS_OCCURRENCE_NOTE if necessary), recurrence parameters can change (for example user has opted for YEARLY with discount whereas last time it was MONTHLY).
Recommendations for WALLET SIDE (does not go to spec but justifies spec decisions):
WALLET should convert recurrence points to days of month for each month (exact dates will differ from month to month), then try to group overlapping payments into batches such that there are as few monthly calendar reminders as possible, ideally one reminder for all recurrent events (guaranteed if all events have PATIENCE_PERIOD_DAYS=31days, but this is up to SERVICE). For example if there are two payments: first on 3rd of May with PATIENCE_PERIOD_DAYS=10days and second on 9th of May with PATIENCE_PERIOD_DAYS=5days then there will be one calendar reminder on 9th of May.
WALLET should store payment history and balance changes if applicable to provide user with context, so user can compare new info with history and for example see that "this time SERVICE wants 1000 SAT while last time it was 1100 SAT because we agreed a sum to be denominated in USD and I see that BTC has appreciated since then".
Hello. I think the k1 value used during withdrawRequest process should be signed using the same key derivation process outlined in LUD-04. That way, requests from the same domain are signed by the same key used in LUD-04, and a stored user session can also verify a wallet responding to a withdrawRequest.
This will solve the problem outlined in LUD-03 where anyone with the k1 value can submit their bolt11 invoice to be paid. With the proposed change above, sites could reuse a user's authenticated session from LUD-04 to verify a signed k1 value for processing a bolt11 withdraw.
Thank you for reading. Let me know what you think.
While requiring HTTPS URLs for lnurl-pay is fine for any service offered over the internet, it sometimes make it very hard for small people who don't know or are unable to be running stuff on their own domain to accept payments in that way.
Without HTTP, it would be possible for people to use lnurl-pay inside LANs, through IPv6-powered home computers, or even to run lnurl-pay endpoints from their own phones, so easier for local merchants individuals to do this without relying on trusted third-parties.
Since lnurl-pay already relies on a final invoice issued by a Lightning server and that invoice is signed, one could optionally opt in to the HTTP verification by appending a ?pk=<lightning-node-pubkey>
query string param to the initial URL that is shared with the payer.
Then, if the communication was done through HTTP and not HTTPS, the wallet would check if the initial pk
matches the public key that signed the final invoice received as an extra check.
If your wallet is in your phone or watch, scanning a QR code and doing something with the lnurl
is ok. But what if your wallet is running in your desktop or laptop? lnurl
must be clickable, and it must redirect to the proper your computer somehow knows is capable of handling lightning stuff.
This isn't a big problem, but here's an issue for further reference.
As far as I can see this spec doesn't specify response status codes.
For example most LNURLp implementations I've come across return 200
after the call to /.well-known/lnurp/something
.
I just found that this one from IBEX:
LNURL1DP68GURN8GHJ76TZV4UXSATZ9E5KYETCD4JHYCMPV3HJUCM0D5HKCMN4WFKZ7URP0YHKJMNKDA5KXEFDWFJHZATFWFJK6ETWW3EN76E385CKGCTRXCENGDPEXQURGCF5XSERWE3KVYMRXWF3VEJRWCMPVESNGVFKXY6XYCMPX4JNZD3NVCUNXDRX8P3K2ER9VCCRVVP5XGCRJEF3E60P6U
Returns 201
. Understandable but there should be a clear spec.
I propose to specify a new protocol for automated user-friendly withdrawals. It's a smarter way to implement invoice-less sending.
It often occurs that some kind of automated system of the payer can determine the obligation to pay. Examples include: ATMs (based on user inserting money), exchanges (based on automatic buys facilitating DCA), payroll software...
Currently the process is as follows:
This process has several serious drawbacks:
As a solution I propose a new sub protocol to LNURL.
All this is based on real-life experience with various LN services, first-hand experience with customers and baristas in Paralelna Polis and discussion with Karel Kyovsky, CEO of General Bytes - Bitcoin ATM manufacturer.
I've read the documentation of Firebase and don't have a personal experience with it. It may happen that this specification is lacking something to actually allow push notifications.
The response message from the server is:
{
"tag": "autoWithdraw",
"k1" : "<RANDOM SECRET>",
"capabilities": {
"websocket": "wss://example.com/ws", // Websocket notification endpoint
"fcm": "https://example.com/fcm", // Push notification registration endpoint
"openChannel": { allowed: true, "uri": "<LN NODE URI>", "min" 100000, "fee": 1000, "spendUnconfirmedPush": true } // the server supports opening a channel. The fee is flat in satoshis
},
"callback": "https://example.com/withdraw",
"singleUse": true, // if false - long-term business relationship exists, true - single interaction
"allowPartial": false // the service may decide to allow or forbid partial withdraws
}
The wallet proceeds by subscribing to websocket or fcm providing ?k1=SECRET
. In case of fcm, it provides full json that should be sent to the server in the body using POST
method. The application may re-subscribe by POST
ing again, which causes the json to be replaced by a new version. The server treats it as opaque data and just re-sends it when the user should be notified. The wallet can unsubscribe using DELETE
method.
Websocket notifies the application by sending the same message that would be a reply of callback with action=poll
(see bellow)
The callback has required parameters k1=SECRET&action=ACTION
Action may be:
reject
- the wallet identified that the capabilities aren't appropriate and signals this to the serverpoll
- this should only be called after receiving a notification from FCM, not periodically, the server responds with message described bellowinvoice
with additional pr=INVOICE
parameter - requests withdraw, server responds with { "status": "OK" }
or { "status": "ERROR", "reason": "..." }
just like in case of lnurl-withdraw
channel
- with remoteid=<local LN ID>&private=<1 or 0>
parameters - request channel open like in case of lnurl-channel
, the server replies with { "status": "OK" }
or {"status" : "ERROR", "reason": "..." }
Response to poll:
{
"maxWithdrawable": 10000000, // millisat
"minWithdrawable": 1, // optional defaults to maxWithdrawable
"defaultDescription": "<INVOICE DESCRIPTION>",
}
singleUse
is true
, the wallet discards k1
and other metadata after successful withdrawal. In case it's false, it keeps it stored and continues listening for notificationsallowPartial
to false avoids refunding/accounting hell (as explained by Karel; big turbo channel is more practical anyway; splicing might be added in the future to avoid multiple channels)Hi there,
lud-6 metadata is still unclearly defined, even after the last update.
Would the following "nonArrayEntry", be allowed?
[
"nonArrayEntry",
[
"text/plain",
"HfnJxZojptcreqTXkWPLT w"
],
[
"application/payer-ids",
[
"text/plain"
],
[
"application/pubkey"
],
[
"text/identifier"
],
[
"text/email"
],
[
"application/lnurl-auth",
false,
"d394e8f300e30d9aba0a1e8fc48a92a2252cfa3b215c5421b888d6884036a59b"
]
]
]
The security policy for this repo has not been set and so it is not clear how security related issues should be raised.
LNURLPoS and LNURLVend encrypt a pin on each payment, which is passed to server through an LNURL-pay, then decrypted and sent back in the receipt upon completion of payment. The crypto was elegantly written by @stepansnigirev and using readily available bitcoin crypto.
On LNURLPoS the encrypted pin is added to LNURL here: https://github.com/arcbtc/LNURLPoS/blob/40cb199c4599d9f6c64b7e372a5c7938b75a83f0/LNURLPoS/LNURLPoS.ino#L232
On LNbits server side, it is decrypted and sent back as a receipt here:
https://github.com/lnbits/lnbits-legend/blob/a2bb34060102d049e07766436359e9526e3d8aab/lnbits/extensions/lnurlpos/lnurl.py#L37
Bleskomat uses practically the same method, but for LNURL-withdraws and their offline ATM.
If we made encrypting a confirmation pin and passing through LNURL, then sending back decrypted pin in the receipt, part of the LNURL protocol, then a service like OpenNode could support it directly and LNURLPoS, LNURLVend, Bleskomat, etc would be compatible native with their platform.
I tried to join your Telegram group on Android. I copied the ln invoice into Breez wallet and Breez showed a toast saying that I am sending 1 sat. After that 1 sat was not subtracted from my balance and I was banned from the group for a day. It's been 31 hours now and I am still banned.
The idea here is that, additionally to the <username>@<domainname>
format of LUD-16 static internet identifiers, a SERVICE
can offer multiple addresses resolving to the same user by using the format <username>+<tag>@<domainname>
, e.g. [email protected]
(see Plus signs ("+") in email addresses). The normal flow does not change: Upon seeing such an address, WALLET
makes a GET
request to https://<domain>/.well-known/lnurlp/<username>+<tag>
endpoint.
It is upon the SERVICE
to decide what to do. If it supports tags, it SHOULD strip the +<tag>
part and continue as normal.
A SERVICE
MAY subsequently include the tag in the metadata
property of the response:
[
"text/tag", // optional
string // the tag that has been passed in `<username>+<tag>`
],
Currently, the <username>
character set is limited to a-z0-9-_.
which means the +
sign has not been allowed yet and wallets that are not aware of it will either just deny it, or support this change and immediately use <username>+<tag>
as <username>
. I suppose that most wallets do not validate for a-z0-9-_.
and will happily use everything up to @
as username. The only tested wallet is Zeus, which supports sending usernames with +
.
Example of a service that already accept +tag
: https://ln.tips/.well-known/lnurlp/fiatjaf+test (albeit without resolving to "fiatjaf" and a dedicated entry in metadata)
Example of a service that does not support +tag
: https://stacker.news/.well-known/lnurlp/fiatjaf+test
With all that said, I propose adding +<tag>
as an OPTIONAL feature with a suggestion for SERVICE
s to handle them accordingly. No already existing SERVICE
is forced to update and can continue to work as is.
With this suggestion, a SERVICE
can offers users a "standardized" way to use multiple identifiers. e.g. a user can include [email protected]
in the description of the first episode of a podcast, [email protected]
for the second and so on and so forth. For written text it might be [email protected]
, for code repositories it might be [email protected]
or [email protected]
, etc.
Since a user knows whether a SERVICE
supports this scheme, an identifier with +<tag>
is used only when this is the case - so there is no disadvantage on the SERVICE
side. On the WALLET
side, it might be that the GET request will not be performed if the wallet follows the current validation rules. In this case, a user SHOULD strip the tag manually - which is arguably not desirable.
Should this be an addition to LUD-16 or would you rather suggest this being a distinct LUD? Imho, an own LUD seemed overkill as the changes would be minimal and optional. What do you think?
Any and all suggestions are highly appreciated 🙏
verify
base spec.author: [TBD]
discussion: [TBD]
The idea here is that a SERVICE
can verify an invoice has been paid by contacting the LN SERVICE
.
A SERVICE
can verify a payment was made without access to the user's node. Users can give their LNURL or Lightning Address to a SERVICE
and allow the SERVICE
to create and verify invoices on their behalf.
This allows users to re-use existing wallets and SERVICE
s to facilitate P2P payments without being an intermediary.
An optional field verify
is added to the callback
response:
{
"status": "OK",
"routes": [],
"pr": "lnbc10...",
+ "verify": "https://example.com/verify/894e7f7e...",
}
The field verify
provides an URL to a SERVICE
to check if the invoice has been settled:
https://example.com/verify/894e7f7e...
Response
{
"status": "OK",
"settled": true,
"preimage": "123456...",
"pr": "lnbc10..."
}
{
"status": "OK",
"settled": false,
"preimage": null,
"pr": "lnbc10..."
}
or
{
"status": "ERROR",
"reason": "Not found"
}
Looking forward to hear your feedback!
In LUD-03
in the callback the LN WALLET
send a Bolt11 Lightning payment request (invoice).
Bolt11 allows that a payment request might contain only a hash of the description (tag h
)
Bolt11 specifies that if the tag h
is present in a payment request a lightning node:
- MUST check that the SHA2 256-bit hash in the `h` field exactly matches the hashed description.
To be able to do so, a lightning node must learn the preimage together with the bolt11 payment request (containing a hashed description) before it can initiate a payment.
LN Wallet
must also provide the pre-image of the hashed description used in the Bolt11 payment request (aka LN invoice).LUD-03
would need to be extended, so that if a LN WALLET
uses tag h
in the generated Bolt11 payment request, it also MUST return the preimage of tag h
in its callback.LUD-03
should make this mandatory and introduce an additional GET parameter for the callback (in addition to already existing k1
and pr
).I was thinking that it'd be useful to extend this specification for cases when the customer has a business relationship with other party and the other party might owe him some satoshis. This is different from the mere withdraw case, when it's only possible to withdraw over LN and actively scanning the QR code - in this case opening a (turbo) channel should be possible too. Further, I'd like to see a way to enable the users to withdraw without scanning anything.
Cases when this might be useful:
Rough idea of extension:
A new fields "accountBalance" and "accountCapabilities" are added to login response object. Here's an example:
{
#...
# msat
"accountBalance": 1000000000,
"accountCapabilities": {
# whether the service can open a channel in case of routing failure
# MANDATORY (the field must be present even if false)
"openChannel": true,
# msat, not present if openChannel is false
"openChannelFee": 1000000,
# sat, the minimum amount for opening the channel
"minChannelAmount": 100000
# Indicates whether the opened channel supports spending zeroconf push - AKA "Turbo channels"
"spendUnconfirmeddPush": true,
# The wallet can GET this URL to retrieve current balance, MANDATORY
"pollURL": "https://example.com/acount/poll",
# Minimum delay between polls in seconds to prevent overloading
"minPollPeriod": 30,
# The wallet can use this URL for withdraws, MANDATORY
"withdrawURL": "https://example.com/account/withdraw"
# OPTIONAL, the wallet can use websocket to wait for balance changes
"websocketURL": "https://example.com/account/listen"
# OPTIONAL, the wallet can provide a callback that will be triggered whenever the balance changes
"subscribeURL": "https://example.com/account/subscribe"
}
#...
}
Websocket sends same messages as poll response: JSON object with MANDATORY "accountBalance" field and OPTIONAL "accountCapabilities", which is same as above. If it's missing, the wallet assumes it didn't change since the last poll.
This is just the protocol, the wallet is responsible for all automation (if any). In the future, it should be extended with splicing in, if there's an open channel between the parties. The provider MAY use trusted third-party service for opening the channels.
Additional ideas: on-chain withdraw for larger amounts, topup (off and on-chain)
What do you think?
The Readme mentions:
Once LNURL is decoded:
- If tag query parameter is present then this LNURL has a special meaning, further actions will be based on tag parameter value.
- Otherwise a GET request should be executed which must return a JSON object containing a tag field, further actions will be based on tag field value.
Does the first point mean the original URL encoded in the QR code can contain tag=withdrawRequest
and then the wallet can skip the first HTTP GET and get all the other parameters from the original URL?
In this case the URL in the QR code would also have callback, k1, defaultDescription, minWithdrawable, maxWithdrawable query parameters?
So the withdraw flow would be:
LN WALLET gets callback, k1, defaultDescription, minWithdrawable, maxWithdrawable from url query params
[...]
This is not mentioned on the LNURL-withdraw page or the other pages. It would save one round trip of HTTP but is it actually supported by wallets?
The LNURL flow describes that after the LNURL is decoded, a request is made to the LN SERVICE to attain the full transaction data in JSON format. The reason for this step is encoding transaction JSON into the LNURL directly would make it too large and impractical for QR codes.
However, when two apps communicate via the lightning:
URL Scheme, size limitations are not relevant. For App to App communication, we could encode the transaction JSON in a LAPPURL, meaning the GET request to the service is no longer required.
This optimisation is specifically for the use case of app to app communications, intended to be hidden from the user. This change would have the following advantages to developers of Lapps who plan to use the lightning:
URL Scheme only in their product;
LN SERVICE
.LN SERVICE
fails to deliver the transaction JSON for whatever reason, this is handled differently by different wallets.LN_SERVICE
Here is an example of how a LAPPURL could be used for withdrawals
Lapp to Wallet service interaction flow:
User taps a withdrawal button in LN APP
, which creates a LAPPURL - a bech32 encoded json:
{
callback: String, // the URL which LN APP has defined as it's LN SERVICE, would accept a withdrawal Lightning invoice as query parameter
k1: String, // random or non-random string to identify the user's LN WALLET when using the callback URL
maxWithdrawable: MilliSatoshi, // max withdrawable amount for a given user on LN SERVICE
defaultDescription: String, // A default withdrawal invoice description
minWithdrawable: MilliSatoshi // An optional field, defaults to 1 MilliSatoshi if not present, can not be less than 1 or more than `maxWithdrawable`
tag: "withdrawRequest" // type of LAPPURL
}
LN APP
opens LN WALLET
with lightning:LAPPURL..
and LN WALLET
decodes LAPPURL to retrieve the JSON.
LN WALLET
Displays a withdraw dialog where user can specify an exact sum to be withdrawn which would be bounded by:
max can receive = min(maxWithdrawable, local estimation of how much can be routed into wallet)
min can receive = max(minWithdrawable, local minimal value allowed by wallet)
Once accepted by the user, LN WALLET
sends an HTTPS GET to LN SERVICE
in the form of
<callback>?k1=<k1>&pr=<lightning invoice, ...>
LN SERVICE
sends a {"status":"OK"}
or {"status":"ERROR", "reason":"error details..."}
JSON response and then attempts to pay the invoices asynchronously.
LN WALLET
awaits for incoming payment if response was successful.
Could we adapt LNURL to work with https://bitcoinqr.dev standard?
Related:
sbddesign/bip21-site#70
I've been implementing Lightning Address for lnsms.world the last few days and wanted to share my
experience and thoughts.
The idea was to allow users to simply specify [email protected] as a destination to be able to easily send a text message to the phone number NUMBER. On the surface it seemed like a perfect fit.
One major issue I have though is that due to the complexities of how SMS messages are encoded, it's impossible for me to know how many bytes a message will take up and consequently how much I need to charge for sending it without first seeing the complete message.
I could take a guess, but if i want to be completely safe the limit i would have to put on message size would be ridiculously restrictive in relation to the price.
Unfortunately, the way LA/LNURL currently works I need to give a price or a price range before I see the comment (which I my case will be the content of the message to send). So what I would like is a way of not having to give either a price or a price range (and ideally not a comment limit either), but just indicate that I would like the client software to give me a comment and in exchange I will give an invoice.
The second issue I had is that all information being exchanged is sent as request string parameters in a GET request. That means in a typical configuration it will be written to the webserver's access log, for example /var/log/nginx/access.log. So in my case both the recipient number and the full message contents ends up there, which I consider a big no-no. I'm very careful to not store that kind of information anywhere for obvious privacy reasons. Fortunately it was relatively straight-forward to use a custom log format + some regex matching to strip that out but I imagine this could be a problem for other services as well.
While working on this I got some ideas on how, in an ideal world, LA/LNURL would work to be 100% adapted to my use-case:
I imagine something like this:
There is only one endpoint. The client would repeatedly hit that endpoint until it gets an invoice. The first time, the client would not supply any data (same as now). If there is no data required, the server would simply respond with an invoice immediately. If any data is needed, the server would instead respond with a list of required fields. Those could include an exact amount (and in this case the server will provide an acceptable range) and it could also include a comment, with or without a max length. The client the asks the user for the required information and hits the endpoint again, this time with the data attached. (The client chooses wether to use a GET or POST for this and the server will respond to either.) The server once again either responds with an invoice, or again gives a list of additional data fields that are required. This goes on until the server is satisfied and responds with an invoice. When the client gets the invoice it asks the user for the final confirmation, using the amount in the invoice and then makes the payment.
In the vast majority of cases this will only involve one or two requests, but in some cases it could be three or even more. So there will basically be a kind simple dialogue between the server and the user. I think something like this would be flexible enough to cover most use-cases and at the same time save in one HTTP request in those cases where there is no need for the user to specify an amount.
I realize there are most likely reasons you have designed LA/LNURL they way you have and regardless it might be hard to change fundamental parts of it at this stage but I still wanted to share.
As it is now, using Lightning Address with lnsms is a terrible user experience.
Feel free to close this if this has been discussed already.
I'm seeing 1 or more email sign up failures on SN per day from users attempting to use their lightning address as an email address.
It got me thinking it'd be nice if this initiated an lnurl-auth login without the QR crap. E.g. send the lnurl-auth challenge, callback, etc. to the domain
of the lightning address, and have the service at domain
go through the lnurl-auth flow from there.
This would be particularly nice for services that send tips to lightning addresses as the user could auth and specify their preferred receiving wallet simultaneously.
A few things occurred to me after posting this. lnurl-auth via lightning address will need prompting in the wallet. If we are okay with prompting via lightning address, perhaps we could also support lnurl-withdrawals via lightning address. In this way, you'd be able to provide most of a wallet's functionality through the lightning address alone.
Maybe this a dumb idea.
Here's a QR code for https://charger.bigsun.xyz/lnurl-login?tag=login&k1=b58e7b599ac78aea4393a34f4250e3abbe31f9674ba28cf8db113de28de5efbe
:
And now one for LNURL1DP68GURN8GHJ7CMGV9EXWETJ9E3XJEMNW4HZU7RE0GHKCMN4WFKZ6MR0VA5KU0M5V9NN6MR0VA5KUFNTXY7KYDFCV5MKYDFE89SKXDECV9JKZDPN8YEKZVE5VC6RYDFSV5EKZCNZV5ENZE3EXCMNGCNPXGUXXE3CV33RZVFNV3JNYWRYV56K2ENZV5ZUKEYM
, the lnurl-bech32-encoded version of the above:
Apparently the uppercasing QR-reduction properties aren't so powerful as one would think initially.
According to the LNURL auth specs the key used by the wallet
for signing the k1
secret should be specific to the service
, using the service
domain name. However the spec is not clear whether the key should be generated with the complete service
domain name including subdomains, or the top domain name.
Wallet uses subdomains
The service
may internally use a subdomain for everything LNURL related, like api.service.com
. In that case the account will be forever coupled to whatever internal architecture the service is currently using. If the service
architecture changes, the wallet
will use a different signing key, effectively creating a new account and locking the user out of his previous one.
Wallet uses top domain only
If there are several services on the same top domain, then the signing key would be the same for all these services. Yet, regarding security, each service maintains its own k1
secrets and will reject any authentication attempts using k1
secrets it has not solicited. That is, if you have 2 services, one for testnet on api.testnet.service.com
and one for mainnet on api.mainnet.service.com
, one cannot sign-in to mainnet
using a signature valid on testnet
.
Regarding privacy, the account public key is the same for every services on a top domain, and someone operating 2 of those services would know that it's the same user. But that will happen anyway, even if we use subdomains, as soon as the user provide a LN invoice to the service (containing the node public key).
So, in any case I think the spec should be clear on this, and should recommend one option or the other. I think using top domain is the better option, with subdomains the risk of being locked out is too great.
With LUD-16, what prevents someone from requesting an infinite amount of invoices from your ln node? Seems like this could cause problems with wasted data and bog down the ln node keeping track of a bunch of invoices that may never be used. Could some kind of proof of work be required in order for an invoice to be returned?
1000
Hello,
Commit d9f902c brought a significant change to the lnurl-pay
flow: instead of having a single GET
that would retrieve all the necessary data for the payment in one go, the client must now:
GET
payment metadata from the serverGET
the actual BOLT11 invoiceI could only find the following rationale for this change, in issue #9 (comment):
1 is simpler and easier to implement as it involves less roundtrips but it doesn't allow for undefined-amount payments and it also leaks the customer privacy as he scans the QR code even if he didn't decide to pay.
Which does not make sense to me:
GET
flow.GET
to some server as soon as the QR is scanned, even if that's only to retrieve some metadata.Is there another rationale for this change? It seems to me that one of the key objective of LNURL-pay is to provide static invoices, that is, being able to generate a Lightning invoice from a fixed URI
. Having a 2-steps flow breaks this, and makes the protocol harder to implement, so I may have missed something?
Sorry if this has already been discussed.
In lud-18 there is no description at all how this should be handled on the wallet in regards to UX.
Is the wallet required to notify the user that it sends the pubkey as identity or does it do it without the users knowledge?
Does the user have to input email, identifier and a text, if SERVICE has all of these 3 identities as mandatory?
Does the wallet have to make sure valid data is inputted in email and identifier field?
Further more I have to admit that I don't really see a usecase for those loose identification methods.
Email, identifier, text and pubkey do not proof anything. The only method that proofs a identity is the "auth" method.
Do you have a specific example where the loose identification methods are needed or already used and why?
Implementing the UI for those loose methods in a wallet is quite a lot of work as it involves extra input fields
Why can't the lnurl
have just query strings as normal URLs?
What about using /.well-known/whatever
endpoints? Aside from handling the cases described in https://github.com/btcontract/lnurl-rfc/blob/61071a18453c3e8a99f65c1db94fdd1c2f2e1247/spec.md, LN services could have /.well-known/lnurl:withdraw
URLs, for example, that would allow automatic withdrawing at the request of the wallet, without user interaction (so a wallet could programmatically withdraw funds from a custodial site every day or week, for example).
Other interesting cases are /.well-known/ln:deposit
for depositing money on websites from the wallet itself, without the user even having to scan a QR code or deal with an invoice. This could also be used by individuals who want to be paid and have a webserver (or a variant, /.well-known/ln:pay
).
All these cases are doable with the lnurl
scheme described, I'm just suggesting .well-known
because it is a (sort of) standard.
In LUD-16, you have <domain>
. It is my assumption (although it's not clearly defined) that this is referring to the DNS A record for the domain? This is very inflexible. It is every likely that one may not want to run a service on the host defined by the DNS A record to serve lightning invoices. It is very likely that their main website will be hosted there. E-mail has MX records for this very purpose. I propose that you create a new optional DNS record type that defines the host that should be contacted to request the invoice from. If that record is defined, then don't use the A record. If the record isn't defined, use the A record (just like e-mail).
Additionally, you may want to give the user the flexibility on what TCP port is used to connect to instead of port 80 or 443. You might want to also define a DNS SRV record that allows this to be changed. This can be very useful to people who have only one IP address but still want to run the invoice serving service on a different machine than their website.
In many places the spec says a GET request is sent with valuable information. GET requests are for "fetch" and "query" idempotent stuff, not for requests that trigger actions, in these cases the spec should say POST.
Hello, the following example link return 404, it should not be interpreted as URL with markdown because it can lead to confusion, thank you!
An example LNURL:
https://service.com/api?q=3fc3645b439ce8e7f2553a69e5267081d96dcd340693afabe04be7b0ccd178df
I think having a standard for static tlv/keysend invoices could be cool.
Additionally to the standard lnurl-pay data the lnurl-pay-tlv data could contain:
destination pubkey
list of tlv payloads, such as fixed ones (userid) and maybe optional ones like custom donation messages
This would basically eliminate the 2 roundtrips of lnurl-pay as all of the above information is enough to have the same information
The specification doesn't describe what response should be at step 2 when there's an error - e.g. the secret is invalid. Should there be specific JSON body or just HTTP error? Which HTTP error?
I am looking for a way to implement the following flow with LNURL:
Suppose the merchant is a video streaming service that charges monthly. A user browses around on the merchant's website and decides to sign up.
An extension to this is that the user can instruct their wallet to auto-pay invoices from that merchant up to a certain limit. The limit can be hinted in the initial merchant QR code too, so that approval of push invoices and approval of the (hinted) limit can be done in a single action.
The flow contains elements of LNURL-withdraw, but isn't quite the same. I don't think the initial merchant request to push invoices via a QR code is covered already.
Any thoughts on this?
The callback url in payRequest makes it impossible to create a static implementation of a payRequest server - the server must know its full URI to return in the callback parameter. It would be nice to be able to specify at least server-relative callback URLs so that a static implementation (run behind NGINX or some other TLS proxy) could be built that doesn't need additional LNURL-specific configuration
LNURL-withdraw is increasingly used for convenient contactless payments via NFC devices that do not require a battery and do not have a user interface.
While these devices can produce unique one-time LNURLw-links with replay protection there is still the risk of losing the device or maliciously scanning the device without knowledge of the owner.
Security of tap & pay experiences could be improved by adding a second factor PIN to withdrawRequest callbacks.
I prepared a draft pull request for a new LUD: #200
A protocol like this could be the solution for all services and websites that want to show a static QR code for payments (like a static site or a site not directly connected to an LN node), a QR code that could be printed on paper and so on -- while at the same time allowing for payment/user identification without loss of privacy (by the optional usage of per-service linkingKey
s).
Two ideas of how this could be done come to my mind:
?tag=pay&k1=<challenge>
in the lnurl and immediately calls the URL with the signed challenge;pr
(an invoice) along with metadata
;tag: "pay"
plus callback
, k1
, metadata
and pricing information somehow (a fixed price or min/max prices?);callback
, plus the k1
signed with the user's linkingKey
, then gets a pr
(invoice) back;The spec 1 is simpler and easier to implement as it involves less roundtrips but it doesn't allow for undefined-amount payments and it also leaks the customer privacy as he scans the QR code even if he didn't decide to pay.
The metadata
field could be a string, a JSON object, raw HTML or who-knows-what, but the wallet will have to display it to the user somehow. Perhaps a good idea would be to do like emails do and return the metadata in all available mimetypes the service supports (a raw string should be mandatory) so the wallet can choose the one it wants and display.
The metadata could also be hashed into the invoice description (or in the descriptionHash
field if that's still a living thing). This is useful for accountability between the parties.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.