diff --git a/package.json b/package.json index 4f6e4fec..662b6962 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@types/node-fetch": "^2.5.3", "@types/underscore": "^1.9.4", "apollo-server": "^2.9.7", + "base64url": "^3.0.1", "date-fns": "^2.8.1", "dotenv": "^8.2.0", "graphql": "^14.5.8", diff --git a/src/helpers/helpers.ts b/src/helpers/helpers.ts index bfcb63be..b58022c4 100644 --- a/src/helpers/helpers.ts +++ b/src/helpers/helpers.ts @@ -1,3 +1,6 @@ +import base64url from "base64url"; +import { authenticatedLndGrpc } from "ln-service"; + export const getIp = (req: any) => { if (!req || !req.headers) { return ""; @@ -9,3 +12,37 @@ export const getIp = (req: any) => { const ip = process.env.NODE_ENV === "development" ? "1.2.3.4" : before; return ip; }; + +export const getBase64CertfromDerFormat = (url: string) => { + if (!url) return null; + + const base64 = base64url.toBase64(url); + + const prefix = "-----BEGIN CERTIFICATE-----\n"; + const postfix = "-----END CERTIFICATE-----"; + const pem = base64.match(/.{0,64}/g) || []; + const pemString = pem.join("\n"); + const pemComplete = prefix + pemString + postfix; + const pemText = base64url.encode(pemComplete); + + return pemText; +}; + +export const getAuthLnd = (auth: string) => { + const url = new URL(auth); + + const encodedCert = url.searchParams.get("cert") || ""; + const encodedMacaroon = url.searchParams.get("macaroon") || ""; + const socket = url.host; + + const cert = getBase64CertfromDerFormat(encodedCert); + const macaroon = base64url.toBase64(encodedMacaroon); + + const { lnd } = authenticatedLndGrpc({ + cert, + macaroon, + socket + }); + + return lnd; +}; diff --git a/src/main.ts b/src/main.ts index f8f759ef..3a76ac29 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,25 +3,12 @@ import { thunderHubSchema } from "./schemas"; import { logger } from "./helpers/logger"; import { getIp } from "./helpers/helpers"; import depthLimit from "graphql-depth-limit"; -import lnService from "ln-service"; - -const { lnd } = lnService.authenticatedLndGrpc({ - cert: process.env.LND_CERT, - macaroon: process.env.LND_MAC, - socket: process.env.LND_IP -}); -logger.info("Connection established with gRPC"); - -lnService.getWalletInfo({ lnd }, (error: any, result: any) => { - logger.info(`Connected to node: ${result.alias}`); - if (error) logger.error(`Error connecting to node: ${error}`); -}); const server = new ApolloServer({ schema: thunderHubSchema, context: async ({ req }: any) => { const ip = getIp(req); - return { ip, lnd }; + return { ip }; }, validationRules: [ depthLimit(2, { ignore: [/_trusted$/, "idontcare", "whatever"] }) diff --git a/src/schemas/query/channels/channelBalance.ts b/src/schemas/query/channels/channelBalance.ts index 4262b296..fd6803bd 100644 --- a/src/schemas/query/channels/channelBalance.ts +++ b/src/schemas/query/channels/channelBalance.ts @@ -2,6 +2,8 @@ import { getChannelBalance as getLnChannelBalance } from "ln-service"; import { logger } from "../../../helpers/logger"; import { ChannelBalanceType } from "../../../schemaTypes/query/info/channelBalance"; import { requestLimiter } from "../../../helpers/rateLimiter"; +import { GraphQLNonNull, GraphQLString } from "graphql"; +import { getAuthLnd } from "../../../helpers/helpers"; interface ChannelBalanceProps { channel_balance: number; @@ -10,9 +12,11 @@ interface ChannelBalanceProps { export const getChannelBalance = { type: ChannelBalanceType, + args: { auth: { type: new GraphQLNonNull(GraphQLString) } }, resolve: async (root: any, params: any, context: any) => { await requestLimiter(context.ip, params, "channelBalance", 1, "1s"); - const { lnd } = context; + + const lnd = getAuthLnd(params.auth); try { const channelBalance: ChannelBalanceProps = await getLnChannelBalance({ diff --git a/src/schemas/query/channels/channels.ts b/src/schemas/query/channels/channels.ts index 21dc3c8e..ce714579 100644 --- a/src/schemas/query/channels/channels.ts +++ b/src/schemas/query/channels/channels.ts @@ -1,8 +1,9 @@ -import { GraphQLList } from "graphql"; +import { GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; import { getChannels as getLnChannels, getNode } from "ln-service"; import { logger } from "../../../helpers/logger"; import { ChannelType } from "../../../schemaTypes/query/info/channels"; import { requestLimiter } from "../../../helpers/rateLimiter"; +import { getAuthLnd } from "../../../helpers/helpers"; interface ChannelListProps { channels: ChannelProps[]; @@ -36,9 +37,11 @@ interface ChannelProps { export const getChannels = { type: new GraphQLList(ChannelType), + args: { auth: { type: new GraphQLNonNull(GraphQLString) } }, resolve: async (root: any, params: any, context: any) => { await requestLimiter(context.ip, params, "channels", 1, "1s"); - const { lnd } = context; + + const lnd = getAuthLnd(params.auth); try { const channelList: ChannelListProps = await getLnChannels({ diff --git a/src/schemas/query/channels/pendingChannels.ts b/src/schemas/query/channels/pendingChannels.ts index bee7b882..f73b9aa3 100644 --- a/src/schemas/query/channels/pendingChannels.ts +++ b/src/schemas/query/channels/pendingChannels.ts @@ -4,8 +4,9 @@ import { } from "ln-service"; import { logger } from "../../../helpers/logger"; import { PendingChannelType } from "../../../schemaTypes/query/info/pendingChannels"; -import { GraphQLList } from "graphql"; +import { GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; import { requestLimiter } from "../../../helpers/rateLimiter"; +import { getAuthLnd } from "../../../helpers/helpers"; interface PendingChannelListProps { pending_channels: PendingChannelProps[]; @@ -27,9 +28,11 @@ interface PendingChannelProps { export const getPendingChannels = { type: new GraphQLList(PendingChannelType), + args: { auth: { type: new GraphQLNonNull(GraphQLString) } }, resolve: async (root: any, params: any, context: any) => { await requestLimiter(context.ip, params, "pendingChannels", 1, "1s"); - const { lnd } = context; + + const lnd = getAuthLnd(params.auth); try { const pendingChannels: PendingChannelListProps = await getLnPendingChannels( diff --git a/src/schemas/query/general/chainBalance.ts b/src/schemas/query/general/chainBalance.ts index 0550b32f..c34fadc6 100644 --- a/src/schemas/query/general/chainBalance.ts +++ b/src/schemas/query/general/chainBalance.ts @@ -4,7 +4,8 @@ import { } from "ln-service"; import { logger } from "../../../helpers/logger"; import { requestLimiter } from "../../../helpers/rateLimiter"; -import { GraphQLInt } from "graphql"; +import { GraphQLInt, GraphQLNonNull, GraphQLString } from "graphql"; +import { getAuthLnd } from "../../../helpers/helpers"; interface ChainBalanceProps { chain_balance: number; @@ -16,9 +17,11 @@ interface PendingChainBalanceProps { export const getChainBalance = { type: GraphQLInt, + args: { auth: { type: new GraphQLNonNull(GraphQLString) } }, resolve: async (root: any, params: any, context: any) => { await requestLimiter(context.ip, params, "chainBalance", 1, "1s"); - const { lnd } = context; + + const lnd = getAuthLnd(params.auth); try { const value: ChainBalanceProps = await getBalance({ @@ -34,9 +37,11 @@ export const getChainBalance = { export const getPendingChainBalance = { type: GraphQLInt, + args: { auth: { type: new GraphQLNonNull(GraphQLString) } }, resolve: async (root: any, params: any, context: any) => { await requestLimiter(context.ip, params, "pendingChainBalance", 1, "1s"); - const { lnd } = context; + + const lnd = getAuthLnd(params.auth); try { const pendingValue: PendingChainBalanceProps = await getPending({ diff --git a/src/schemas/query/general/networkInfo.ts b/src/schemas/query/general/networkInfo.ts index 4785aaf0..e2d0d13c 100644 --- a/src/schemas/query/general/networkInfo.ts +++ b/src/schemas/query/general/networkInfo.ts @@ -2,6 +2,8 @@ import { getNetworkInfo as getLnNetworkInfo } from "ln-service"; import { logger } from "../../../helpers/logger"; import { requestLimiter } from "../../../helpers/rateLimiter"; import { NetworkInfoType } from "../../../schemaTypes/query/info/networkInfo"; +import { GraphQLNonNull, GraphQLString } from "graphql"; +import { getAuthLnd } from "../../../helpers/helpers"; interface NetworkInfoProps { average_channel_size: number; @@ -16,9 +18,11 @@ interface NetworkInfoProps { export const getNetworkInfo = { type: NetworkInfoType, + args: { auth: { type: new GraphQLNonNull(GraphQLString) } }, resolve: async (root: any, params: any, context: any) => { await requestLimiter(context.ip, params, "networkInfo", 1, "1s"); - const { lnd } = context; + + const lnd = getAuthLnd(params.auth); try { const info: NetworkInfoProps = await getLnNetworkInfo({ diff --git a/src/schemas/query/general/nodeInfo.ts b/src/schemas/query/general/nodeInfo.ts index 1d9d01a8..be3dc65e 100644 --- a/src/schemas/query/general/nodeInfo.ts +++ b/src/schemas/query/general/nodeInfo.ts @@ -2,6 +2,8 @@ import { getWalletInfo } from "ln-service"; import { logger } from "../../../helpers/logger"; import { NodeInfoType } from "../../../schemaTypes/query/info/nodeInfo"; import { requestLimiter } from "../../../helpers/rateLimiter"; +import { getAuthLnd } from "../../../helpers/helpers"; +import { GraphQLNonNull, GraphQLString } from "graphql"; interface NodeInfoProps { chains: string[]; @@ -22,9 +24,11 @@ interface NodeInfoProps { export const getNodeInfo = { type: NodeInfoType, + args: { auth: { type: new GraphQLNonNull(GraphQLString) } }, resolve: async (root: any, params: any, context: any) => { await requestLimiter(context.ip, params, "nodeInfo", 1, "1s"); - const { lnd } = context; + + const lnd = getAuthLnd(params.auth); try { const info: NodeInfoProps = await getWalletInfo({ diff --git a/src/schemas/query/invoices/invoices.ts b/src/schemas/query/invoices/invoices.ts index 6691b16f..71071bb4 100644 --- a/src/schemas/query/invoices/invoices.ts +++ b/src/schemas/query/invoices/invoices.ts @@ -1,8 +1,9 @@ -import { GraphQLList } from "graphql"; +import { GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; import { getInvoices as getLnInvoices } from "ln-service"; import { logger } from "../../../helpers/logger"; import { requestLimiter } from "../../../helpers/rateLimiter"; import { GetInvoiceType } from "../../../schemaTypes/query/info/invoices"; +import { getAuthLnd } from "../../../helpers/helpers"; interface PaymentProps { confirmed_at: string; @@ -45,9 +46,11 @@ interface InvoicesProps { export const getInvoices = { type: new GraphQLList(GetInvoiceType), + args: { auth: { type: new GraphQLNonNull(GraphQLString) } }, resolve: async (root: any, params: any, context: any) => { await requestLimiter(context.ip, params, "getInvoices", 1, "1s"); - const { lnd } = context; + + const lnd = getAuthLnd(params.auth); try { const invoiceList: InvoicesProps = await getLnInvoices({ diff --git a/src/schemas/query/invoices/payments.ts b/src/schemas/query/invoices/payments.ts index 285559bf..e39a4485 100644 --- a/src/schemas/query/invoices/payments.ts +++ b/src/schemas/query/invoices/payments.ts @@ -1,8 +1,9 @@ -import { GraphQLList } from "graphql"; +import { GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; import { getPayments as getLnPayments } from "ln-service"; import { logger } from "../../../helpers/logger"; import { requestLimiter } from "../../../helpers/rateLimiter"; import { GetPaymentType } from "../../../schemaTypes/query/info/payments"; +import { getAuthLnd } from "../../../helpers/helpers"; interface PaymentProps { created_at: string; @@ -25,9 +26,11 @@ interface PaymentsProps { export const getPayments = { type: new GraphQLList(GetPaymentType), + args: { auth: { type: new GraphQLNonNull(GraphQLString) } }, resolve: async (root: any, params: any, context: any) => { await requestLimiter(context.ip, params, "getPayments", 1, "1s"); - const { lnd } = context; + + const lnd = getAuthLnd(params.auth); try { const paymentList: PaymentsProps = await getLnPayments({ diff --git a/src/schemas/query/report/ForwardChannels.ts b/src/schemas/query/report/ForwardChannels.ts index 92ee1791..e5a4721c 100644 --- a/src/schemas/query/report/ForwardChannels.ts +++ b/src/schemas/query/report/ForwardChannels.ts @@ -1,4 +1,4 @@ -import { GraphQLString } from "graphql"; +import { GraphQLString, GraphQLNonNull } from "graphql"; import { getForwards as getLnForwards } from "ln-service"; import { logger } from "../../../helpers/logger"; import { requestLimiter } from "../../../helpers/rateLimiter"; @@ -7,16 +7,14 @@ import { countArray } from "./Helpers"; import { ForwardCompleteProps } from "./ForwardReport.interface"; import { ForwardChannelsType } from "../../../schemaTypes/query/report/ForwardChannels"; import { sortBy } from "underscore"; +import { getAuthLnd } from "../../../helpers/helpers"; export const getForwardChannelsReport = { type: ForwardChannelsType, args: { - time: { - type: GraphQLString - }, - order: { - type: GraphQLString - } + auth: { type: new GraphQLNonNull(GraphQLString) }, + time: { type: GraphQLString }, + order: { type: GraphQLString } }, resolve: async (root: any, params: any, context: any) => { await requestLimiter( @@ -26,7 +24,8 @@ export const getForwardChannelsReport = { 1, "1s" ); - const { lnd } = context; + + const lnd = getAuthLnd(params.auth); let startDate = new Date(); const endDate = new Date(); diff --git a/src/schemas/query/report/ForwardReport.ts b/src/schemas/query/report/ForwardReport.ts index c304c5ce..7b729a9b 100644 --- a/src/schemas/query/report/ForwardReport.ts +++ b/src/schemas/query/report/ForwardReport.ts @@ -1,4 +1,4 @@ -import { GraphQLString } from "graphql"; +import { GraphQLString, GraphQLNonNull } from "graphql"; import { getForwards as getLnForwards } from "ln-service"; import { logger } from "../../../helpers/logger"; import { requestLimiter } from "../../../helpers/rateLimiter"; @@ -11,17 +11,18 @@ import { } from "date-fns"; import { reduceForwardArray } from "./Helpers"; import { ForwardCompleteProps } from "./ForwardReport.interface"; +import { getAuthLnd } from "../../../helpers/helpers"; export const getForwardReport = { type: GraphQLString, args: { - time: { - type: GraphQLString - } + auth: { type: new GraphQLNonNull(GraphQLString) }, + time: { type: GraphQLString } }, resolve: async (root: any, params: any, context: any) => { await requestLimiter(context.ip, params, "getForwardReport", 1, "1s"); - const { lnd } = context; + + const lnd = getAuthLnd(params.auth); let startDate = new Date(); const endDate = new Date(); diff --git a/yarn.lock b/yarn.lock index 51a3415b..10d6124d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -987,6 +987,11 @@ base64-js@^1.0.2: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== +base64url@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"