diff options
| -rw-r--r-- | db/models/payments.js | 9 | ||||
| -rw-r--r-- | lib/utils.js | 48 | ||||
| -rw-r--r-- | routes/bitcoin/receive.js | 12 | ||||
| -rw-r--r-- | routes/bitcoin/send.js | 4 | ||||
| -rw-r--r-- | routes/lightning/receive.js | 3 | ||||
| -rw-r--r-- | routes/lightning/send.js | 127 |
6 files changed, 119 insertions, 84 deletions
diff --git a/db/models/payments.js b/db/models/payments.js index 8c464293..076a644f 100644 --- a/db/models/payments.js +++ b/db/models/payments.js @@ -64,6 +64,15 @@ const attributes = { comment: null, field: "rate" }, + preimage: { + type: DataTypes.STRING(255), + allowNull: true, + defaultValue: null, + primaryKey: false, + autoIncrement: false, + comment: null, + field: "preimage" + }, network: { type: DataTypes.STRING(255), allowNull: true, diff --git a/lib/utils.js b/lib/utils.js index a861f8b7..cd1b3d69 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,8 +1,8 @@ const sequelize = require("sequelize"); const { Op } = sequelize; -getUser = async username => { - let user = await db.User.findOne({ +getUser = async (username, transaction) => { + let params = { include: [ { model: db.Account, @@ -14,11 +14,18 @@ getUser = async username => { } ], where: { username } - }); + }; + + if (transaction) { + params.lock = transaction.LOCK.UPDATE; + params.transaction = transaction; + } + + let user = await db.User.findOne(params); user = user.get({ plain: true }); - user.payments = await db.Payment.findAll({ + params = { order: [["id", "DESC"]], where: { user_id: user.id, @@ -35,13 +42,20 @@ getUser = async username => { model: db.Account, as: "account" } - }); + }; + + if (transaction) { + params.lock = transaction.LOCK.UPDATE; + params.transaction = transaction; + } + + user.payments = await db.Payment.findAll(params); return user; }; -getUserById = async id => { - let user = await db.User.findOne({ +getUserById = async (id, transaction) => { + let params = { include: [ { model: db.Account, @@ -53,11 +67,18 @@ getUserById = async id => { } ], where: { id } - }); + }; + + if (transaction) { + params.lock = transaction.LOCK.UPDATE; + params.transaction = transaction; + } + + let user = await db.User.findOne(params); user = user.get({ plain: true }); - user.payments = await db.Payment.findAll({ + params = { order: [["id", "DESC"]], where: { user_id: user.id, @@ -74,7 +95,14 @@ getUserById = async id => { model: db.Account, as: "account" } - }); + }; + + if (transaction) { + params.lock = transaction.LOCK.UPDATE; + params.transaction = transaction; + } + + user.payments = await db.Payment.findAll(params); return user; }; diff --git a/routes/bitcoin/receive.js b/routes/bitcoin/receive.js index deded625..1aae1467 100644 --- a/routes/bitcoin/receive.js +++ b/routes/bitcoin/receive.js @@ -73,10 +73,22 @@ zmqRawTx.on("message", async (topic, message, sequence) => { addresses[user.address] = user.username; + let totalOutputs = tx.outs.reduce((a, b) => a + b.value, 0); + let totalInputs = 0; + for (let i = 0; i < tx.ins.length; i++) { + let { hash, index } = tx.ins[i]; + hash = reverse(hash).toString("hex"); + let hex = await bc.getRawTransaction(hash.toString('hex')); + let inputTx = bitcoin.Transaction.fromHex(hex); + totalInputs += inputTx.outs[index].value; + } + let fee = totalInputs - totalOutputs; + let payment = await db.Payment.create({ account_id: account.id, user_id: user.id, hash, + fee, amount: value - tip, currency, rate, diff --git a/routes/bitcoin/send.js b/routes/bitcoin/send.js index 42027f71..d9324028 100644 --- a/routes/bitcoin/send.js +++ b/routes/bitcoin/send.js @@ -31,7 +31,7 @@ module.exports = async (req, res) => { total = total - change + fee; let amount = total - fee; - l.info("attempting bitcoin payment", user.username, total, change, amount, fee); + l.info("attempting bitcoin payment", user.username, { total, change, amount, fee }); let account, params; try { @@ -46,7 +46,7 @@ module.exports = async (req, res) => { }); if (total > account.balance) { - l.error("amount exceeds balance", amount, fee, balance); + l.error("amount exceeds balance", amount, fee, account.balance); throw new Error("insufficient funds"); } diff --git a/routes/lightning/receive.js b/routes/lightning/receive.js index 403eb999..980d9e01 100644 --- a/routes/lightning/receive.js +++ b/routes/lightning/receive.js @@ -27,6 +27,7 @@ const handlePayment = async msg => { hash, amount, currency, + preimage: msg.r_preimage.toString('hex'), rate, received: true, confirmed: true, @@ -53,7 +54,7 @@ const handlePayment = async msg => { "lightning payment received", user.username, payment.amount, - payment.tip + payment.tip, ); }; diff --git a/routes/lightning/send.js b/routes/lightning/send.js index 22c7627a..d273471a 100644 --- a/routes/lightning/send.js +++ b/routes/lightning/send.js @@ -15,93 +15,78 @@ module.exports = async (req, res) => { try { await db.transaction(async transaction => { - let { balance } = await db.User.findOne( - { - where: { - username: user.username - } + let account = await db.Account.findOne({ + where: { + user_id: user.id, + asset: config.liquid.btcasset }, - { transaction } - ); + lock: transaction.LOCK.UPDATE, + transaction + }); - if (balance < payreq.satoshis) { - throw new Error("insufficient funds"); + if (account.balance < payreq.satoshis) { + throw new Error("Insufficient funds"); } - user.balance -= payreq.satoshis; - await user.save({ transaction }); - }); - } catch (e) { - l.warn( - "insufficient funds for lightning payment", - user.username, - user.balance - ); - return res.status(500).send("Not enough satoshis"); - } + let m = await lna.sendPaymentSync({ + payment_request: hash + }); - let m; - try { - let paymentHash = payreq.tags.find(t => t.tagName === "payment_hash").data; - m = await lna.sendPaymentSync({ - payment_request: hash - }); + if (m.payment_error) return res.status(500).send(m.payment_error); - if (m.payment_error) return res.status(500).send(m.payment_error); + if (seen.includes(m.payment_preimage)) { + l.warn("duplicate payment detected", m.payment_preimage); + throw new Error("Duplicate payment detected"); + } - if (seen.includes(m.payment_preimage)) { - l.warn("duplicate payment detected", m.payment_preimage); - return res.status(500).send("duplicate payment detected"); - } + let total = parseInt(m.payment_route.total_amt); + let fee = m.payment_route.total_fees; - let total = parseInt(m.payment_route.total_amt); - let fee = m.payment_route.total_fees; + account.balance -= total; + await account.save({ transaction }); - const account = await db.Account.findOne({ - where: { + let payment = await db.Payment.create({ + amount: -total, + account_id: account.id, user_id: user.id, - asset: config.liquid.btcasset - } - }); - - account.balance -= total - payreq.satoshis; - await account.save() - - let payment = await db.Payment.create({ - amount: -total, - account_id: account.id, - user_id: user.id, - hash, - rate: app.get("rates")[user.currency], - currency: user.currency, - confirmed: true, - network: "LNBTC" - }); + hash, + preimage: m.payment_preimage.toString("hex"), + rate: app.get("rates")[user.currency], + currency: user.currency, + confirmed: true, + network: "LNBTC" + }, { transaction }); - payment = payment.get({ plain: true }); - payment.account = account.get({ plain: true }); + payment = payment.get({ plain: true }); + payment.account = account.get({ plain: true }); - seen.push(m.payment_preimage); - l.info("sent lightning", user.username, -payment.amount); + seen.push(m.payment_preimage); + l.info("sent lightning", user.username, -payment.amount); - user = await getUser(user.username); - emit(user.username, "user", user); + user = await getUser(user.username, transaction); + emit(user.username, "user", user); - if (payreq.payeeNodeKey === config.lnb.id) { - lna.addInvoice({ value: payreq.satoshis }, (err, invoice) => { - let payback = lnb.sendPayment(lnb.meta, {}); + if (payreq.payeeNodeKey === config.lnb.id) { + lna.addInvoice({ value: payreq.satoshis }, (err, invoice) => { + let payback = lnb.sendPayment(lnb.meta, {}); - /* eslint-disable-next-line */ - let { payment_request } = invoice; - /* eslint-disable-next-line */ - payback.write({ payment_request }); - }); - } + /* eslint-disable-next-line */ + let { payment_request } = invoice; + /* eslint-disable-next-line */ + payback.write({ payment_request }); + }); + } - seen.push(hash); - res.send(payment); + seen.push(hash); + res.send(payment); + }); } catch (e) { - l.info("error sending lightning", e.message); - return res.status(500).send(e); + l.error( + "problem sending lightning payment", + user.username, + user.balance, + e.message + ); + return res.status(500).send(e.message); } }; |
