Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic withdrawals #14

Closed
Kixunil opened this issue Oct 12, 2019 · 6 comments
Closed

Automatic withdrawals #14

Kixunil opened this issue Oct 12, 2019 · 6 comments

Comments

@Kixunil
Copy link
Contributor

Kixunil commented Oct 12, 2019

Summary

I propose to specify a new protocol for automated user-friendly withdrawals. It's a smarter way to implement invoice-less sending.

Motivation

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:

  • The service must somehow notify the user about the balance (this is often, but not always during user interaction)
  • The user is requested to enter an invoice
  • The user manually creates the invoice and enters it into the system somehow
  • The system attempts to pay the invoice
  • If the attempt fails, refund needs to be processed
  • In theory, channel open with push (Turbo or not) could be offered via LNURL, which would require further manual interaction. I'm not aware of any service that provides this currently.

This process has several serious drawbacks:

  • It requires many manual steps, which isn't particularly user-friendly
  • It may be confusing for newbies
  • Amount must be correctly manually entered, amount-less invoices are insecure
  • The server has no way of determining if the wallet is compatible with LNURL protocol

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.

Design goals

  • Sending over existing LN channels is preferred
  • The user should not be bothered with deciding which method to use
  • Provide a good fallback for scenarios where LN payment fails
  • The protocol should be well-usable for both mobile and "server" (always-online routing node) wallets
  • Specify minimum (but extensible) protocol according to current needs. The intent is to not over-specify

Disclaimer

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.

Specification

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 POSTing 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 server
  • poll - this should only be called after receiving a notification from FCM, not periodically, the server responds with message described bellow
  • invoice 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>", 
}

The flow

  • When the user scans QR code, the wallet checks capabilities according to wallet internal policy.
  • If capabilities are fine, the wallet subscribes to updates
  • Whenever the wallet receives notification, it checks if it has enough incoming capacity for LN invoice
  • If the wallet has enough capacity, it attempts to withdraw using invoice, it attempts to open channel in case of failure
  • If the channel opening is allowed, the wallet attempts to open a channel
  • If channel open fails, the failure is presented to the user
  • If 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 notifications

How does this solve the situation above

  • The QR code is scanned once
  • Single QR code for invoice and channel - newbies not forced to decide
  • The protocol requires no further interaction from the user (but it might be reasonable for wallet to infomr the user about the cost of opening the channel)
  • Incompatible wallets will simply fail the protocol before any interaction is attempted
  • FCM makes sure mobile wallets can be notified
  • websocket protocol allows for "server" wallets to be notified (this doesn't need to be built-in to LN nodes - might be an external app)
  • setting allowPartial 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)
@Kixunil
Copy link
Contributor Author

Kixunil commented Oct 12, 2019

Note that I'm willing to help with implementation of this spec in BLW, server-side, and possibly other wallets. I have practically zero experience with mobile apps, but I'm willing to learn.

@fiatjaf
Copy link
Collaborator

fiatjaf commented Dec 8, 2019

I think the "automatic" part of this protocol is too convoluted to be included in the lnurl spec -- it should rather be tried first on a wallet and service and then have its own "super spec" that uses lnurl-withdraw and lnurl-channel at its base.

However I've recently realized that the part about doing withdrawals either through normal lightning transactions or turbo-channels is doable with the current lnurl spec and works on BLW today: the service can just have an lnurl for each user/event and decide at the time the wallet scans the QR if it will return a "channel" or a "withdraw" response.

@Kixunil
Copy link
Contributor Author

Kixunil commented Dec 9, 2019

Fair, as I said, I'm willing to code it. Very busy right now, maybe during the Christmas. @btcontract would you be willing to assist me in making appropriate changes to BLW? In case you don't feel like coding it, just point me at what you consider the best resource for learning enough Scala and whatever libs/frameworks you use to be able to do it myself.

Regarding what's possible today, how exactly would a server know beforehand whether there exists a route to the wallet? It doesn't send forwarding hints in the first request.

@fiatjaf fiatjaf closed this as completed Jan 2, 2021
@Kixunil
Copy link
Contributor Author

Kixunil commented Jan 3, 2021

Why was this closed, please?

@fiatjaf
Copy link
Collaborator

fiatjaf commented Jan 3, 2021

I just thought this had been long forgotten by everybody and was cleaning up some things in the repo.
We can reopen it if you prefer.

@Kixunil
Copy link
Contributor Author

Kixunil commented Jan 4, 2021

It was not forgotten by me but perhaps other developers didn't find the use cases convincing even when backed by actual experience of hundreds of people. As I said, I would be willing to help with it if anyone is interested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants