-
Notifications
You must be signed in to change notification settings - Fork 232
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
Add LNURL payRequest #291
Add LNURL payRequest #291
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ export class User { | |
this._bitcoindrpc = bitcoindrpc; | ||
this._lightning = lightning; | ||
this._userid = false; | ||
this._publicid = false; | ||
this._login = false; | ||
this._password = false; | ||
this._balance = 0; | ||
|
@@ -43,6 +44,18 @@ export class User { | |
return this._refresh_token; | ||
} | ||
|
||
async loadByPublicId(publicId) { | ||
if (!publicId) return false; | ||
let userid = await this._redis.get('userid_for_' + publicId); | ||
|
||
if (userid) { | ||
this._userid = userid; | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
async loadByAuthorization(authorization) { | ||
if (!authorization) return false; | ||
let access_token = authorization.replace('Bearer ', ''); | ||
|
@@ -76,9 +89,14 @@ export class User { | |
|
||
buffer = crypto.randomBytes(24); | ||
let userid = buffer.toString('hex'); | ||
|
||
buffer = crypto.randomBytes(24); | ||
let publicid = buffer.toString('hex'); | ||
|
||
this._login = login; | ||
this._password = password; | ||
this._userid = userid; | ||
this._publicid = publicid; | ||
await this._saveUserToDatabase(); | ||
} | ||
|
||
|
@@ -131,6 +149,21 @@ export class User { | |
if (config.bitcoind) return this._bitcoindrpc.request('importaddress', [address, address, false]); | ||
} | ||
|
||
async getPublicId() { | ||
if (!this._userid) return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if this._publicid exists, it should probably return early. |
||
|
||
// Old user without publicid compatibility | ||
let publicid = await this._redis.get('publicid_for_' + this._userid); | ||
if (!publicid) { | ||
const buffer = crypto.randomBytes(24); | ||
publicid = buffer.toString('hex'); | ||
await this._redis.set('publicid_for_' + this._userid, publicid); | ||
await this._redis.set('userid_for_' + publicid, this._userid); | ||
} | ||
|
||
return publicid; | ||
} | ||
|
||
/** | ||
* LndHub no longer relies on redis balance as source of truth, this is | ||
* more a cache now. See `this.getCalculatedBalance()` to get correct balance. | ||
|
@@ -494,6 +527,8 @@ export class User { | |
async _saveUserToDatabase() { | ||
let key; | ||
await this._redis.set((key = 'user_' + this._login + '_' + this._hash(this._password)), this._userid); | ||
await this._redis.set('userid_for_' + this._publicid, this._userid); | ||
await this._redis.set('publicid_for_' + this._userid, this._publicid); | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -4,6 +4,8 @@ const config = require('../config'); | |||||
let express = require('express'); | ||||||
let router = express.Router(); | ||||||
let logger = require('../utils/logger'); | ||||||
let crypto = require('crypto'); | ||||||
let { bech32 } = require('bech32'); | ||||||
const MIN_BTC_BLOCK = 670000; | ||||||
console.log('using config', JSON.stringify(config)); | ||||||
|
||||||
|
@@ -164,6 +166,17 @@ router.post('/auth', postLimiter, async function (req, res) { | |||||
} | ||||||
}); | ||||||
|
||||||
router.get('/publicid', postLimiter, async function (req, res) { | ||||||
logger.log('/publicid', [req.id]); | ||||||
let u = new User(redis, bitcoinclient, lightning); | ||||||
if (!(await u.loadByAuthorization(req.headers.authorization))) { | ||||||
return errorBadAuth(res); | ||||||
} | ||||||
|
||||||
logger.log('/publicid', [req.id, 'userid: ' + u.getUserId(), 'invoice: ' + req.body.invoice]); | ||||||
res.send(await u.getPublicId()); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't you return json here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, you might want to return the public id in the response on account creation. |
||||||
}); | ||||||
|
||||||
router.post('/addinvoice', postLimiter, async function (req, res) { | ||||||
logger.log('/addinvoice', [req.id]); | ||||||
let u = new User(redis, bitcoinclient, lightning); | ||||||
|
@@ -192,6 +205,71 @@ router.post('/addinvoice', postLimiter, async function (req, res) { | |||||
); | ||||||
}); | ||||||
|
||||||
router.get('/lnurlp/:publicid/qr', postLimiter, async function (req, res) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
With this, Lightning addresses |
||||||
logger.log('/lnurlp/:publicid', [req.id]); | ||||||
let u = new User(redis, bitcoinclient, lightning); | ||||||
const publicid = req.params.publicid; | ||||||
if (!(await u.loadByPublicId(publicid))) { | ||||||
return errorBadArguments(res); | ||||||
} | ||||||
|
||||||
let host = req.headers.host; | ||||||
const url = req.protocol + '://' + host + '/lnurlp/' + publicid; | ||||||
const words = bech32.toWords(Buffer.from(url, 'utf8')); | ||||||
const lnurlp = bech32.encode('lnurl', words, 1023); | ||||||
res.send({ lnurlp }); | ||||||
}); | ||||||
|
||||||
router.get('/lnurlp/:publicid', postLimiter, async function (req, res) { | ||||||
logger.log('/lnurlp/:publicid', [req.id]); | ||||||
let u = new User(redis, bitcoinclient, lightning); | ||||||
const publicid = req.params.publicid; | ||||||
if (!(await u.loadByPublicId(publicid))) { | ||||||
return errorBadArguments(res); | ||||||
} | ||||||
|
||||||
const metadata = JSON.stringify([['text/plain', 'Static QR code provided by BlueWallet.io']]); | ||||||
|
||||||
if (!req.query.amount) { | ||||||
let host = req.headers.host; | ||||||
return res.send({ | ||||||
callback: req.protocol + '://' + host + '/lnurlp/' + publicid, | ||||||
maxSendable: 1000000000, | ||||||
minSendable: 1000, | ||||||
metadata, | ||||||
tag: 'payRequest', | ||||||
}); | ||||||
} | ||||||
|
||||||
const satAmount = req.query.amount / 1000; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: Currently the largest channel is 10 BTC, so in millisatoshis, that would fit within Number.MAX_SAFE_INTEGER, but keep in mind that once you pass 90071.99 BTC this will break in weird ways silently. I am sure other parts of LNDHub assume all integers will be fine too... so this is probably not a problem worth fixing right now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, this should enforce the minSendable / maxSendable you return above. Return an error if they try to request an amount outside the parameters. |
||||||
|
||||||
const invoice = new Invo(redis, bitcoinclient, lightning); | ||||||
const r_preimage = invoice.makePreimageHex(); | ||||||
lightning.addInvoice( | ||||||
{ | ||||||
memo: '', | ||||||
value: satAmount, | ||||||
expiry: 3600 * 24, | ||||||
r_preimage: Buffer.from(r_preimage, 'hex').toString('base64'), | ||||||
description_hash: crypto.createHash('sha256').update(metadata).digest(), | ||||||
}, | ||||||
async function (err, info) { | ||||||
if (err) return errorLnd(res); | ||||||
|
||||||
await u.saveUserInvoice(info); | ||||||
await invoice.savePreimage(r_preimage); | ||||||
|
||||||
res.send({ | ||||||
status: 'OK', | ||||||
successAction: { tag: 'message', message: 'Payment received!' }, | ||||||
routes: [], | ||||||
pr: info.payment_request, | ||||||
disposable: false, | ||||||
}); | ||||||
}, | ||||||
); | ||||||
}); | ||||||
|
||||||
router.post('/payinvoice', async function (req, res) { | ||||||
let u = new User(redis, bitcoinclient, lightning); | ||||||
if (!(await u.loadByAuthorization(req.headers.authorization))) { | ||||||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't you also set this._publicid?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, all the loadBy* methods should also fetch _publicid and store it in the _publicid.