From f30363ff3929cd11e5974496eacb1a7c6bf46899 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Mon, 13 Jul 2020 14:43:13 +0100 Subject: [PATCH] FIX: groundcontrol notifications for internal invoices --- class/Invo.js | 26 ++++++++++++++++++++++++++ controllers/api.js | 27 ++++++++++++++++++++++----- doc/schema.md | 1 + 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/class/Invo.js b/class/Invo.js index 093c309..8c77044 100644 --- a/class/Invo.js +++ b/class/Invo.js @@ -1,3 +1,4 @@ +var crypto = require('crypto'); var lightningPayReq = require('bolt11'); export class Invo { @@ -65,6 +66,31 @@ export class Invo { return await this._redis.get('ispaid_' + paymentHash); } + async getPreimage() { + if (!this._bolt11) throw new Error('bolt11 is not provided'); + const decoded = lightningPayReq.decode(this._bolt11); + let paymentHash = false; + for (const tag of decoded.tags) { + if (tag.tagName === 'payment_hash') { + paymentHash = tag.data; + } + } + if (!paymentHash) throw new Error('Could not find payment hash in invoice tags'); + return await this._redis.get('preimage_for_' + paymentHash); + } + + async savePreimage(preimageHex) { + const paymentHashHex = require('crypto').createHash('sha256').update(Buffer.from(preimageHex, 'hex')).digest('hex'); + const key = 'preimage_for_' + paymentHashHex; + await this._redis.set(key, preimageHex); + await this._redis.expire(key, 3600 * 24 * 30); // 1 month + } + + makePreimageHex() { + let buffer = crypto.randomBytes(32); + return buffer.toString('hex'); + } + /** * Queries LND ofr all user invoices * diff --git a/controllers/api.js b/controllers/api.js index 2870bd3..5cfba4d 100644 --- a/controllers/api.js +++ b/controllers/api.js @@ -54,8 +54,7 @@ redis.info(function (err, info) { } }); -let subscribeInvoicesCall = lightning.subscribeInvoices({}); -subscribeInvoicesCall.on('data', async function (response) { +const subscribeInvoicesCallCallback = async function (response) { if (response.state === 'SETTLED') { const LightningInvoiceSettledNotification = { memo: response.memo, @@ -94,7 +93,9 @@ subscribeInvoicesCall.on('data', async function (response) { ); console.log('GroundControl:', apiResponse.originalResponse.status); } -}); +}; +let subscribeInvoicesCall = lightning.subscribeInvoices({}); +subscribeInvoicesCall.on('data', subscribeInvoicesCallCallback); subscribeInvoicesCall.on('status', function (status) { // The current status of the stream. }); @@ -151,11 +152,14 @@ router.post('/addinvoice', postLimiter, async function(req, res) { if (!req.body.amt || /*stupid NaN*/ !(req.body.amt > 0)) return errorBadArguments(res); - lightning.addInvoice({ memo: req.body.memo, value: req.body.amt, expiry: 3600 * 24 }, async function(err, info) { + const invoice = new Invo(redis, bitcoinclient, lightning); + const r_preimage = invoice.makePreimageHex(); + lightning.addInvoice({ memo: req.body.memo, value: req.body.amt, expiry: 3600 * 24, r_preimage: Buffer.from(r_preimage, 'hex').toString('base64') }, async function(err, info) { if (err) return errorLnd(res); info.pay_req = info.payment_request; // client backwards compatibility await u.saveUserInvoice(info); + await invoice.savePreimage(r_preimage); res.send(info); }); @@ -237,8 +241,21 @@ router.post('/payinvoice', async function(req, res) { pay_req: req.body.invoice, }); - await UserPayee.setPaymentHashPaid(info.payment_hash); + const invoice = new Invo(redis, bitcoinclient, lightning); + invoice.setInvoice(req.body.invoice); + await invoice.markAsPaidInDatabase(); + // now, faking LND callback about invoice paid: + const preimage = await invoice.getPreimage(); + if (preimage) { + subscribeInvoicesCallCallback({ + state: 'SETTLED', + memo: info.description, + r_preimage: Buffer.from(preimage, 'hex'), + r_hash: Buffer.from(info.payment_hash, 'hex'), + amt_paid_sat: +info.num_satoshis, + }); + } await lock.releaseLock(); return res.send(info); } diff --git a/doc/schema.md b/doc/schema.md index fb58b26..7f9b90e 100644 --- a/doc/schema.md +++ b/doc/schema.md @@ -11,6 +11,7 @@ User storage schema * refresh_token_for_{userid} = {access_token} * importing_{txid} = 1 `atomic lock when processing topup tx` * invoice_paying_for_{userid} = 1 `lock for when payinvoice is in progress` +* preimage_for_{payment_hash_hex} = {preimage_hex} `ttl 1 month`