From fdbbb5af05c46a203032575a55ac66b655c801f1 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 10 Jun 2021 17:24:14 -0400 Subject: remove build endpoint --- index.js | 68 ---------------------------------------------------------------- 1 file changed, 68 deletions(-) diff --git a/index.js b/index.js index b7f8c6ba..4866691f 100644 --- a/index.js +++ b/index.js @@ -70,7 +70,6 @@ require("./lib/notifications"); require("./lib/register"); require("./lib/send"); - require("./routes/assets"); require("./routes/invoices"); require("./routes/payments"); @@ -82,73 +81,6 @@ require("./routes/funding"); if (config.lnurl) require("./routes/lnurl"); if (config.imap) require("./lib/mail"); - -const sigHeaderName = "X-Hub-Signature-256"; -const sigHashAlg = "sha256"; -function verifyPostData(req, res, next) { - try { - if (!req.rawBody) { - return next("Request body empty"); - } - - const sig = Buffer.from(req.get(sigHeaderName) || "", "utf8"); - const hmac = crypto.createHmac(sigHashAlg, config.github); - const digest = Buffer.from( - sigHashAlg + "=" + hmac.update(req.rawBody).digest("hex"), - "utf8" - ); - - if (sig.length !== digest.length || !crypto.timingSafeEqual(digest, sig)) { - return next( - `Request body digest (${digest}) did not match ${sigHeaderName} (${sig})` - ); - } - } catch (e) { - console.log(e.message); - } - - return next(); -} - -app.post("/build", (req, res) => { - const mailgun = require("mailgun-js")(config.mailgun); - let data = { - subject: "Build started", - text: `Build started at ${new Date()}`, - from: "adam@coinos.io", - to: "build@coinos.io" - }; - - mailgun.messages().send(data); - l.info("build starting"); - fs.writeFileSync("build.json", req.body.payload); - - const { exec } = require("child_process"); - exec("bash build.sh", (error, stdout, stderr) => { - if (error) { - data = { - subject: "Build error", - text: `Build failed at ${new Date()}`, - from: "adam@coinos.io", - to: "build@coinos.io" - }; - - mailgun.messages().send(data); - l.warn("build failed"); - } else { - data = { - subject: "Build finished", - text: `Build finished at ${new Date()}`, - from: "adam@coinos.io", - to: "build@coinos.io" - }; - - mailgun.messages().send(data); - l.info("build finished"); - } - }); -}); - app.use((err, req, res, next) => { const details = { path: req.path, -- cgit v1.2.3 From e343c8b07794a86156601efebe58b443b15b10da Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 17 Jun 2021 17:05:09 -0400 Subject: add fee to total --- routes/liquid/send.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routes/liquid/send.js b/routes/liquid/send.js index 19b7344f..6455736c 100644 --- a/routes/liquid/send.js +++ b/routes/liquid/send.js @@ -75,12 +75,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) { -- cgit v1.2.3 From de06cbf6141679b2e10ca01bee1e372efc356c91 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 24 Jun 2021 02:52:33 -0400 Subject: only send on-chain after db transaction --- routes/bitcoin/send.js | 55 +++++++++++++++++++++++++------------------------- routes/liquid/fee.js | 7 +------ routes/liquid/send.js | 28 ++++++++++++++----------- routes/payments.js | 12 +++++++---- 4 files changed, 52 insertions(+), 50 deletions(-) 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/liquid/fee.js b/routes/liquid/fee.js index 2070873a..a6c29f41 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({ @@ -81,7 +76,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/send.js b/routes/liquid/send.js index 6455736c..13f77d26 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("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]; @@ -132,16 +134,11 @@ sendLiquid = async ({ asset, amount, user, address, memo, tx, limit }) => { } } - if (config.liquid.walletpass) - await lq.walletPassphrase(config.liquid.walletpass, 300); + signed = await lq.signRawTransactionWithWallet( + await lq.blindRawTransaction(tx.hex) + ); + let txid = Transaction.fromHex(signed.hex).getId(); - 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); - - 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/payments.js b/routes/payments.js index e8e94d8a..86c0dd15 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -138,10 +138,14 @@ ah(async () => { require("./liquid/receive"); setTimeout(async () => { - const address = await lq.getNewAddress(); - const { hdkeypath } = await lq.getAddressInfo(address); - const parts = hdkeypath.split("/"); - app.set("lqAddressIndex", parts[parts.length - 1].slice(0, -1)); + try { + const address = await lq.getNewAddress(); + const { hdkeypath } = await lq.getAddressInfo(address); + const parts = hdkeypath.split("/"); + app.set("lqAddressIndex", parts[parts.length - 1].slice(0, -1)); + } catch(e) { + console.log("Problem getting liquid address", e.message); + } }, 50); } -- cgit v1.2.3 From 36a05a441a6be0a1a270802c9a38a2265a7b775e Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 24 Jun 2021 02:52:43 -0400 Subject: contact form handler --- lib/mail.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/mail.js b/lib/mail.js index 705880d2..9780fab7 100644 --- a/lib/mail.js +++ b/lib/mail.js @@ -10,6 +10,22 @@ function openInbox(cb) { imap.openBox("INBOX", true, cb); } +app.post( + "/contact", + ah((req, res) => { + const mailgun = require("mailgun-js")(config.mailgun); + let data = { + subject: "Contact form", + text: JSON.stringify(req.body), + from: "server@coinos.io", + to: "contact@coinos.io" + }; + + mailgun.messages().send(data); + res.send(req.body); + }) +); + imap.once("ready", function() { openInbox(function(err, box) { if (err) throw err; -- cgit v1.2.3 From 3fedd98ccd302589dcf21cb1152a6132bfcb35fc Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 24 Jun 2021 02:52:51 -0400 Subject: pm2 upgrade --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ae280704..d8985205 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "devDependencies": { "nodemon": "^2.0.2", "pino-pretty": "^3.6.1", - "pm2": "^4.5.6", + "pm2": "^5.1.0", "prettier": "^1.18.2" } } -- cgit v1.2.3 From 3ff26389ca9854d7d34ed28d3f034b2d32938872 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 24 Jun 2021 06:32:21 -0400 Subject: invoice amount validation --- routes/invoices.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/routes/invoices.js b/routes/invoices.js index cc8549e1..93528d60 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 }; @@ -43,7 +47,7 @@ app.post( if (!user) throw new Error("user not provided"); if (!invoice.currency) invoice.currency = user.currency; if (!invoice.rate) invoice.rate = app.get("rates")[invoice.currency]; - if (invoice.tip > invoice.amount || invoice.tip > 1000000) throw new Error("tip is too large") + invoice.user_id = user.id; invoice.account_id = user.account_id; @@ -54,7 +58,7 @@ app.post( invoice.amount, invoice.tip, invoice.currency, - `${invoice.text.substr(0, 8)}..${invoice.text.substr(-6)}`, + `${invoice.text.substr(0, 8)}..${invoice.text.substr(-6)}` ); if (!invoice.tip) invoice.tip = 0; -- cgit v1.2.3 From 9e6091525bfa1e068fa4592b48acd8fa9d8a7fc5 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 30 Jun 2021 14:59:30 -0400 Subject: handle address failure --- routes/payments.js | 2 +- routes/users.js | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/routes/payments.js b/routes/payments.js index 86c0dd15..fe8092a9 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -144,7 +144,7 @@ ah(async () => { const parts = hdkeypath.split("/"); app.set("lqAddressIndex", parts[parts.length - 1].slice(0, -1)); } catch(e) { - console.log("Problem getting liquid address", e.message); + l.warn("Problem getting liquid address index", e.message); } }, 50); } diff --git a/routes/users.js b/routes/users.js index 09ba5d43..25a737ce 100644 --- a/routes/users.js +++ b/routes/users.js @@ -160,7 +160,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); @@ -335,10 +336,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(); } @@ -438,11 +436,22 @@ 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 { -- cgit v1.2.3 From 8a554b6d3085efc3e4b062bdb0ac61cc0ce89416 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 8 Jul 2021 15:14:57 -0400 Subject: check that token can decode properly --- lib/sockets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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"); -- cgit v1.2.3 From 556b133ffbd4591b49626c821d7602e09e97a1f2 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 8 Jul 2021 15:15:14 -0400 Subject: throw instead of returning to abort db transaction --- routes/send.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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}`; -- cgit v1.2.3 From 300ccc094a9c5261a42479ce4f241655dbd190e1 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 8 Jul 2021 15:16:18 -0400 Subject: ignore balance watcher script --- .gitignore | 1 + watcher.sh | 20 -------------------- 2 files changed, 1 insertion(+), 20 deletions(-) delete mode 100755 watcher.sh diff --git a/.gitignore b/.gitignore index b3bbeb6d..131e61c6 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ lnurl-server.sqlite3 watcher.log build.json build.sh +watcher.sh 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 -- cgit v1.2.3 From 29956a035056c76b3329792feb9a7fb3d7c8ec0e Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 8 Jul 2021 15:17:06 -0400 Subject: only convert btc --- routes/liquid/fee.js | 2 ++ routes/liquid/receive.js | 6 +++--- routes/users.js | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/routes/liquid/fee.js b/routes/liquid/fee.js index a6c29f41..fad69f13 100644 --- a/routes/liquid/fee.js +++ b/routes/liquid/fee.js @@ -59,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); diff --git a/routes/liquid/receive.js b/routes/liquid/receive.js index 05ecc21f..9d8923eb 100644 --- a/routes/liquid/receive.js +++ b/routes/liquid/receive.js @@ -287,9 +287,9 @@ setInterval(async () => { for (let i = 0; i < arr.length; i++) { const hash = arr[i]; - let account, address, user, total; + let account, address, user, total, p; await db.transaction(async transaction => { - let p = await db.Payment.findOne({ + p = await db.Payment.findOne({ where: { hash, confirmed: 0, received: 1 }, include: [ { @@ -343,7 +343,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, c.address, user.username); user.account = account; diff --git a/routes/users.js b/routes/users.js index 25a737ce..558d476d 100644 --- a/routes/users.js +++ b/routes/users.js @@ -438,7 +438,9 @@ app.get( type = "p2sh"; // async request to node to bump its internal index but don't use result - lq.getNewAddress().catch((e) => l.warn("Problem bumping liquid address index", e.message)) + lq.getNewAddress().catch(e => + l.warn("Problem bumping liquid address index", e.message) + ); i = parseInt(app.get("lqAddressIndex")); -- cgit v1.2.3 From 889e472f78660f4680586ae0606650229e87d75d Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 3 Aug 2021 04:44:14 -0400 Subject: silhouettes contact endpoint --- lib/mail.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/mail.js b/lib/mail.js index 9780fab7..ca2c2b81 100644 --- a/lib/mail.js +++ b/lib/mail.js @@ -10,6 +10,22 @@ 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: "server@coinos.io", + to: "testsil@coinos.io" + }; + + mailgun.messages().send(data); + res.send(req.body); + }) +); + app.post( "/contact", ah((req, res) => { -- cgit v1.2.3 From 05f4be452167da0ee719b6d27b16299c189a748c Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 3 Aug 2021 04:44:52 -0400 Subject: increase allowed fee rate --- lib/send.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/send.js b/lib/send.js index 660fcae4..fd577231 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"); -- cgit v1.2.3 From 1fbae57bb9540b1c116efc367809ac01ae2e9df2 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 3 Aug 2021 04:47:20 -0400 Subject: move lightning invoice fetch out of db transaction --- routes/lightning/receive.js | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/routes/lightning/receive.js b/routes/lightning/receive.js index 55193d2d..492320d3 100644 --- a/routes/lightning/receive.js +++ b/routes/lightning/receive.js @@ -1,20 +1,18 @@ 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; @@ -83,7 +81,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; -- cgit v1.2.3