From 0e244af52537c2a72b39f153bb51a8fdfc31023e Mon Sep 17 00:00:00 2001 From: emad-salah Date: Tue, 10 Dec 2019 00:14:59 +0100 Subject: [PATCH] New Lightning services module for managing Lightning gRPC instances throughout the API --- services/lnd/lnd.js | 1 - src/routes.js | 68 +++++++++++++++++++++++++------------- src/server.js | 41 ++++------------------- src/sockets.js | 30 ++--------------- utils/lightningServices.js | 57 ++++++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+), 87 deletions(-) create mode 100644 utils/lightningServices.js diff --git a/services/lnd/lnd.js b/services/lnd/lnd.js index 7065aff9..da5ffe51 100644 --- a/services/lnd/lnd.js +++ b/services/lnd/lnd.js @@ -1,6 +1,5 @@ // app/lnd.js -const debug = require("debug")("lncliweb:lnd"); const logger = require("winston"); // TODO diff --git a/src/routes.js b/src/routes.js index 55a2d475..2d7619a6 100644 --- a/src/routes.js +++ b/src/routes.js @@ -12,6 +12,7 @@ const responseTime = require("response-time"); const getListPage = require("../utils/paginate"); const auth = require("../services/auth/auth"); const FS = require("../utils/fs"); +const LightningServices = require("../utils/lightningServices"); const GunDB = require("../services/gunDB/Mediator"); const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10; @@ -19,10 +20,7 @@ const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10; // module.exports = (app) => { module.exports = ( app, - lightning, config, - walletUnlocker, - lnServicesData, mySocketsEvents, { serverPort } ) => { @@ -34,6 +32,8 @@ module.exports = ( const getAvailableService = () => new Promise((resolve, reject) => { + const { lightning } = LightningServices.services; + lightning.getInfo({}, (err, response) => { if (err) { if (err.message.includes("unknown service lnrpc.Lightning")) { @@ -118,16 +118,7 @@ module.exports = ( }; const recreateLnServices = async () => { - const lnServices = await require("../services/lnd/lightning")( - lnServicesData.lndProto, - lnServicesData.lndHost, - lnServicesData.lndCertPath, - lnServicesData.macaroonPath - ); - // eslint-disable-next-line prefer-destructuring, no-param-reassign - lightning = lnServices.lightning; - // eslint-disable-next-line prefer-destructuring, no-param-reassign - walletUnlocker = lnServices.walletUnlocker; + await LightningServices.init(); return true; }; @@ -138,6 +129,7 @@ module.exports = ( const args = { wallet_password: Buffer.from(password, "utf-8") }; + const { walletUnlocker } = LightningServices.services; walletUnlocker.unlockWallet(args, (unlockErr, unlockResponse) => { if (unlockErr) { reject(unlockErr); @@ -281,6 +273,7 @@ module.exports = ( return false; } catch (err) { logger.debug("Unlock Error:", err); + console.error(err); res.status(400); res.send({ field: "user", errorMessage: sanitizeLNDError(err.message), success: false }); return err; @@ -288,6 +281,7 @@ module.exports = ( }); app.post("/api/lnd/connect", (req, res) => { + const { lightning, walletUnlocker } = LightningServices.services; const args = { wallet_password: Buffer.from(req.body.password, "utf-8") }; @@ -329,6 +323,7 @@ module.exports = ( app.post("/api/lnd/wallet", async (req, res) => { try { + const { walletUnlocker } = LightningServices.services; const { password, alias } = req.body; const healthResponse = await checkHealth(); if (!alias) { @@ -418,7 +413,7 @@ module.exports = ( setTimeout(async () => { try { const macaroonExists = await FS.access( - lnServicesData.macaroonPath + LightningServices.servicesData.macaroonPath ); if (!macaroonExists) { return waitUntilFileExists(seconds + 1); @@ -427,14 +422,8 @@ module.exports = ( logger.debug("admin.macaroon file created"); mySocketsEvents.emit("updateLightning"); - const lnServices = await require("../services/lnd/lightning")( - lnServicesData.lndProto, - lnServicesData.lndHost, - lnServicesData.lndCertPath, - lnServicesData.macaroonPath - ); - lightning = lnServices.lightning; - walletUnlocker = lnServices.walletUnlocker; + await LightningServices.init(); + const token = await auth.generateToken(); return res.json({ mnemonicPhrase, @@ -545,7 +534,8 @@ module.exports = ( // get lnd info app.get("/api/lnd/getinfo", (req, res) => { - // logger.debug("Estimated Fee:", lightning.estimateFee); + const { lightning } = LightningServices.services; + lightning.getInfo({}, async (err, response) => { if (err) { logger.error("GetInfo Error:", err); @@ -572,6 +562,8 @@ module.exports = ( // get lnd node info app.post("/api/lnd/getnodeinfo", (req, res) => { + const { lightning } = LightningServices.services; + lightning.getNodeInfo( { pub_key: req.body.pubkey }, async (err, response) => { @@ -596,6 +588,7 @@ module.exports = ( }); app.get("/api/lnd/getnetworkinfo", (req, res) => { + const { lightning } = LightningServices.services; lightning.getNetworkInfo({}, async (err, response) => { if (err) { logger.debug("GetNetworkInfo Error:", err); @@ -617,6 +610,7 @@ module.exports = ( // get lnd node active channels list app.get("/api/lnd/listpeers", (req, res) => { + const { lightning } = LightningServices.services; lightning.listPeers({}, async (err, response) => { if (err) { logger.debug("ListPeers Error:", err); @@ -638,6 +632,7 @@ module.exports = ( // newaddress app.post("/api/lnd/newaddress", (req, res) => { + const { lightning } = LightningServices.services; lightning.newAddress({ type: req.body.type }, async (err, response) => { if (err) { logger.debug("NewAddress Error:", err); @@ -659,6 +654,7 @@ module.exports = ( // connect peer to lnd node app.post("/api/lnd/connectpeer", async (req, res) => { + const { lightning } = LightningServices.services; if (req.limituser) { const health = await checkHealth(); if (health.LNDStatus.success) { @@ -689,6 +685,7 @@ module.exports = ( // disconnect peer from lnd node app.post("/api/lnd/disconnectpeer", async (req, res) => { + const { lightning } = LightningServices.services; if (req.limituser) { const health = await checkHealth(); if (health.LNDStatus.success) { @@ -716,6 +713,7 @@ module.exports = ( // get lnd node opened channels list app.get("/api/lnd/listchannels", (req, res) => { + const { lightning } = LightningServices.services; lightning.listChannels({}, async (err, response) => { if (err) { logger.debug("ListChannels Error:", err); @@ -737,6 +735,7 @@ module.exports = ( // get lnd node pending channels list app.get("/api/lnd/pendingchannels", (req, res) => { + const { lightning } = LightningServices.services; lightning.pendingChannels({}, async (err, response) => { if (err) { logger.debug("PendingChannels Error:", err); @@ -757,6 +756,7 @@ module.exports = ( }); app.get("/api/lnd/unifiedTrx", (req, res) => { + const { lightning } = LightningServices.services; const { itemsPerPage, page, reversed = true } = req.query; const offset = (page - 1) * itemsPerPage; lightning.listPayments({}, (err, { payments = [] } = {}) => { @@ -802,6 +802,7 @@ module.exports = ( // get lnd node payments list app.get("/api/lnd/listpayments", (req, res) => { + const { lightning } = LightningServices.services; const { itemsPerPage, page, paginate = true } = req.query; lightning.listPayments({ include_incomplete: !!req.include_incomplete, @@ -822,6 +823,7 @@ module.exports = ( // get lnd node invoices list app.get("/api/lnd/listinvoices", (req, res) => { + const { lightning } = LightningServices.services; const { page, itemsPerPage, reversed = true } = req.query; const offset = (page - 1) * itemsPerPage; // const limit = page * itemsPerPage; @@ -852,6 +854,7 @@ module.exports = ( // get lnd node forwarding history app.get("/api/lnd/forwardinghistory", (req, res) => { + const { lightning } = LightningServices.services; lightning.forwardingHistory({}, async (err, response) => { if (err) { logger.debug("ForwardingHistory Error:", err); @@ -873,6 +876,7 @@ module.exports = ( // get the lnd node wallet balance app.get("/api/lnd/walletbalance", (req, res) => { + const { lightning } = LightningServices.services; lightning.walletBalance({}, async (err, response) => { if (err) { logger.debug("WalletBalance Error:", err); @@ -894,6 +898,7 @@ module.exports = ( // get the lnd node wallet balance and channel balance app.get("/api/lnd/balance", async (req, res) => { + const { lightning } = LightningServices.services; const health = await checkHealth(); lightning.walletBalance({}, (err, walletBalance) => { if (err) { @@ -936,6 +941,7 @@ module.exports = ( }); app.post("/api/lnd/decodePayReq", (req, res) => { + const { lightning } = LightningServices.services; const { payReq } = req.body; lightning.decodePayReq({ pay_req: payReq }, async (err, paymentRequest) => { if (err) { @@ -958,6 +964,7 @@ module.exports = ( }); app.get("/api/lnd/channelbalance", (req, res) => { + const { lightning } = LightningServices.services; lightning.channelBalance({}, async (err, response) => { if (err) { logger.debug("ChannelBalance Error:", err); @@ -979,6 +986,7 @@ module.exports = ( // openchannel app.post("/api/lnd/openchannel", async (req, res) => { + const { lightning } = LightningServices.services; if (req.limituser) { const health = await checkHealth(); if (health.LNDStatus.success) { @@ -1020,6 +1028,7 @@ module.exports = ( // closechannel app.post("/api/lnd/closechannel", async (req, res) => { + const { lightning } = LightningServices.services; if (req.limituser) { const health = await checkHealth(); if (health.LNDStatus.success) { @@ -1069,6 +1078,7 @@ module.exports = ( // sendpayment app.post("/api/lnd/sendpayment", async (req, res) => { + const { lightning } = LightningServices.services; if (req.limituser) { const health = await checkHealth(); if (health.LNDStatus.success) { @@ -1121,6 +1131,7 @@ module.exports = ( // addinvoice app.post("/api/lnd/addinvoice", async (req, res) => { + const { lightning } = LightningServices.services; if (req.limituser) { const health = await checkHealth(); if (health.LNDStatus.success) { @@ -1160,6 +1171,7 @@ module.exports = ( // signmessage app.post("/api/lnd/signmessage", async (req, res) => { + const { lightning } = LightningServices.services; if (req.limituser) { const health = await checkHealth(); if (health.LNDStatus.success) { @@ -1190,6 +1202,7 @@ module.exports = ( // verifymessage app.post("/api/lnd/verifymessage", (req, res) => { + const { lightning } = LightningServices.services; lightning.verifyMessage( { msg: Buffer.from(req.body.msg, "utf8"), signature: req.body.signature }, async (err, response) => { @@ -1211,6 +1224,7 @@ module.exports = ( // sendcoins app.post("/api/lnd/sendcoins", async (req, res) => { + const { lightning } = LightningServices.services; if (req.limituser) { const health = await checkHealth(); if (health.LNDStatus.success) { @@ -1243,6 +1257,7 @@ module.exports = ( // queryroute app.post("/api/lnd/queryroute", (req, res) => { + const { lightning } = LightningServices.services; const numRoutes = config.maxNumRoutesToQuery || DEFAULT_MAX_NUM_ROUTES_TO_QUERY; lightning.queryRoutes( @@ -1265,6 +1280,7 @@ module.exports = ( }); app.post("/api/lnd/estimatefee", (req, res) => { + const { lightning } = LightningServices.services; const { amount, confirmationBlocks } = req.body; lightning.estimateFee( { @@ -1293,6 +1309,7 @@ module.exports = ( }); app.post("/api/lnd/listunspent", (req, res) => { + const { lightning } = LightningServices.services; const { minConfirmations = 3, maxConfirmations = 6 } = req.body; lightning.listUnspent( { @@ -1310,6 +1327,7 @@ module.exports = ( }); app.get("/api/lnd/transactions", (req, res) => { + const { lightning } = LightningServices.services; const { page, paginate = true, itemsPerPage } = req.query; lightning.getTransactions({}, (err, { transactions = [] } = {}) => { if (err) { @@ -1325,6 +1343,7 @@ module.exports = ( }); app.post("/api/lnd/sendmany", (req, res) => { + const { lightning } = LightningServices.services; const { addresses } = req.body; lightning.sendMany({ AddrToAmount: addresses }, (err, transactions) => { if (err) { @@ -1336,6 +1355,7 @@ module.exports = ( }); app.get("/api/lnd/closedchannels", (req, res) => { + const { lightning } = LightningServices.services; const { closeTypeFilters = [] } = req.query; const lndFilters = closeTypeFilters.reduce( (filters, filter) => ({ ...filters, [filter]: true }), @@ -1351,6 +1371,7 @@ module.exports = ( }); app.post("/api/lnd/exportchanbackup", (req, res) => { + const { lightning } = LightningServices.services; const { channelPoint } = req.body; lightning.exportChannelBackup( { chan_point: { funding_txid_str: channelPoint } }, @@ -1365,6 +1386,7 @@ module.exports = ( }); app.post("/api/lnd/exportallchanbackups", (req, res) => { + const { lightning } = LightningServices.services; lightning.exportAllChannelBackups({}, (err, channelBackups) => { if (err) { return handleError(res, err); diff --git a/src/server.js b/src/server.js index 16c2d97f..cc81b9d1 100644 --- a/src/server.js +++ b/src/server.js @@ -7,8 +7,7 @@ const server = program => { const Https = require("https"); const Http = require("http"); const Express = require("express"); - // const Cluster = require("cluster"); - // const OS = require("os"); + const LightningServices = require("../utils/lightningServices"); const app = Express(); const FS = require("../utils/fs"); @@ -32,12 +31,6 @@ const server = program => { // utilities functions ================= require("../utils/server-utils")(module); - // setup lightning client ================= - const lnrpc = require("../services/lnd/lightning"); - const lndHost = program.lndhost || defaults.lndHost; - const lndCertPath = program.lndCertPath || defaults.lndCertPath; - const macaroonPath = program.macaroonPath || defaults.macaroonPath; - logger.info("Mainnet Mode:", !!program.mainnet); const wait = seconds => @@ -48,24 +41,11 @@ const server = program => { // eslint-disable-next-line consistent-return const startServer = async () => { try { - const macaroonExists = await FS.access(macaroonPath); - const lnServices = await lnrpc( - defaults.lndProto, - lndHost, - lndCertPath, - macaroonExists ? macaroonPath : macaroonPath - ); - const { lightning } = lnServices; - const { walletUnlocker } = lnServices; - const lnServicesData = { - lndProto: defaults.lndProto, - lndHost, - lndCertPath, - macaroonPath: macaroonExists ? macaroonPath : null - }; + LightningServices.setDefaults(program); + await LightningServices.init(); // init lnd module ================= - const lnd = require("../services/lnd/lnd")(lightning); + const lnd = require("../services/lnd/lnd")(LightningServices.services.lightning); const unprotectedRoutes = { GET: { @@ -166,7 +146,7 @@ const server = program => { logger.error(err); res .status(500) - .send({ status: 500, message: "internal error", type: "internal" }); + .send({ status: 500, errorMessage: "internal error" }); }); const createServer = async () => { @@ -193,27 +173,18 @@ const server = program => { const io = require("socket.io")(serverInstance); - // setup sockets ================= - const lndLogfile = program.lndlogfile || defaults.lndLogFile; - const Sockets = require("./sockets")( io, - lightning, lnd, program.user, program.pwd, program.limituser, - program.limitpwd, - lndLogfile, - lnServicesData + program.limitpwd ); require("./routes")( app, - lightning, defaults, - walletUnlocker, - lnServicesData, Sockets, { serverHost: module.serverHost, diff --git a/src/sockets.js b/src/sockets.js index 0584159f..93435646 100644 --- a/src/sockets.js +++ b/src/sockets.js @@ -1,18 +1,14 @@ // app/sockets.js const logger = require("winston"); -const FS = require("../utils/fs"); module.exports = ( io, - lightning, lnd, login, pass, limitlogin, - limitpass, - lndLogfile, - lnServicesData + limitpass ) => { const Mediator = require("../services/gunDB/Mediator/index.js"); const EventEmitter = require("events"); @@ -72,32 +68,10 @@ module.exports = ( return null; }; - io.on("connection", async socket => { + io.on("connection", socket => { // this is where we create the websocket connection // with the GunDB service. Mediator.createMediator(socket); - - const macaroonExists = await (lnServicesData.macaroonPath ? FS.access(lnServicesData.macaroonPath) : false); - const lnServices = await require("../services/lnd/lightning")( - lnServicesData.lndProto, - lnServicesData.lndHost, - lnServicesData.lndCertPath, - macaroonExists ? lnServicesData.macaroonPath : null - ); - // eslint-disable-next-line prefer-destructuring, no-param-reassign - lightning = lnServices.lightning; - - mySocketsEvents.addListener("updateLightning", async () => { - const newMacaroonExists = await (lnServicesData.macaroonPath ? FS.access(lnServicesData.macaroonPath) : false); - const newLNServices = await require("../services/lnd/lightning")( - lnServicesData.lndProto, - lnServicesData.lndHost, - lnServicesData.lndCertPath, - newMacaroonExists ? lnServicesData.macaroonPath : null - ); - // eslint-disable-next-line prefer-destructuring, no-param-reassign - lightning = newLNServices.lightning; - }); logger.debug("socket.handshake", socket.handshake); diff --git a/utils/lightningServices.js b/utils/lightningServices.js new file mode 100644 index 00000000..eff11738 --- /dev/null +++ b/utils/lightningServices.js @@ -0,0 +1,57 @@ +const FS = require("../utils/fs"); +const lnrpc = require("../services/lnd/lightning"); + +class LightningServices { + setDefaults = program => { + const defaults = require("../config/defaults")(program.mainnet); + + this.defaults = defaults; + this.config = { + useTLS: program.usetls, + serverPort: program.serverport || defaults.serverPort, + serverHost: program.serverhost || defaults.serverHost, + lndHost: program.lndhost || defaults.lndHost, + lndCertPath: program.lndCertPath || defaults.lndCertPath, + macaroonPath: program.macaroonPath || defaults.macaroonPath + }; + } + + isInitialized = () => { + return !!(this.lightning && this.walletUnlocker); + } + + get services() { + return { + lightning: this.lightning, + walletUnlocker: this.walletUnlocker, + }; + } + + get servicesData() { + return this.lnServicesData; + } + + init = async () => { + const { macaroonPath, lndHost, lndCertPath } = this.config; + const macaroonExists = await FS.access(macaroonPath); + const lnServices = await lnrpc( + this.defaults.lndProto, + lndHost, + lndCertPath, + macaroonExists ? macaroonPath : null + ); + const { lightning, walletUnlocker } = lnServices; + this.lightning = lightning; + this.walletUnlocker = walletUnlocker + this.lnServicesData = { + lndProto: this.defaults.lndProto, + lndHost, + lndCertPath, + macaroonPath: macaroonExists ? macaroonPath : null + }; + } +} + +const lightningServices = new LightningServices(); + +module.exports = lightningServices; \ No newline at end of file