diff options
| author | Adam Soltys <[email protected]> | 2020-05-07 20:15:56 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2020-05-07 20:58:24 -0700 |
| commit | 47aa48402a0d34dd8ab11f0f315393fcc4f5c61b (patch) | |
| tree | 2e907e64628d26c4707660e854e9bc87b332ff08 | |
| parent | 2f0f9e640ead76518f7f8013b16704da89421ee4 (diff) | |
| download | coinos-server-47aa48402a0d34dd8ab11f0f315393fcc4f5c61b.tar.gz coinos-server-47aa48402a0d34dd8ab11f0f315393fcc4f5c61b.zip | |
c-lightning support
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | routes/balances.js | 22 | ||||
| -rw-r--r-- | routes/lightning/invoice.js | 20 | ||||
| -rw-r--r-- | routes/lightning/query.js | 28 | ||||
| -rw-r--r-- | routes/lightning/receive.js | 45 | ||||
| -rw-r--r-- | routes/lightning/send.js | 102 | ||||
| -rw-r--r-- | routes/lightning/user.js | 52 | ||||
| -rw-r--r-- | routes/payments.js | 14 | ||||
| -rw-r--r-- | yarn.lock | 27 |
9 files changed, 180 insertions, 131 deletions
diff --git a/package.json b/package.json index 5e1e537c..588ec7a8 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "body-parser": "^1.19.0", "bolt11": "^1.2.5", "buffer-reverse": "^1.0.1", + "clightning-client": "^0.1.2", "compression": "^1.7.4", "connect-redis": "3.4.1", "cookie-parser": "^1.4.4", diff --git a/routes/balances.js b/routes/balances.js index 9e57750b..78567c44 100644 --- a/routes/balances.js +++ b/routes/balances.js @@ -11,19 +11,33 @@ app.get("/info", async (req, res) => { group: ["asset"], }); + let lnchannel; + let lnwallet; + let lninfo; + + if (config.lna.clightning) { + lninfo = await lna.getinfo(); + const funds = await lna.listfunds(); + lnchannel = parseInt(funds.channels.reduce((a, b) => a + b.channel_sat, 0)); + lnwallet = parseInt(funds.outputs.reduce((a, b) => a + b.value, 0)); + } else { + lninfo = await lna.getInfo({}); + lnchannel = parseInt((await lna.channelBalance({})).balance); + lnwallet = parseInt((await lna.walletBalance({})).total_balance); + } + const info = { - lnd: await lna.getInfo({}), bitcoind: await bc.getNetworkInfo(), elementsd: await lq.getNetworkInfo(), accounts, assets, bitcoin: parseInt((await bc.getBalance()) * SATS), liquid: parseInt(assets.bitcoin * SATS), - lnchannel: parseInt((await lna.channelBalance({})).balance), - lnwallet: parseInt((await lna.walletBalance({})).total_balance), + lnchannel, + lnwallet, }; - const { bitcoin, liquid, lnchannel, user, lnwallet } = info; + const { bitcoin, liquid, user } = info; info.total = parseInt(bitcoin) + diff --git a/routes/lightning/invoice.js b/routes/lightning/invoice.js index f19b3484..3f4ab1db 100644 --- a/routes/lightning/invoice.js +++ b/routes/lightning/invoice.js @@ -3,9 +3,25 @@ module.exports = async (req, res) => { l.info("adding lightning invoice", req.user.username, amount, tip); + /* + const exists = await db.Invoice.findOne({ + where: { + [Op.or]: { + address: invoice.address || "undefined", + text: invoice.text + } + } + }); + */ + try { - const invoice = await lnb.addInvoice({ value: amount + tip }); - res.send(invoice.payment_request); + if (config.lna.clightning) { + const invoice = await lnb.invoice(`${amount + tip}sat` || "any", new Date(), "", 360); + res.send(invoice.bolt11); + } else { + const invoice = await lnb.addInvoice({ value: amount + tip }); + res.send(invoice.payment_request); + } } catch (e) { l.error("problem creating invoice", e.message); res.status(500).send(e.message); diff --git a/routes/lightning/query.js b/routes/lightning/query.js deleted file mode 100644 index 6e70f30a..00000000 --- a/routes/lightning/query.js +++ /dev/null @@ -1,28 +0,0 @@ -const bolt11 = require("bolt11"); - -module.exports = async (req, res) => { - let { amount, payreq } = req.body; - - try { - let invoice = await db.Invoice.findOne({ - where: { text: payreq }, - include: { - model: db.User, - as: "user" - } - }); - - if (invoice) emit(req.user.username, "to", invoice.user); - - payreq = bolt11.decode(payreq); - - res.send( - await lna.queryRoutes({ - pub_key: payreq.payeeNodeKey, - amt: amount || payreq.satoshis - }) - ); - } catch (e) { - res.status(500).send(e.message); - } -}; diff --git a/routes/lightning/receive.js b/routes/lightning/receive.js index 286541d2..3082c205 100644 --- a/routes/lightning/receive.js +++ b/routes/lightning/receive.js @@ -21,13 +21,15 @@ const handlePayment = async msg => { } }); + let preimage = msg.r_preimage.toString("hex"); + let payment = await db.Payment.create({ account_id: account.id, user_id, hash, amount, currency, - preimage: msg.r_preimage.toString('hex'), + preimage, rate, received: true, confirmed: true, @@ -54,12 +56,43 @@ const handlePayment = async msg => { "lightning payment received", user.username, payment.amount, - payment.tip, + payment.tip ); }; -const invoices = lna.subscribeInvoices({}); -invoices.on("data", handlePayment); +if (config.lna.clightning) { + const poll = async ln => { + const wait = async i => { + const { + bolt11: payment_request, + pay_index, + status, + msatoshi_received, + payment_preimage: r_preimage + } = await ln.waitanyinvoice(i); + + let settled = status === "paid"; + let amt_paid_sat = parseInt(msatoshi_received / 1000); + + await handlePayment({ + payment_request, + settled, + amt_paid_sat, + r_preimage + }); + wait(pay_index); + }; + + const { invoices } = await ln.listinvoices(); + wait(Math.max(...invoices.map(i => i.pay_index).filter(n => n))); + }; + + poll(lna); + poll(lnb); +} else { + const invoices = lna.subscribeInvoices({}); + invoices.on("data", handlePayment); -const invoicesb = lnb.subscribeInvoices({}); -invoicesb.on("data", handlePayment); + const invoicesb = lnb.subscribeInvoices({}); + invoicesb.on("data", handlePayment); +} diff --git a/routes/lightning/send.js b/routes/lightning/send.js index 2c17ba1f..e3b5c75a 100644 --- a/routes/lightning/send.js +++ b/routes/lightning/send.js @@ -30,57 +30,90 @@ module.exports = async (req, res) => { throw new Error("Insufficient funds"); } - let m = await lna.sendPaymentSync({ - amt: amount, - payment_request: hash, - max_parts: 10, - }); + let error, fee, total, preimage; + + if (config.lna.clightning) { + let m = await lna.pay(hash); + let sent; + ({ + msatoshi: total, + msatoshi_sent: sent, + payment_preimage: preimage + } = m); + + fee = parseInt((sent - total)/1000); + total = parseInt(total / 1000); + } else { + let m = await lna.sendPaymentSync({ + amt: amount, + payment_request: hash, + max_parts: 10 + }); + + ({ + payment_error: error, + payment_route: { total_fees: fee, total_amt: total }, + payment_preimage: preimage + } = m); + } + + preimage = preimage.toString("hex"); + + + if (payreq.payeeNodeKey === config.lnb.id) { + if (config.lna.clightning) { + let invoice = await lna.invoice(`${amount}sat` || "any", new Date(), "", 360); + let { bolt11: payment_request } = invoice; + await lnb.pay(payment_request); + } else { + let invoice = await lna.addInvoice({ value: amount }) + let { payment_request } = invoice; + await lnb.sendPaymentSync({ payment_request, max_parts: 10 }); + } + l.info("lnb sent back", amount); + } - if (m.payment_error) { - l.error("error sending lightning", m.payment_hash.toString('hex'), m.payment_error); - return res.status(500).send(m.payment_error); - } + if (error) { + l.error("error sending lightning", error); + return res.status(500).send(error); + } - if (seen.includes(m.payment_preimage)) { - l.warn("duplicate payment detected", m.payment_preimage); + if (seen.includes(preimage)) { + l.warn("duplicate payment detected", preimage); throw new Error("Duplicate payment detected"); } - let total = parseInt(m.payment_route.total_amt); - let fee = m.payment_route.total_fees; + total = parseInt(total); + fee = parseInt(fee); account.balance -= total; await account.save({ transaction }); - let payment = await db.Payment.create({ - amount: -total, - account_id: account.id, - user_id: user.id, - hash, - preimage: m.payment_preimage.toString("hex"), - rate: app.get("rates")[user.currency], - currency: user.currency, - confirmed: true, - network: "LNBTC" - }, { transaction }); + let payment = await db.Payment.create( + { + amount: -total, + account_id: account.id, + user_id: user.id, + hash, + preimage, + rate: app.get("rates")[user.currency], + currency: user.currency, + confirmed: true, + network: "LNBTC", + fee + }, + { transaction } + ); 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(preimage); + l.info("sent lightning", user.username, total); user = await getUser(user.username, transaction); emit(user.username, "user", user); - l.info(config.lnb.id, payreq.payeeNodeKey); - if (payreq.payeeNodeKey === config.lnb.id) { - let invoice = await lna.addInvoice({ value: amount }) - let { payment_request } = invoice; - await lnb.sendPaymentSync({ payment_request, max_parts: 10 }); - l.info("lnb sending back", amount); - } - seen.push(hash); res.send(payment); }); @@ -88,7 +121,6 @@ module.exports = async (req, res) => { l.error( "problem sending lightning payment", user.username, - user.balance, e.message ); return res.status(500).send(e.message); diff --git a/routes/lightning/user.js b/routes/lightning/user.js deleted file mode 100644 index 41bd507f..00000000 --- a/routes/lightning/user.js +++ /dev/null @@ -1,52 +0,0 @@ -const bolt11 = require("bolt11"); - -module.exports = async (req, res) => { - let { payuser, amount } = req.body; - - let user = await db.User.findOne({ - where: { - username: payuser - } - }); - - const account = await db.Account.findOne({ - where: { - user_id: user.id, - asset: config.liquid.btcasset - } - }); - - l.info("paying user", req.user.username, payuser); - - if (!user) { - return res.status(500).send("Couldn't find the user you're trying to pay"); - } - let err = m => res.status(500).send(m); - - let invoice; - try { - invoice = await lnb.addInvoice({ value: amount }); - } catch (e) { - return err(e.message); - } - - let hash = invoice.payment_request; - - await db.Payment.create({ - user_id: user.id, - account_id: account.id, - hash, - amount, - rate: app.get("rates")[req.user.currency], - currency: req.user.currency, - tip: 0, - confirmed: true, - received: false, - network: "LNBTC" - }); - - req.url = "/lightning/send"; - let payreq = bolt11.decode(hash); - req.body.payreq = hash; - return app._router.handle(req, res); -}; diff --git a/routes/payments.js b/routes/payments.js index 4089ae23..04b5dc51 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -1,13 +1,21 @@ const BitcoinCore = require("bitcoin-core"); const lnd = require("../lib/lnd"); const { Op } = require("sequelize"); +const { join } = require("path"); (async () => { bc = new BitcoinCore(config.bitcoin); lq = new BitcoinCore(config.liquid); - lna = lnd(config.lna); - lnb = lnd(config.lnb); + if (config.lna.clightning) { + const lnapath = join(require('os').homedir(), '.lightningreg/regtest') + const lnbpath = join(require('os').homedir(), '.lightningregb/regtest') + lna = require('clightning-client')(lnapath); + lnb = require('clightning-client')(lnbpath); + } else { + lna = lnd(config.lna); + lnb = lnd(config.lnb); + } seen = []; addresses = {}; @@ -25,9 +33,7 @@ const { Op } = require("sequelize"); app.post("/send", auth, require("./send")); app.post("/lightning/invoice", auth, require("./lightning/invoice")); - app.post("/lightning/query", auth, require("./lightning/query")); app.post("/lightning/send", auth, require("./lightning/send")); - app.post("/lightning/user", auth, require("./lightning/user")); require("./lightning/receive"); app.post("/bitcoin/send", auth, require("./bitcoin/send")); @@ -1187,6 +1187,16 @@ __metadata: languageName: node linkType: hard +"clightning-client@npm:^0.1.2": + version: 0.1.2 + resolution: "clightning-client@npm:0.1.2" + dependencies: + debug: ^4.1.1 + error: ^7.2.0 + checksum: 2/23a25632222a59a14fa44445c59c6bbe0cbf71fefe6ce0dcbb7a4beab7b008284d0cc07dd47e501a0e1e830332afdbdd9cbf58fd8b36a3c8a1294594a174c722 + languageName: node + linkType: hard + "cliui@npm:^3.0.3": version: 3.2.0 resolution: "cliui@npm:3.2.0" @@ -1243,6 +1253,7 @@ __metadata: body-parser: ^1.19.0 bolt11: ^1.2.5 buffer-reverse: ^1.0.1 + clightning-client: ^0.1.2 compression: ^1.7.4 connect-redis: 3.4.1 cookie-parser: ^1.4.4 @@ -1921,6 +1932,15 @@ __metadata: languageName: node linkType: hard +"error@npm:^7.2.0": + version: 7.2.1 + resolution: "error@npm:7.2.1" + dependencies: + string-template: ~0.2.1 + checksum: 2/21ba37fe6750a1a7357509941983af66fa5e3fbe74eca96d48573878916e38fbe77db4d11374c26cd49b3b091ed9e74dc21983fbb86b564989664b1b87ab3003 + languageName: node + linkType: hard + "es6-promise@npm:^4.0.3": version: 4.2.8 resolution: "es6-promise@npm:4.2.8" @@ -5159,6 +5179,13 @@ fsevents@~2.1.2: languageName: node linkType: hard +"string-template@npm:~0.2.1": + version: 0.2.1 + resolution: "string-template@npm:0.2.1" + checksum: 2/c64ac6e7f744d8e37961c987c70038ae4e3ab3c6c9b3822709c1350397f7f0be1e84685924afb44468484823b4e1a8b5d8d4ff925cb0a01b13d8b73e50670513 + languageName: node + linkType: hard + "string-width@npm:^1.0.1": version: 1.0.2 resolution: "string-width@npm:1.0.2" |
