diff options
| author | Adam Soltys <[email protected]> | 2021-02-19 21:46:21 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2021-02-19 21:46:21 -0800 |
| commit | fb2a1a9f7262c58636b62fe1eee1b95e88a481c8 (patch) | |
| tree | fc05ad9ac87f402ec48e08d8792e44df72631d23 | |
| parent | 035afbbbc1c00f15d3956dbd21adb3a9c82c223e (diff) | |
| download | coinos-server-fb2a1a9f7262c58636b62fe1eee1b95e88a481c8.tar.gz coinos-server-fb2a1a9f7262c58636b62fe1eee1b95e88a481c8.zip | |
instant lightning->liquid conversion
| -rw-r--r-- | index.js | 1 | ||||
| -rw-r--r-- | routes/invoices.js | 24 | ||||
| -rw-r--r-- | routes/lightning/invoice.js | 3 | ||||
| -rw-r--r-- | routes/lightning/receive.js | 45 | ||||
| -rw-r--r-- | routes/liquid/fee.js | 89 | ||||
| -rw-r--r-- | routes/liquid/send.js | 185 |
6 files changed, 188 insertions, 159 deletions
@@ -17,6 +17,7 @@ fail = msg => { }; challenge = {}; +convert = {}; logins = {}; sessions = {}; sockets = {}; diff --git a/routes/invoices.js b/routes/invoices.js index 3ef0b4e3..9a24a8c0 100644 --- a/routes/invoices.js +++ b/routes/invoices.js @@ -21,11 +21,14 @@ app.get( app.post( "/invoice", + optionalAuth, ah(async (req, res, next) => { try { - let { invoice, user } = req.body; + let { liquidAddress, invoice, user } = req.body; let { blindkey } = invoice; + if (liquidAddress) convert[invoice.text] = liquidAddress; + if (!user) ({ user } = req); else { user = await db.User.findOne({ @@ -35,6 +38,7 @@ app.post( }); } if (!user) throw new Error("user not provided"); + if (!invoice.currency) invoice.currency = user.currency; invoice.user_id = user.id; invoice.account_id = user.account_id; @@ -49,15 +53,17 @@ app.post( if (!invoice.tip) invoice.tip = 0; - const exists = await db.Invoice.findOne({ - where: { - [Op.or]: { - address: invoice.address || "", - unconfidential: invoice.unconfidential || "", - text: invoice.text + const exists = + invoice.text && + (await db.Invoice.findOne({ + where: { + [Op.or]: { + address: invoice.address || "", + unconfidential: invoice.unconfidential || "", + text: invoice.text + } } - } - }); + })); invoice = exists ? await exists.update(invoice) diff --git a/routes/lightning/invoice.js b/routes/lightning/invoice.js index 701ed3c2..140a3002 100644 --- a/routes/lightning/invoice.js +++ b/routes/lightning/invoice.js @@ -1,5 +1,6 @@ module.exports = ah(async (req, res) => { - const { amount, memo, tip } = req.body; + let { amount, memo, tip } = req.body; + if (!tip) tip = 0; try { if (config.lna.clightning) { diff --git a/routes/lightning/receive.js b/routes/lightning/receive.js index 0c8d6557..2144470f 100644 --- a/routes/lightning/receive.js +++ b/routes/lightning/receive.js @@ -1,12 +1,12 @@ -const handlePayment = async (msg) => { +const handlePayment = async msg => { try { - await db.transaction(async (transaction) => { + await db.transaction(async transaction => { if (!msg.settled) return; const invoice = await db.Invoice.findOne({ where: { - text: msg.payment_request, - }, + text: msg.payment_request + } }); if (!invoice) @@ -22,17 +22,38 @@ const handlePayment = async (msg) => { where: { user_id, asset: config.liquid.btcasset, + pubkey: null }, include: { model: db.User, - as: "user", + as: "user" }, lock: transaction.LOCK.UPDATE, - transaction, + transaction }); const { user } = account; + let address = convert[msg.payment_request]; + if (address) { + user.account = account; + + let { tx } = await liquidTx({ + address, + asset: config.liquid.btcasset, + amount, + feeRate: 1000, + replaceable: true, + user + }); + + return sendLiquid({ + address, + user, + tx + }); + } + let preimage = msg.r_preimage.toString("hex"); let payment = await db.Payment.create( @@ -48,7 +69,7 @@ const handlePayment = async (msg) => { received: true, confirmed: true, network: "lightning", - tip, + tip }, { transaction } ); @@ -82,14 +103,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"; @@ -99,13 +120,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 489c5c4b..82bb7604 100644 --- a/routes/liquid/fee.js +++ b/routes/liquid/fee.js @@ -1,25 +1,18 @@ const buildTx = require("../../lib/buildliquidtx"); -module.exports = ah(async (req, res) => { - let { user } = req; - let { address, asset, amount, feeRate, replaceable } = req.body; +liquidTx = async ({ address, asset, amount, feeRate, replaceable, user }) => { let tx, fee; if (user.account.pubkey) { - try { - let psbt = await buildTx({ - address, - asset, - amount, - feeRate, - replaceable, - user - }); - return res.send(psbt); - } catch (e) { - l.error(e.message, e.stack); - return res.status(500).send(e.message); - } + let psbt = await buildTx({ + address, + asset, + amount, + feeRate, + replaceable, + user + }); + return psbt; } let invoice = await db.Invoice.findOne({ @@ -35,47 +28,51 @@ module.exports = ah(async (req, res) => { let { ismine } = await lq.getAddressInfo(address); if (ismine) { emit(user.username, "to", invoice.user); - return res.end(); + return; } } - try { - if (config.liquid.walletpass) - await lq.walletPassphrase(config.liquid.walletpass, 300); + if (config.liquid.walletpass) + await lq.walletPassphrase(config.liquid.walletpass, 300); - amount = parseInt(amount); + amount = parseInt(amount); - let params = { - subtractFeeFromOutputs: amount === user.balance ? [0] : [] - }; + let params = { + subtractFeeFromOutputs: amount === user.balance ? [0] : [] + }; - let value = (amount / SATS).toFixed(8); - if (feeRate) params.feeRate = (feeRate / SATS).toFixed(8); + let value = (amount / SATS).toFixed(8); + if (feeRate) params.feeRate = (feeRate / SATS).toFixed(8); - let info = await lq.getAddressInfo(address); + let info = await lq.getAddressInfo(address); - tx = await lq.createRawTransaction( - [], - { - [address]: (amount / SATS).toFixed(8) - }, - 0, - replaceable, - { - [address]: asset - } - ); + tx = await lq.createRawTransaction( + [], + { + [address]: (amount / SATS).toFixed(8) + }, + 0, + replaceable, + { + [address]: asset + } + ); + + tx = await lq.fundRawTransaction(tx, params); - tx = await lq.fundRawTransaction(tx, params); + let blinded = await lq.blindRawTransaction(tx.hex); + let signed = await lq.signRawTransactionWithWallet(blinded); - let blinded = await lq.blindRawTransaction(tx.hex); - let signed = await lq.signRawTransactionWithWallet(blinded); + decoded = await lq.decodeRawTransaction(signed.hex); + feeRate = Math.round((tx.fee * SATS * 1000) / decoded.vsize); + l.info("estimated", asset, feeRate); - decoded = await lq.decodeRawTransaction(signed.hex); - feeRate = Math.round((tx.fee * SATS * 1000) / decoded.vsize); - l.info("estimated", asset, feeRate); + return { feeRate, tx }; +}; - res.send({ feeRate, tx }); +module.exports = ah(async (req, res) => { + try { + res.send(await liquidTx({ ...req.body, user: req.user })); } catch (e) { 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 01b1a41d..ee62c1b8 100644 --- a/routes/liquid/send.js +++ b/routes/liquid/send.js @@ -1,10 +1,7 @@ const btc = config.liquid.btcasset; const lcad = config.liquid.cadasset; -module.exports = ah(async (req, res) => { - let { user } = req; - let { address, memo, tx } = req.body; - +sendLiquid = async ({ user, address, memo, tx }) => { const isChange = async address => (await lq.getAddressInfo(address)).ismine && !Object.keys(addresses).includes(address); @@ -38,113 +35,119 @@ module.exports = ah(async (req, res) => { const assets = Object.keys(totals); const payments = []; - try { - await db.transaction(async transaction => { - for (let i = 0; i < assets.length; i++) { - let asset = assets[i]; - let amount = totals[asset]; - if (change[asset]) amount -= change[asset]; - let total = amount; - - if (asset === btc) { - let covered = 0; - let nonbtc = assets.filter(a => a !== btc); - if (nonbtc.length === 1) { - let faucet = await db.Account.findOne({ - where: { - asset: nonbtc[0], - user_id: null - }, - lock: transaction.LOCK.UPDATE, - transaction - }); - - if (faucet) { - covered = faucet.balance; - if (covered > fee) covered = fee; - await faucet.decrement({ balance: covered }, { transaction }); - await faucet.reload({ transaction }); - await faucet.save({ transaction }); - } + return db.transaction(async transaction => { + for (let i = 0; i < assets.length; i++) { + let asset = assets[i]; + let amount = totals[asset]; + if (change[asset]) amount -= change[asset]; + let total = amount; - total += fee - covered; - } - } - - if (asset !== btc || total) { - l.info("creating liquid payment", user.username, asset, total, fee); - - let account = await db.Account.findOne({ + if (asset === btc) { + let covered = 0; + let nonbtc = assets.filter(a => a !== btc); + if (nonbtc.length === 1) { + let faucet = await db.Account.findOne({ where: { - user_id: user.id, - asset, - pubkey: null + asset: nonbtc[0], + user_id: null }, lock: transaction.LOCK.UPDATE, - order: [['balance', 'DESC']], transaction }); - if (total > account.balance) { - l.warn("amount exceeds balance", { - total, - fee, - balance: account.balance - }); - throw new Error( - `Insufficient funds, need ${total} ${ - account.ticker === "BTC" ? "SAT" : account.ticker - }, have ${account.balance}` - ); + if (faucet) { + covered = faucet.balance; + if (covered > fee) covered = fee; + await faucet.decrement({ balance: covered }, { transaction }); + await faucet.reload({ transaction }); + await faucet.save({ transaction }); } - await account.decrement({ balance: total }, { transaction }); - await account.reload({ transaction }); + total += fee - covered; + } + } - let payment = { - amount: -amount, - account_id: account.id, - fee, - memo, + if (asset !== btc || total) { + l.info("creating liquid payment", user.username, asset, total, fee); + + let account = await db.Account.findOne({ + where: { user_id: user.id, - rate: app.get("rates")[user.currency], - currency: user.currency, - address, - confirmed: true, - received: false, - network: "liquid" - }; + asset, + pubkey: null + }, + lock: transaction.LOCK.UPDATE, + order: [["balance", "DESC"]], + transaction + }); - payment.account = account; - payments.push(payment); + if (total > account.balance) { + l.warn("amount exceeds balance", { + total, + fee, + balance: account.balance + }); + throw new Error( + `Insufficient funds, need ${total} ${ + account.ticker === "BTC" ? "SAT" : account.ticker + }, have ${account.balance}` + ); } + + await account.decrement({ balance: total }, { transaction }); + await account.reload({ transaction }); + + let payment = { + amount: -amount, + account_id: account.id, + fee, + memo, + user_id: user.id, + rate: app.get("rates")[user.currency], + currency: user.currency, + address, + confirmed: true, + received: false, + network: "liquid" + }; + + payment.account = account; + payments.push(payment); } + } - if (config.liquid.walletpass) - await lq.walletPassphrase(config.liquid.walletpass, 300); + 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); + let blinded = await lq.blindRawTransaction(tx.hex); + let signed = await lq.signRawTransactionWithWallet(blinded); + let txid = await lq.sendRawTransaction(signed.hex); - let main; - for (let i = 0; i < assets.length; i++) { - p = payments[i]; - if (p) { - let { account } = p; - p.hash = txid; - p = await db.Payment.create(p, { transaction }); - if (account.ticker !== "BTC" || !main) { - main = p.get({ plain: true }); - main.account = account.get({ plain: true }); - } + let main; + for (let i = 0; i < assets.length; i++) { + p = payments[i]; + if (p) { + let { account } = p; + p.hash = txid; + p = await db.Payment.create(p, { transaction }); + if (account.ticker !== "BTC" || !main) { + main = p.get({ plain: true }); + main.account = account.get({ plain: true }); } } + } - emit(user.username, "account", main.account); - emit(user.username, "payment", main); - res.send(main); - }); + emit(user.username, "account", main.account); + emit(user.username, "payment", main); + return main; + }); +}; + +module.exports = ah(async (req, res) => { + let { user } = req; + let { address, memo, tx } = req.body; + try { + res.send(await sendLiquid({ user, address, memo, tx })); } catch (e) { l.error("problem sending liquid", user.username, e.message); return res.status(500).send(e.message); |
