From 8f2d98774b8ac4ddc9751a8e6fbda212b7bd8a8f Mon Sep 17 00:00:00 2001 From: Overtorment Date: Sun, 3 Mar 2019 15:59:30 +0000 Subject: [PATCH] FIX: properly locked payments --- class/Paym.js | 9 +++++-- class/User.js | 60 ++++++++++++++++++++++++++++++++++++++++++++++ controllers/api.js | 2 ++ doc/schema.md | 2 ++ 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/class/Paym.js b/class/Paym.js index d043bfb..f63f40b 100644 --- a/class/Paym.js +++ b/class/Paym.js @@ -2,14 +2,15 @@ var crypto = require('crypto'); var lightningPayReq = require('bolt11'); import { BigNumber } from 'bignumber.js'; -export class Paym { +export class Payment { constructor(redis, bitcoindrpc, lightning) { this._redis = redis; this._bitcoindrpc = bitcoindrpc; this._lightning = lightning; + this._decoded = false; } - async decodePayReq(invoice) { + async decodePayReqViaRpc(invoice) { return new Promise(function(resolve, reject) { this._lightning.decodePayReq({ pay_req: invoice }, function(err, info) { if (err) return reject(err); @@ -17,4 +18,8 @@ export class Paym { }); }); } + + decodePayReq(payReq) { + this._decoded = lightningPayReq.decode(payReq); + } } diff --git a/class/User.js b/class/User.js index 7eb1c3a..a2b62e0 100644 --- a/class/User.js +++ b/class/User.js @@ -153,6 +153,13 @@ export class User { calculatedBalance -= +tx.value; } } + + let lockedPayments = await this.getLockedPayments(); + for (let paym of lockedPayments) { + // TODO: check if payment in determined state and actually evict it from this list + calculatedBalance -= +paym.amount; + } + return calculatedBalance; } @@ -386,6 +393,59 @@ export class User { } } + /** + * Adds invoice to a list of user's locked payments. + * Used to calculate balance till the lock is lifted (payment is in + * determined state - succeded or failed). + * + * @param {String} pay_req + * @param {Object} decodedInvoice + * @returns {Promise} + */ + async lockFunds(pay_req, decodedInvoice) { + let doc = { + pay_req, + amount: +decodedInvoice.num_satoshis, + timestamp: Math.floor(+new Date() / 1000), + }; + + return this._redis.rpush('locked_payments_for_' + this._userid, JSON.stringify(doc)); + } + + /** + * Strips specific payreq from the list of locked payments + * @param pay_req + * @returns {Promise} + */ + async unlockFunds(pay_req) { + let payments = await this.getLockedPayments(); + let saveBack = []; + for (let paym of payments) { + if (paym.pay_req !== pay_req) { + saveBack.push(paym); + } + } + + await this._redis.del('locked_payments_for_' + this._userid); + for (let doc of saveBack) { + await this._redis.rpush('locked_payments_for_' + this._userid, JSON.stringify(doc)); + } + } + + async getLockedPayments() { + let payments = await this._redis.lrange('locked_payments_for_' + this._userid, 0, -1); + let result = []; + for (let paym of payments) { + let json; + try { + json = JSON.parse(paym); + result.push(json); + } catch (_) {} + } + + return result; + } + _hash(string) { return crypto .createHash('sha256') diff --git a/controllers/api.js b/controllers/api.js index 4f0a4ff..82a968e 100644 --- a/controllers/api.js +++ b/controllers/api.js @@ -185,6 +185,7 @@ router.post('/payinvoice', async function(req, res) { var call = lightning.sendPayment(); call.on('data', async function(payment) { // payment callback + await u.unlockFunds(req.body.invoice); if (payment && payment.payment_route && payment.payment_route.total_amt_msat) { userBalance -= +payment.payment_route.total_fees + +payment.payment_route.total_amt; u.saveBalance(userBalance); @@ -206,6 +207,7 @@ router.post('/payinvoice', async function(req, res) { } let inv = { payment_request: req.body.invoice, amt: info.num_satoshis }; // amt is used only for 'tip' invoices try { + await u.lockFunds(req.body.invoice, info); call.write(inv); } catch (Err) { await lock.releaseLock(); diff --git a/doc/schema.md b/doc/schema.md index ec5fb10..79bb075 100644 --- a/doc/schema.md +++ b/doc/schema.md @@ -20,6 +20,8 @@ User storage schema * bitcoin_address_for_{userid} = {address} * balance_for_{userid} = {int} * txs_for_{userid} = [] `serialized paid lnd invoices in a list` +* locked_invoices_for_{userod} = [] `serialized attempts to pay invoice. used in calculating user's balance` + : {pay_req:..., amount:666, timestamp:666} * imported_txids_for_{userid} = [] `list of txids processed for this user` * metadata_for_{userid}= {serialized json} * userinvoices_for_{userid} = []