diff options
| author | Adam Soltys <[email protected]> | 2021-08-09 20:55:17 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2021-08-09 20:55:17 -0700 |
| commit | 56559c7b9749fe62fe2fe0ba90725391d83b8bf7 (patch) | |
| tree | 48d6a0630d05d6787b4410708a5efceff472636b | |
| parent | 803ecab2b5c3a00d107372ea6757cd6ce0f8224d (diff) | |
| parent | 8d6f65079868ae6134f593752e3380180591623d (diff) | |
| download | coinos-server-56559c7b9749fe62fe2fe0ba90725391d83b8bf7.tar.gz coinos-server-56559c7b9749fe62fe2fe0ba90725391d83b8bf7.zip | |
Merge branch 'v1.0.0'
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | Dockerfile | 7 | ||||
| -rw-r--r-- | doc/fund-server-bitcoin-cli.md | 10 | ||||
| -rw-r--r-- | lib/mail.js | 32 | ||||
| -rw-r--r-- | lib/persist.js | 2 | ||||
| -rw-r--r-- | lib/send.js | 2 | ||||
| -rw-r--r-- | lib/sockets.js | 2 | ||||
| -rw-r--r-- | package.json | 2 | ||||
| -rw-r--r-- | routes/admin.js | 224 | ||||
| -rw-r--r-- | routes/bitcoin/receive.js | 48 | ||||
| -rw-r--r-- | routes/bitcoin/send.js | 55 | ||||
| -rw-r--r-- | routes/invoices.js | 4 | ||||
| -rw-r--r-- | routes/lightning/receive.js | 53 | ||||
| -rw-r--r-- | routes/liquid/fee.js | 9 | ||||
| -rw-r--r-- | routes/liquid/receive.js | 71 | ||||
| -rw-r--r-- | routes/liquid/send.js | 34 | ||||
| -rw-r--r-- | routes/lnurl.js | 10 | ||||
| -rw-r--r-- | routes/payments.js | 1 | ||||
| -rw-r--r-- | routes/referrals.js | 7 | ||||
| -rw-r--r-- | routes/send.js | 2 | ||||
| -rw-r--r-- | routes/users.js | 25 | ||||
| -rw-r--r-- | scripts/apiParse.js | 14 | ||||
| -rwxr-xr-x | watcher.sh | 20 |
23 files changed, 280 insertions, 357 deletions
@@ -15,3 +15,6 @@ lnurl-server.sqlite3 watcher.log config mysql +build.json +build.sh +watcher.sh @@ -1,16 +1,13 @@ -FROM node:slim +FROM node:alpine ARG NODE_ENV=production ENV NODE_ENV $NODE_ENV -RUN apt-get update -RUN apt-get install git make gcc g++ python libzmq3-dev -y +RUN apk add git COPY . /app WORKDIR /app RUN yarn install -ENV SHELL /bin/bash - CMD ["yarn", "start"] diff --git a/doc/fund-server-bitcoin-cli.md b/doc/fund-server-bitcoin-cli.md index 3c5b6ef5..5b096f0f 100644 --- a/doc/fund-server-bitcoin-cli.md +++ b/doc/fund-server-bitcoin-cli.md @@ -14,11 +14,19 @@ docker exec -it bitcoin bash bitcoin-cli -regtest -datadir=/config -rpcwallet=coinosdev getnewaddress # mine a block to the new address -bitcoin-cli -regtest -datadir=/config generatetoaddress 101 theaddressgneratedabove +bitcoin-cli -regtest -datadir=/config generatetoaddress 101 theaddressgeneratedabove # verify you got the reward: bitcoin-cli -regtest -datadir=/config -conf=./bitcoin.conf -rpcwallet=coinosdev getbalance > 50.00000000 + +# send 10 bitcoin to your account on the server +# get the address to send to from ./receive, select 'Bitcoin' +bitcoin-cli -regtest -datadir=/config -rpcwallet=coinosdev sendtoaddress [account-address] 10 + +# confirm the transaction +# (do the mining command again) +bitcoin-cli -regtest -datadir=/config generatetoaddress 101 theaddressgeneratedabove ```  diff --git a/lib/mail.js b/lib/mail.js index 705880d2..ca2c2b81 100644 --- a/lib/mail.js +++ b/lib/mail.js @@ -10,6 +10,38 @@ function openInbox(cb) { imap.openBox("INBOX", true, cb); } +app.post( + "/contact-sil", + ah((req, res) => { + const mailgun = require("mailgun-js")(config.mailgun); + let data = { + subject: "Contact form", + text: JSON.stringify(req.body), + from: "[email protected]", + to: "[email protected]" + }; + + mailgun.messages().send(data); + res.send(req.body); + }) +); + +app.post( + "/contact", + ah((req, res) => { + const mailgun = require("mailgun-js")(config.mailgun); + let data = { + subject: "Contact form", + text: JSON.stringify(req.body), + from: "[email protected]", + to: "[email protected]" + }; + + mailgun.messages().send(data); + res.send(req.body); + }) +); + imap.once("ready", function() { openInbox(function(err, box) { if (err) throw err; diff --git a/lib/persist.js b/lib/persist.js index 54e7e209..a2febf68 100644 --- a/lib/persist.js +++ b/lib/persist.js @@ -7,8 +7,6 @@ module.exports = (path, initial = {}) => { } catch(e) {} } - // console.log("initial", path, initial); - return new Proxy(initial, { set(obj, prop, value) { fs.writeFileSync( diff --git a/lib/send.js b/lib/send.js index c54985d3..92b2eda3 100644 --- a/lib/send.js +++ b/lib/send.js @@ -6,7 +6,7 @@ send = async (amount, memo, hash, user) => { let routingInfo = payreq.tags.find(t => t.tagName === "routing_info"); if ( routingInfo && - routingInfo.data.find(d => d.fee_proportional_millionths > 100) + routingInfo.data.find(d => d.fee_proportional_millionths > 1000) ) throw new Error("Fee rate too high"); diff --git a/lib/sockets.js b/lib/sockets.js index 931a9d23..bd59ee38 100644 --- a/lib/sockets.js +++ b/lib/sockets.js @@ -96,7 +96,7 @@ wss.on("connection", function(ws, req) { lastSeen[token] = new Date(); try { - if (!token || token === "null") return ws.close(); + if (!token || token === "null" || !jwt.decode(token)) return ws.close(); const username = jwt.decode(token).username; if (!username) throw new Error("Invalid JWT token"); diff --git a/package.json b/package.json index 22833f29..f4db1af1 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "passport-local": "1.0.0", "pino": "^5.17.0", "pino-pretty": "^4.8.0", - "pm2": "^4.5.6", + "pm2": "^5.1.0", "query-string": "^6.13.1", "sequelize": "^5.21.3", "sqlite3": "^5.0.2", diff --git a/routes/admin.js b/routes/admin.js index c401c724..c7b7578d 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -35,20 +35,12 @@ router.get( .select('username', 'email', 'phone', 'verified', knex.raw('LEFT(createdAt,10) as created_at')) .from('users') - if (starts_with) { - search = search + '%' - } else if (contains) { - search = '%' + search + '%' - } + var timeCondition = parseInput.addTimeSearch(req.query, 'users.createdAt') + var userCondition = parseInput.addUserSearch(req.query) - if (search) { - users = users - .where(function() { - this.where('username', 'like', search) - this.orWhere('email', 'like', search) - }) - } - debug('search ? ' + search) + if (timeCondition) users = users.whereRaw(timeCondition) + if (userCondition) users = users.whereRaw(userCondition) + // Alternative Sequelize query syntax // await db.User.findAll({ // attributes: ['username', 'email', 'phone', 'verified', 'createdAt'] @@ -101,6 +93,11 @@ router.get( // ] // }) + var timeCondition = parseInput.addTimeSearch(req.query, 'referrals.updatedAt') + var userCondition = parseInput.addUserSearch(req.query, 'sponsor') + + if (timeCondition) referrals = referrals.whereRaw(timeCondition) + if (userCondition) referrals = referrals.whereRaw(userCondition) debug('Referrals: ' + JSON.stringify(referrals)) return res.send({referrals: referrals}) @@ -141,20 +138,12 @@ router.get( .leftJoin('users', 'users.email', 'waiting_list.email') .where('waiting_list.id', '>', 0) - if (starts_with) { - search = search + '%' - } else if (contains) { - search = '%' + search + '%' - } + var timeCondition = parseInput.addTimeSearch(req.query, 'waiting_list.updated_at') + var userCondition = parseInput.addUserSearch(req.query) + + if (timeCondition) queue = queue.whereRaw(timeCondition) + if (userCondition) queue = queue.whereRaw(userCondition) - if (search) { - queue = queue - .where(function() { - this.where('waiting_list.email', 'like', search) - this.orWhere('users.username', 'like', search) - }) - } - debug('search ? ' + search) const found = await queue debug('Waiting list: ' + JSON.stringify(found)) @@ -195,19 +184,12 @@ router.get( .where('balance', '>', 0) } - if (starts_with) { - search = search + '%' - } else if (contains) { - search = '%' + search + '%' - } + var timeCondition = parseInput.addTimeSearch(req.query, 'accounts.updatedAt') + var userCondition = parseInput.addUserSearch(req.query) + + if (timeCondition) accounts = accounts.whereRaw(timeCondition) + if (userCondition) accounts = accounts.whereRaw(userCondition) - if (search) { - accounts = accounts - .where(function() { - this.where('users.email', 'like', search) - this.orWhere('users.username', 'like', search) - }) - } debug('search ? ' + search) const found = await accounts @@ -270,21 +252,11 @@ router.get( debug(types[i] + ' since ' + since) } } + if (since) transactions = transactions.havingRaw('invoices + payments + deposits + orders > ?', [0]) - if (starts_with) { - search = search + '%' - } else if (contains) { - search = '%' + search + '%' - } - - if (search) { - transactions = transactions - .where(function() { - this.where('users.email', 'like', search) - this.orWhere('users.username', 'like', search) - }) - } + var userCondition = parseInput.addUserSearch(req.query) + if (userCondition) transactions = transactions.whereRaw(userCondition) transactions = transactions .groupBy('users.id') @@ -338,27 +310,16 @@ router.get( .leftJoin('users as u1', 'a1.user_id', 'u1.id') .leftJoin('users as u2', 'a2.user_id', 'u2.id') - if (since) { - orders = orders - .where('orders.updatedAt', '>=', since) - } + var timeCondition = parseInput.addTimeSearch(req.query, 'orders.updatedAt') + if (timeCondition) orders = orders.whereRaw(timeCondition) - if (starts_with) { - search = search + '%' - } else if (contains) { - search = '%' + search + '%' - } - - if (search) { - orders = orders - .where( function () { - this.where('u1.email', 'like', search) - this.orWhere('u1.username', 'like', search) - this.orWhere('u2.username', 'like', search) - this.orWhere('u2.email', 'like', search) - }) - } - debug('orders since ' + since) + var userCondition1 = parseInput.addUserSearch(req.query, 'u1') + var userCondition2 = parseInput.addUserSearch(req.query, 'u2') + + if (userCondition1) orders = orders.where( function () { + this.whereRaw(userCondition1) + this.orWhere(knex.raw(userCondition2)) + }) var found = await orders debug('orders: ' + JSON.stringify(found)) @@ -403,26 +364,11 @@ router.get( .leftJoin('accounts', 'invoices.account_id', 'accounts.id') .leftJoin('users', 'accounts.user_id', 'users.id') - if (since) { - invoices = invoices - .where('invoices.updatedAt', '>=', since) - } - - debug('invoices since ' + since) - - if (starts_with) { - search = search + '%' - } else if (contains) { - search = '%' + search + '%' - } + var timeCondition = parseInput.addTimeSearch(req.query, 'invoices.updatedAt') + var userCondition = parseInput.addUserSearch(req.query) - if (search) { - invoices = invoices - .where(function() { - this.where('users.email', 'like', search) - this.orWhere('users.username', 'like', search) - }) - } + if (timeCondition) invoices = invoices.whereRaw(timeCondition) + if (userCondition) invoices = invoices.whereRaw(userCondition) var found = await invoices debug('invoices: ' + JSON.stringify(found)) @@ -468,26 +414,11 @@ router.get( debug('dtype: ' + deposits.constructor + ' : ' + typeof(deposits)) - if (since) { - deposits = deposits - .where('deposits.updatedAt', '>=', since) - } - - debug('deposits since ' + since) - - if (starts_with) { - search = search + '%' - } else if (contains) { - search = '%' + search + '%' - } + var timeCondition = parseInput.addTimeSearch(req.query, 'deposits.updatedAt') + var userCondition = parseInput.addUserSearch(req.query) - if (search) { - deposits = deposits - .where(function() { - this.where('users.email', 'like', search) - this.orWhere('users.username', 'like', search) - }) - } + if (timeCondition) deposits = deposits.whereRaw(timeCondition) + if (userCondition) deposits = deposits.whereRaw(userCondition) var found = await deposits debug('deposits: ' + JSON.stringify(found)) @@ -530,27 +461,11 @@ router.get( .from('withdrawals') .leftJoin('users', 'withdrawals.user_id', 'users.id') - if (since) { - withdrawals = withdrawals - .where('withdrawals.updatedAt', '>=', since) - } - - debug('withdrawals since ' + since) - - - if (starts_with) { - search = search + '%' - } else if (contains) { - search = '%' + search + '%' - } + var timeCondition = parseInput.addTimeSearch(req.query, 'withdrawals.updatedAt') + var userCondition = parseInput.addUserSearch(req.query) - if (search) { - withdrawals = withdrawals - .where(function() { - this.where('users.email', 'like', search) - this.orWhere('users.username', 'like', search) - }) - } + if (timeCondition) withdrawals = withdrawals.whereRaw(timeCondition) + if (userCondition) withdrawals = withdrawals.whereRaw(userCondition) var found = await withdrawals debug('withdrawals: ' + JSON.stringify(found)) @@ -595,27 +510,12 @@ router.get( .leftJoin('accounts', 'payments.account_id', 'accounts.id') .leftJoin('users', 'accounts.user_id', 'users.id') - if (since) { - payments = payments - .where('payments.updatedAt', '>=', since) - } - - debug('payments since ' + since) - + var timeCondition = parseInput.addTimeSearch(req.query, 'payments.updatedAt') + var userCondition = parseInput.addUserSearch(req.query) - if (starts_with) { - search = search + '%' - } else if (contains) { - search = '%' + search + '%' - } + if (timeCondition) payments = payments.whereRaw(timeCondition) + if (userCondition) payments = payments.whereRaw(userCondition) - if (search) { - payments = payments - .where(function() { - this.where('users.email', 'like', search) - this.orWhere('users.username', 'like', search) - }) - } var found = await payments debug('payments: ' + JSON.stringify(found)) return res.send({payments: found}) @@ -658,7 +558,9 @@ router.get( // 'payments.network', // knex.raw('LEFT(hash,20) as hash'), // knex.raw('Left(payments.updatedAt,16) as transferred') - ) + // knex.raw( parseInput.readableDate('Max(payments.updatedAt)', 'last_changed') ) + knex.raw( 'DATE_FORMAT(Max(payments.updatedAt), ?) AS last_changed', "%M %d, %Y" ) + ) .from('payments') .leftJoin('accounts', 'payments.account_id', 'accounts.id') .leftJoin('users', 'payments.user_id', 'users.id') // should really point to accounts @@ -666,30 +568,16 @@ router.get( // .groupBy('payments.currency') // .groupBy('payments.network') - if (since) { - kyc = kyc - .where('payments.updatedAt', '>=', since) - } - - debug('payments since ' + since) + var timeCondition = parseInput.addTimeSearch(req.query, 'payments.updatedAt') + var userCondition = parseInput.addUserSearch(req.query) - if (starts_with) { - search = search + '%' - } else if (contains) { - search = '%' + search + '%' - } + if (timeCondition) kyc = kyc.whereRaw(timeCondition) + if (userCondition) kyc = kyc.whereRaw(userCondition) - if (search) { - kyc = kyc - .where(function() { - this.where('users.email', 'like', search) - this.orWhere('users.username', 'like', search) - }) - } var found = await kyc.having('max', '>=', threshold) debug('payments: ' + JSON.stringify(found)) - return res.send({kyc_transactions: found}) + return res.send({found: found}) }) ); diff --git a/routes/bitcoin/receive.js b/routes/bitcoin/receive.js index 67ccdb45..603db47b 100644 --- a/routes/bitcoin/receive.js +++ b/routes/bitcoin/receive.js @@ -18,6 +18,8 @@ const network = config.bitcoin.network === "mainnet" ? "bitcoin" : config.bitcoin.network ]; +const queue = {}; + zmqRawTx.on("message", async (topic, message, sequence) => { const hex = message.toString("hex"); let tx = bitcoin.Transaction.fromHex(message); @@ -27,7 +29,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => { if (payments.includes(hash)) return; Promise.all( - tx.outs.map(async o => { + tx.outs.map(async (o) => { try { const { value } = o; @@ -46,20 +48,20 @@ zmqRawTx.on("message", async (topic, message, sequence) => { let user = await db.User.findOne({ where: { - username: addresses[address] - } + username: addresses[address], + }, }); const invoice = await db.Invoice.findOne({ where: { user_id: user.id, - network: "bitcoin" + network: "bitcoin", }, order: [["id", "DESC"]], include: { model: db.Account, - as: "account" - } + as: "account", + }, }); if (!invoice) return; @@ -104,7 +106,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => { confirmed, address, network: "bitcoin", - invoice_id: invoice.id + invoice_id: invoice.id, }); payment = payment.get({ plain: true }); payment.account = account.get({ plain: true }); @@ -123,19 +125,17 @@ zmqRawTx.on("message", async (topic, message, sequence) => { ); }); -let queue = {}; - zmqRawBlock.on("message", async (topic, message, sequence) => { - 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; + 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; + }); }); -}); setInterval(async () => { try { @@ -144,25 +144,25 @@ setInterval(async () => { const hash = arr[i]; let account, address, user, total; - await db.transaction(async transaction => { + await db.transaction(async (transaction) => { let p = await db.Payment.findOne({ where: { hash, confirmed: 0, received: 1 }, include: [ { model: db.Account, - as: "account" + as: "account", }, { model: db.Invoice, - as: "invoice" + as: "invoice", }, { model: db.User, - as: "user" - } + as: "user", + }, ], lock: transaction.LOCK.UPDATE, - transaction + transaction, }); if (p && p.address) address = p.address; @@ -211,7 +211,7 @@ setInterval(async () => { address: c.address, amount: total - 100, user, - limit: total + limit: total, }); } } diff --git a/routes/bitcoin/send.js b/routes/bitcoin/send.js index cab79e06..470889b3 100644 --- a/routes/bitcoin/send.js +++ b/routes/bitcoin/send.js @@ -1,4 +1,3 @@ -const bitcoin = require("bitcoinjs-lib"); const btc = config.liquid.btcasset; module.exports = ah(async (req, res) => { @@ -32,10 +31,10 @@ module.exports = ah(async (req, res) => { total = total - change + fee; let amount = total - fee; - let params; + let account, params; try { await db.transaction(async transaction => { - let account = await db.Account.findOne({ + account = await db.Account.findOne({ where: { id: user.account_id }, @@ -62,38 +61,38 @@ module.exports = ah(async (req, res) => { await account.decrement({ balance: total }, { transaction }); await account.reload({ transaction }); + }); - params = { - amount: -amount, - fee, - memo, - account_id: account.id, - user_id: user.id, - rate: app.get("rates")[user.currency], - currency: user.currency, - address, - confirmed: true, - received: false, - network: "bitcoin" - }; + params = { + amount: -amount, + fee, + memo, + account_id: account.id, + user_id: user.id, + rate: app.get("rates")[user.currency], + currency: user.currency, + address, + confirmed: true, + received: false, + network: "bitcoin" + }; - if (config.bitcoin.walletpass) - await bc.walletPassphrase(config.bitcoin.walletpass, 300); + if (config.bitcoin.walletpass) + await bc.walletPassphrase(config.bitcoin.walletpass, 300); - hex = (await bc.signRawTransactionWithWallet(hex)).hex; - params.hash = await bc.sendRawTransaction(hex); + hex = (await bc.signRawTransactionWithWallet(hex)).hex; + params.hash = await bc.sendRawTransaction(hex); - let payment = await db.Payment.create(params, { transaction }); + let payment = await db.Payment.create(params); - payment = payment.get({ plain: true }); - payment.account = account.get({ plain: true }); + payment = payment.get({ plain: true }); + payment.account = account.get({ plain: true }); - emit(user.username, "payment", payment); - res.send(payment); + emit(user.username, "payment", payment); + res.send(payment); - payments.push(params.hash); - l.info("sent bitcoin", user.username, total); - }); + payments.push(params.hash); + l.info("sent bitcoin", user.username, total); } catch (e) { l.error("error sending bitcoin", e.message); return res.status(500).send(e.message); diff --git a/routes/invoices.js b/routes/invoices.js index c188811e..51906b8c 100644 --- a/routes/invoices.js +++ b/routes/invoices.js @@ -27,6 +27,10 @@ app.post( let { liquidAddress, invoice, user, tx } = req.body; let { blindkey } = invoice; + if (invoice.amount < 0) throw new Error("amount out of range"); + if (invoice.tip > invoice.amount || invoice.tip > 1000000 || invoice.tip < 0) + throw new Error("tip amount out of range"); + if (liquidAddress) { l.info("conversion request for", liquidAddress, invoice.text); convert[invoice.text] = { address: liquidAddress, tx }; diff --git a/routes/lightning/receive.js b/routes/lightning/receive.js index 0e98cd93..d974860e 100644 --- a/routes/lightning/receive.js +++ b/routes/lightning/receive.js @@ -1,21 +1,18 @@ -const handlePayment = async msg => { +const handlePayment = async (msg) => { + if (!msg.settled) return; let account, total, user; - try { - await db.transaction(async transaction => { - if (!msg.settled) return; - const invoice = await db.Invoice.findOne({ - where: { - text: msg.payment_request - } - }); + const invoice = await db.Invoice.findOne({ + where: { + text: msg.payment_request, + }, + }); - if (!invoice) - return l.warn( - "received lightning with no invoice", - msg.payment_request - ); + if (!invoice) + return l.warn("received lightning with no invoice", msg.payment_request); + try { + await db.transaction(async (transaction) => { const { text: hash, currency, memo, rate, tip, user_id } = invoice; const amount = parseInt(msg.amt_paid_sat) - tip; if (amount > 10000000 || amount < 0) @@ -25,14 +22,14 @@ const handlePayment = async msg => { where: { user_id, asset: config.liquid.btcasset, - pubkey: null + pubkey: null, }, include: { model: db.User, - as: "user" + as: "user", }, lock: transaction.LOCK.UPDATE, - transaction + transaction, }); ({ user } = account); @@ -52,7 +49,8 @@ const handlePayment = async msg => { received: true, confirmed: true, network: "lightning", - tip + tip, + invoice_id: invoice.id, }, { transaction } ); @@ -85,7 +83,12 @@ const handlePayment = async msg => { let c = convert[msg.payment_request]; if (msg.payment_request && c) { - l.info("lightning detected for conversion request", msg.payment_request, c.address, user.username); + l.info( + "lightning detected for conversion request", + msg.payment_request, + c.address, + user.username + ); user.account = account; @@ -94,7 +97,7 @@ const handlePayment = async msg => { address: c.address, amount: total - 100, user, - limit: total + limit: total, }); } catch (e) { l.error("problem sending liquid payment", e.message, e.stack); @@ -106,14 +109,14 @@ const handlePayment = async msg => { }; if (config.lna.clightning) { - const poll = async ln => { - const wait = async i => { + const poll = async (ln) => { + const wait = async (i) => { const { bolt11: payment_request, pay_index, status, msatoshi_received, - payment_preimage: r_preimage + payment_preimage: r_preimage, } = await ln.waitanyinvoice(i); let settled = status === "paid"; @@ -123,13 +126,13 @@ if (config.lna.clightning) { payment_request, settled, amt_paid_sat, - r_preimage + r_preimage, }); wait(pay_index); }; const { invoices } = await ln.listinvoices(); - wait(Math.max(...invoices.map(i => i.pay_index).filter(n => n))); + wait(Math.max(...invoices.map((i) => i.pay_index).filter((n) => n))); }; poll(lna); diff --git a/routes/liquid/fee.js b/routes/liquid/fee.js index 2070873a..fad69f13 100644 --- a/routes/liquid/fee.js +++ b/routes/liquid/fee.js @@ -3,11 +3,6 @@ const buildTx = require("../../lib/buildliquidtx"); liquidTx = async ({ address, asset, amount, feeRate, replaceable, user }) => { let tx, fee; let node = lq; - /* - if (asset === config.liquid.btcasset) { - node = rare; - } - */ if (user.account.pubkey) { let psbt = await buildTx({ @@ -64,6 +59,8 @@ liquidTx = async ({ address, asset, amount, feeRate, replaceable, user }) => { } ); + l.info("funding tx for fee estimate", tx); + tx = await node.fundRawTransaction(tx, params); let blinded = await node.blindRawTransaction(tx.hex); @@ -81,7 +78,7 @@ module.exports = ah(async (req, res) => { let tx = await liquidTx({ ...req.body, user: req.user }) res.send(tx); } catch (e) { - l.error("error estimating liquid fee", e.message, e.stack); + l.error("error estimating liquid fee", e.message); return res.status(500).send(e.message); } }); diff --git a/routes/liquid/receive.js b/routes/liquid/receive.js index 3a6aab10..2396755f 100644 --- a/routes/liquid/receive.js +++ b/routes/liquid/receive.js @@ -15,7 +15,7 @@ const getAccount = async (params, transaction) => { let account = await db.Account.findOne({ where: params, lock: transaction.LOCK.UPDATE, - transaction + transaction, }); if (account) { @@ -37,12 +37,12 @@ const getAccount = async (params, transaction) => { const existing = await db.Account.findOne({ where: { asset, - pubkey + pubkey, }, order: [["id", "ASC"]], limit: 1, lock: transaction.LOCK.UPDATE, - transaction + transaction, }); if (existing) { @@ -65,6 +65,8 @@ const zmqRawTx = zmq.socket("sub"); zmqRawTx.connect(config.liquid.zmqrawtx); zmqRawTx.subscribe("rawtx"); +const queue = {}; + zmqRawTx.on("message", async (topic, message, sequence) => { const hex = message.toString("hex"); @@ -80,7 +82,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => { if (payments.includes(blinded.txid)) return; Promise.all( - tx.vout.map(async o => { + tx.vout.map(async (o) => { try { if (!(o.scriptPubKey && o.scriptPubKey.addresses)) return; @@ -92,14 +94,14 @@ zmqRawTx.on("message", async (topic, message, sequence) => { Object.keys(addresses).includes(address) && !change.includes(address) ) { - await db.transaction(async transaction => { + await db.transaction(async (transaction) => { let user = await getUser(addresses[address], transaction); let invoice = await db.Invoice.findOne({ where: { unconfidential: address, user_id: user.id, - network: "liquid" + network: "liquid", }, order: [["id", "DESC"]], }); @@ -110,10 +112,10 @@ zmqRawTx.on("message", async (topic, message, sequence) => { let account = await db.Account.findOne({ where: { - id: invoice.account_id + id: invoice.account_id, }, lock: transaction.LOCK.UPDATE, - transaction + transaction, }); if ( @@ -131,7 +133,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => { asset, pubkey: account.pubkey, pending: value, - index: 0 + index: 0, }, transaction ); @@ -162,7 +164,8 @@ zmqRawTx.on("message", async (topic, message, sequence) => { tip, confirmed, address, - network: "liquid" + network: "liquid", + invoice_id: invoice.id, }, { transaction } ); @@ -185,12 +188,10 @@ zmqRawTx.on("message", async (topic, message, sequence) => { ); }); -let queue = {}; - zmqRawBlock.on("message", async (topic, message, sequence) => { try { const payments = await db.Payment.findAll({ - where: { confirmed: 0 } + where: { confirmed: 0 }, }); const block = Block.fromHex(message.toString("hex"), true); @@ -200,9 +201,9 @@ zmqRawBlock.on("message", async (topic, message, sequence) => { hash = await lq.getBlockHash(block.height); json = await lq.getBlock(hash, 2); - json.tx.map(async tx => { + json.tx.map(async (tx) => { if (issuances[tx.txid]) { - await db.transaction(async transaction => { + await db.transaction(async (transaction) => { const { user_id, asset, @@ -210,7 +211,7 @@ zmqRawBlock.on("message", async (topic, message, sequence) => { asset_payment_id, token, token_amount, - token_payment_id + token_payment_id, } = issuances[tx.txid]; const user = await getUserById(user_id); @@ -218,7 +219,7 @@ zmqRawBlock.on("message", async (topic, message, sequence) => { let account = await db.Account.findOne({ where: { user_id, asset }, lock: transaction.LOCK.UPDATE, - transaction + transaction, }); account.balance = asset_amount * SATS; account.pending = 0; @@ -228,10 +229,10 @@ zmqRawBlock.on("message", async (topic, message, sequence) => { where: { id: asset_payment_id }, include: { model: db.Account, - as: "account" + as: "account", }, lock: transaction.LOCK.UPDATE, - transaction + transaction, }); payment.confirmed = true; @@ -246,7 +247,7 @@ zmqRawBlock.on("message", async (topic, message, sequence) => { account = await db.Account.findOne({ where: { user_id, asset: token }, lock: transaction.LOCK.UPDATE, - transaction + transaction, }); account.balance = token_amount * SATS; account.pending = 0; @@ -256,10 +257,10 @@ zmqRawBlock.on("message", async (topic, message, sequence) => { where: { id: token_payment_id }, include: { model: db.Account, - as: "account" + as: "account", }, lock: transaction.LOCK.UPDATE, - transaction + transaction, }); payment.confirmed = true; @@ -272,7 +273,7 @@ zmqRawBlock.on("message", async (topic, message, sequence) => { emit(user.username, "payment", payment); } }); - } else if (payments.find(p => p.hash === tx.txid)) queue[tx.txid] = 1; + } else if (payments.find((p) => p.hash === tx.txid)) queue[tx.txid] = 1; }); } catch (e) { return console.log(e); @@ -286,26 +287,26 @@ setInterval(async () => { for (let i = 0; i < arr.length; i++) { const hash = arr[i]; - let account, address, user, total; - await db.transaction(async transaction => { - let p = await db.Payment.findOne({ + let account, address, user, total, p; + await db.transaction(async (transaction) => { + p = await db.Payment.findOne({ where: { hash, confirmed: 0, received: 1 }, include: [ { model: db.Account, - as: "account" + as: "account", }, { model: db.Invoice, - as: "invoice" + as: "invoice", }, { model: db.User, - as: "user" - } + as: "user", + }, ], lock: transaction.LOCK.UPDATE, - transaction + transaction, }); ({ account, address, user } = p); @@ -338,7 +339,7 @@ setInterval(async () => { ); notify(user, `${total} SAT payment confirmed`); - await callWebhook(p.invoice, p); + callWebhook(p.invoice, p); } else { l.warn("couldn't find liquid payment", hash); } @@ -347,7 +348,7 @@ setInterval(async () => { }); let c = convert[address]; - if (address && c) { + if (address && c && p.account.asset === network.assetHash) { l.info( "liquid detected for conversion request", address, @@ -361,11 +362,11 @@ setInterval(async () => { address: c.address, amount: total - 100, user, - limit: total + limit: total, }); } } } catch (e) { l.error("problem processing queued liquid transaction", e.message, e.stack); } -}, 1000); +}); diff --git a/routes/liquid/send.js b/routes/liquid/send.js index 19b7344f..5abc692f 100644 --- a/routes/liquid/send.js +++ b/routes/liquid/send.js @@ -1,5 +1,6 @@ const btc = config.liquid.btcasset; const lcad = config.liquid.cadasset; +const { Transaction } = require("@asoltys/liquidjs-lib"); sendLiquid = async ({ asset, amount, user, address, memo, tx, limit }) => { if (amount > 10000000) throw new Error("Amount too large"); @@ -48,7 +49,8 @@ sendLiquid = async ({ asset, amount, user, address, memo, tx, limit }) => { const assets = Object.keys(totals); const payments = []; - return db.transaction(async transaction => { + let main, signed; + await db.transaction(async transaction => { for (let i = 0; i < assets.length; i++) { let asset = assets[i]; let amount = totals[asset]; @@ -75,12 +77,12 @@ sendLiquid = async ({ asset, amount, user, address, memo, tx, limit }) => { await faucet.reload({ transaction }); await faucet.save({ transaction }); } - - total += fee - covered; } + + total += fee - covered; } - if (limit && total > limit) + if (limit && total > limit + fee) throw new Error("Tx amount exceeds authorized amount"); if (asset !== btc || total) { @@ -132,16 +134,11 @@ sendLiquid = async ({ asset, amount, user, address, memo, tx, limit }) => { } } - if (config.liquid.walletpass) - await lq.walletPassphrase(config.liquid.walletpass, 300); - - let blinded = await lq.blindRawTransaction(tx.hex); - let signed = await lq.signRawTransactionWithWallet(blinded); - let txid = await lq.sendRawTransaction(signed.hex); - - l.info("sent liquid tx", txid, address); + signed = await lq.signRawTransactionWithWallet( + await lq.blindRawTransaction(tx.hex) + ); + let txid = Transaction.fromHex(signed.hex).getId(); - let main; for (let i = 0; i < assets.length; i++) { p = payments[i]; if (p) { @@ -157,15 +154,22 @@ sendLiquid = async ({ asset, amount, user, address, memo, tx, limit }) => { emit(user.username, "account", main.account); emit(user.username, "payment", main); - return main; }); + + if (config.liquid.walletpass) + await lq.walletPassphrase(config.liquid.walletpass, 300); + + let txid = await lq.sendRawTransaction(signed.hex); + l.info("sent liquid tx", txid, address); + + return main; }; module.exports = ah(async (req, res) => { let { user } = req; try { - res.send(await sendLiquid({...req.body, user })); + res.send(await sendLiquid({ ...req.body, user })); } catch (e) { l.error("problem sending liquid", user.username, e.message); return res.status(500).send(e); diff --git a/routes/lnurl.js b/routes/lnurl.js index adf4df7c..67f581d1 100644 --- a/routes/lnurl.js +++ b/routes/lnurl.js @@ -129,16 +129,6 @@ app.get( maxSendable = maxSendable || 1000000000; if (parseInt(amount)) minSendable = maxSendable = amount * 1000; - let invoices = await db.Invoice.findAll({ - where: { - amount - }, - order: [["id", "DESC"]], - limit: 1 - }); - - if (invoices.length) await invoices[0].destroy(); - try { const result = await lnurlServer.generateNewUrl("payRequest", { minSendable, diff --git a/routes/payments.js b/routes/payments.js index 70b711da..efb01079 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -96,6 +96,7 @@ ah(async () => { if (config.lna.clightning) { lna = require("clightning-client")(config.lna.dir); } else { + lnd = require("../lib/lnd"); lna = lnd.default; lnp = [ "addInvoice", diff --git a/routes/referrals.js b/routes/referrals.js index 3bcce5b2..11cf8590 100644 --- a/routes/referrals.js +++ b/routes/referrals.js @@ -244,6 +244,7 @@ router.post( debug('email: ' + email) debug('phone: ' + phone) + debug('user_id: ' + user_id) await knex.table('waiting_list') .insert({ @@ -259,15 +260,17 @@ router.post( router.get( "/joinQueue", ah(async (req, res) => { - const {email, phone} = req.query; + const {email, phone, user_id} = req.query; debug('email: ' + email) debug('phone: ' + phone) + debug('user: ' + user_id) await knex('waiting_list') .insert({ email: email, - phone: phone + phone: phone, + user_id: user_id }) res.send({success: true, message: 'Added ' + email + ' to waiting list ' + phone}) diff --git a/routes/send.js b/routes/send.js index 579e70b4..3aec9fb6 100644 --- a/routes/send.js +++ b/routes/send.js @@ -45,7 +45,7 @@ module.exports = ah(async (req, res, next) => { }; if (!username) { - return res.status(500).send("Vouchers temporarily disabled"); + throw new Error("Vouchers temporarily disabled"); l.info("creating redeemable payment"); params.redeemcode = uuidv4(); params.hash = `${req.get("origin")}/redeem/${params.redeemcode}`; diff --git a/routes/users.js b/routes/users.js index 71f4b89a..23f016c7 100644 --- a/routes/users.js +++ b/routes/users.js @@ -148,7 +148,8 @@ app.post( return res.status(500).send("Username taken"); } else { sockets[username] = sockets[user.username]; - if (user.username !== username) l.info("changing username", user.username, username); + if (user.username !== username) + l.info("changing username", user.username, username); user.username = username; token = jwt.sign({ username }, config.jwt); @@ -323,10 +324,7 @@ app.post( (user.password && !(await bcrypt.compare(req.body.password, user.password))) ) { - l.warn( - "invalid username or password attempt", - req.body.username - ); + l.warn("invalid username or password attempt", req.body.username); return res.status(401).end(); } @@ -426,11 +424,24 @@ app.get( : config.liquid.network ]; type = "p2sh"; - + // async request to node to bump its internal index but don't use result - lq.getNewAddress().catch(console.error); + lq.getNewAddress().catch(e => + l.warn("Problem bumping liquid address index", e.message) + ); i = parseInt(app.get("lqAddressIndex")); + + if (!i) { + const { hdkeypath } = await lq.getAddressInfo( + await lq.getNewAddress() + ); + const parts = hdkeypath.split("/"); + i = parts[parts.length - 1].slice(0, -1); + } + + if (!i) throw new Error("Problem generating address"); + hd = fromBase58(config.liquid.masterkey, n).derivePath(`m/0'/0'/${i}'`); app.set("lqAddressIndex", i + 1); } else { diff --git a/scripts/apiParse.js b/scripts/apiParse.js index 427f34cd..138a82f4 100644 --- a/scripts/apiParse.js +++ b/scripts/apiParse.js @@ -1,9 +1,10 @@ var debug = require('debug')('debug') -const addUserSearch = async function (input) { +const addUserSearch = function (input, table) { debug('add User Search') var {search, starts_with, contains} = input + if (!table) { table = 'Users' } // if (since) cmd = cmd.havingRaw('invoices + payments + deposits + orders > ?', [0]) if (search) { @@ -13,7 +14,7 @@ const addUserSearch = async function (input) { search = '%' + search + '%' } - var condition = "(Users.email like '" + search + " OR Users.username like '" + search + "'" + var condition = "(" + table + ".email like '" + search + "' OR " + table + ".username like '" + search + "')" debug('condition: ' + condition) return condition @@ -22,14 +23,14 @@ const addUserSearch = async function (input) { } } -const addTimeSearch = async function (input, field) { +const addTimeSearch = function (input, field) { debug('add time Search') if (!field) { field = 'updatedAt' } var {since} = input if (since) { - condition = "WHERE " + field + " >= " + since + condition = field + " >= '" + since + "'" debug('condition: ' + condition) return condition @@ -38,5 +39,8 @@ const addTimeSearch = async function (input, field) { } } -module.exports = {addUserSearch: addUserSearch, addTimeSearch: addTimeSearch} +module.exports = { + addUserSearch: addUserSearch, + addTimeSearch: addTimeSearch, +} diff --git a/watcher.sh b/watcher.sh deleted file mode 100755 index 3fb60993..00000000 --- a/watcher.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -sats=100000000 -threshold=$1 -while true -do - ln=$(lncli --lnddir=/root/.lnda --network=mainnet --chain=bitcoin --rpcserver=localhost:10001 channelbalance | jr .balance) - bc=$(bitcoin-cli getbalance) - el=$(elements-cli getbalance | jr .bitcoin) - total=$(node -pe "$ln + $bc * $sats + $el * $sats") - low=$(node -pe "$total < $threshold") - echo $(date -Is) ln $ln bc $bc el $el tot $total - if "$low"; then - echo "Threshold hit, setting new threshold" - threshold=$total - # yarn pm2 stop 0 > /dev/null - node /root/mailgun/index.js - fi - sleep 10 -done |
