FIX: groundcontrol notifications for internal invoices

This commit is contained in:
Overtorment 2020-07-13 14:43:13 +01:00
parent 17bdf37f9b
commit f30363ff39
3 changed files with 49 additions and 5 deletions

View File

@ -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
*

View File

@ -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);
}

View File

@ -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`