diff options
| -rw-r--r-- | db/models/invoices.js | 11 | ||||
| -rw-r--r-- | routes/bitcoin/receive.js | 259 | ||||
| -rw-r--r-- | routes/invoices.js | 2 |
3 files changed, 141 insertions, 131 deletions
diff --git a/db/models/invoices.js b/db/models/invoices.js index b13a9545..82e64427 100644 --- a/db/models/invoices.js +++ b/db/models/invoices.js @@ -129,7 +129,7 @@ const attributes = { field: "address" }, received: { - type: DataTypes.STRING(255), + type: DataTypes.DOUBLE, allowNull: false, defaultValue: 0, primaryKey: false, @@ -146,6 +146,15 @@ const attributes = { comment: null, field: "amount" }, + pending: { + type: DataTypes.DOUBLE, + allowNull: false, + defaultValue: 0, + primaryKey: false, + autoIncrement: false, + comment: null, + field: "pending" + }, webhook: { type: DataTypes.TEXT, allowNull: true, diff --git a/routes/bitcoin/receive.js b/routes/bitcoin/receive.js index 714aa3b2..4ace1c2f 100644 --- a/routes/bitcoin/receive.js +++ b/routes/bitcoin/receive.js @@ -5,13 +5,13 @@ import { emit } from "$lib/sockets"; import store from "$lib/store"; import { notify } from "$lib/notifications"; import { callWebhook } from "$lib/webhooks"; -import reverse from 'buffer-reverse'; -import zmq from 'zeromq/v5-compat'; -import { Op } from '@sequelize/core'; -import { fromBase58 } from 'bip32'; -import bitcoin from 'bitcoinjs-lib'; +import reverse from "buffer-reverse"; +import zmq from "zeromq/v5-compat"; +import { Op } from "@sequelize/core"; +import { fromBase58 } from "bip32"; +import bitcoin from "bitcoinjs-lib"; import { sendLiquid } from "$routes/liquid/send"; -import { computeConversionFee } from './conversionFee'; +import { computeConversionFee } from "./conversionFee"; import { l, warn, err } from "$lib/logging"; const zmqRawBlock = zmq.socket("sub"); @@ -34,149 +34,149 @@ console.warn = () => {}; zmqRawTx.on("message", async (topic, message, sequence) => { try { - const hex = message.toString("hex"); - let tx = bitcoin.Transaction.fromHex(message); + const hex = message.toString("hex"); + let tx = bitcoin.Transaction.fromHex(message); - let hash = reverse(tx.getHash()).toString("hex"); - - if (seen.includes(hash)) return; - seen.push(hash); - if (seen.length > 5000) seen.shift(); + let hash = reverse(tx.getHash()).toString("hex"); - Promise.all( - tx.outs.map(async o => { - try { - const { value } = o; + if (seen.includes(hash)) return; + seen.push(hash); + if (seen.length > 5000) seen.shift(); - let address; + Promise.all( + tx.outs.map(async o => { try { - address = bitcoin.address.fromOutputScript(o.script, network); - } catch (e) { - return; - } - - if ( - Object.keys(store.addresses).includes(address) && - !store.change.includes(address) - ) { - await db.transaction(async transaction => { - let user = await db.User.findOne({ - where: { - username: store.addresses[address] - }, - transaction - }); + const { value } = o; - let invoice = await db.Invoice.findOne({ - where: { - address, - user_id: user.id, - network: "bitcoin" - }, - order: [["id", "DESC"]], - include: { - model: db.Account, - as: "account" - }, - transaction - }); - - if (!invoice) return; - - const currency = invoice ? invoice.currency : user.currency; - const rate = invoice - ? invoice.rate - : store.rates[user.currency]; - const tip = invoice ? invoice.tip : 0; - const memo = invoice ? invoice.memo : ""; - - let confirmed = false; + let address; + try { + address = bitcoin.address.fromOutputScript(o.script, network); + } catch (e) { + return; + } - let { account } = invoice; + if ( + Object.keys(store.addresses).includes(address) && + !store.change.includes(address) + ) { + await db.transaction(async transaction => { + let user = await db.User.findOne({ + where: { + username: store.addresses[address] + }, + transaction + }); - if (account.asset !== config.liquid.btcasset) { - account = await db.Account.findOne({ + let invoice = await db.Invoice.findOne({ where: { + address, user_id: user.id, - asset: config.liquid.btcasset, - pubkey: null + network: "bitcoin" + }, + order: [["id", "DESC"]], + include: { + model: db.Account, + as: "account" }, transaction }); - } - await account.increment({ pending: value }, { transaction }); - await account.save({ transaction }); + if (!invoice) return; - if (config.bitcoin.walletpass) - await bc.walletPassphrase(config.bitcoin.walletpass, 300); + const currency = invoice ? invoice.currency : user.currency; + const rate = invoice ? invoice.rate : store.rates[user.currency]; + const tip = invoice ? invoice.tip : 0; + const memo = invoice ? invoice.memo : ""; - 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); - let inputTx = bitcoin.Transaction.fromHex(hex); - totalInputs += inputTx.outs[index].value; - } - let fee = totalInputs - totalOutputs; + let confirmed = false; - let payment = await db.Payment.create( - { - account_id: account.id, - user_id: user.id, - hash, - fee, - memo, - amount: value - tip, - currency, - rate, - received: true, - tip, - confirmed, - address, - network: "bitcoin", - invoice_id: invoice.id - }, - { transaction } - ); + let { account } = invoice; - payment = payment.get({ plain: true }); - payment.account = account.get({ plain: true }); - payment.invoice = invoice.get({ plain: true }); + if (account.asset !== config.liquid.btcasset) { + account = await db.Account.findOne({ + where: { + user_id: user.id, + asset: config.liquid.btcasset, + pubkey: null + }, + transaction + }); + } - emit(user.username, "account", account); - emit(user.username, "payment", payment); - l("bitcoin detected", user.username, value); - notify(user, `${value} SAT payment detected`); - callWebhook(invoice, payment); - }); + await invoice.increment({ pending: value }, { transaction }); + await invoice.save({ transaction }); + await account.increment({ pending: value }, { transaction }); + await account.save({ transaction }); + + if (config.bitcoin.walletpass) + await bc.walletPassphrase(config.bitcoin.walletpass, 300); + + 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); + 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, + memo, + amount: value - tip, + currency, + rate, + received: true, + tip, + confirmed, + address, + network: "bitcoin", + invoice_id: invoice.id + }, + { transaction } + ); + + payment = payment.get({ plain: true }); + payment.account = account.get({ plain: true }); + payment.invoice = invoice.get({ plain: true }); + + emit(user.username, "account", account); + emit(user.username, "payment", payment); + l("bitcoin detected", user.username, value); + notify(user, `${value} SAT payment detected`); + callWebhook(invoice, payment); + }); + } + } catch (e) { + console.log("problem parsing bitcoin tx", e); } - } catch (e) { - console.log("problem parsing bitcoin tx", e); - } - }) - ); - } catch(e) { + }) + ); + } catch (e) { console.log("problem receiving bitcoin tx", e); - } + } }); zmqRawBlock.on("message", async (topic, message, sequence) => { try { - const payments = await db.Payment.findAll({ - where: { confirmed: false } - }); + const payments = await db.Payment.findAll({ + where: { confirmed: false } + }); - let block = bitcoin.Block.fromHex(message.toString("hex")); - block.transactions.map(tx => { - let hash = reverse(tx.getHash()).toString("hex"); - if (payments.find(p => p.hash === hash)) queue[hash] = 1; - }); - } catch(e) { - console.log(e) - } + let block = bitcoin.Block.fromHex(message.toString("hex")); + block.transactions.map(tx => { + let hash = reverse(tx.getHash()).toString("hex"); + if (payments.find(p => p.hash === hash)) queue[hash] = 1; + }); + } catch (e) { + console.log(e); + } }); setInterval(async () => { @@ -220,6 +220,11 @@ setInterval(async () => { await account.save({ transaction }); await invoice.increment({ received: total }, { transaction }); + await invoice.decrement( + { pending: Math.min(invoice.pending, total) }, + { transaction } + ); + await account.increment({ balance: total }, { transaction }); await account.decrement( { pending: Math.min(account.pending, total) }, @@ -270,10 +275,6 @@ setInterval(async () => { }); } } catch (e) { - err( - "problem processing queued bitcoin transaction", - e.message, - e.stack - ); + err("problem processing queued bitcoin transaction", e.message, e.stack); } }, 1000); diff --git a/routes/invoices.js b/routes/invoices.js index ebe4e6d5..198189b4 100644 --- a/routes/invoices.js +++ b/routes/invoices.js @@ -19,7 +19,7 @@ app.get("/invoice", async (req, res, next) => { include: { model: db.User, as: "user", - attributes: ["username", "currency"] + attributes: ["username", "currency", "profile"] } }); |
