} The payment's preimage.
@@ -498,7 +498,7 @@ const sendSpontaneousPayment = async (
feeLimit,
payment_request: orderResponse.response
})
- const myLndPub = LNDHealthMananger.lndPub
+ const myLndPub = LNDHealthManager.lndPub
if (
(opts.type !== 'contentReveal' &&
opts.type !== 'torrentSeed' &&
diff --git a/src/routes.js b/src/routes.js
index e53c3356..b179f5f8 100644
--- a/src/routes.js
+++ b/src/routes.js
@@ -1,9 +1,10 @@
/**
* @prettier
*/
+// @ts-check
'use strict'
-const Axios = require('axios')
+const Axios = require('axios').default
const Crypto = require('crypto')
const Storage = require('node-persist')
const logger = require('../config/log')
@@ -12,11 +13,11 @@ const responseTime = require('response-time')
const uuid = require('uuid/v4')
const Common = require('shock-common')
const isARealUsableNumber = require('lodash/isFinite')
-const Big = require('big.js')
-const size = require('lodash/size')
-const { range, flatten, evolve } = require('ramda')
+const Big = require('big.js').default
+const { evolve } = require('ramda')
const path = require('path')
const cors = require('cors')
+const ECCrypto = require('eccrypto')
const getListPage = require('../utils/paginate')
const auth = require('../services/auth/auth')
@@ -30,8 +31,6 @@ const {
nonEncryptedRoutes
} = require('../utils/protectedRoutes')
const GunActions = require('../services/gunDB/contact-api/actions')
-const GunGetters = require('../services/gunDB/contact-api/getters')
-const GunKey = require('../services/gunDB/contact-api/key')
const LV2 = require('../utils/lightningServices/v2')
const GunWriteRPC = require('../services/gunDB/rpc')
const Key = require('../services/gunDB/contact-api/key')
@@ -45,11 +44,17 @@ const SESSION_ID = uuid()
// module.exports = (app) => {
module.exports = async (
- app,
+ _app,
config,
mySocketsEvents,
- { serverPort, CA, CA_KEY, useTLS }
+ { serverPort, useTLS, CA, CA_KEY, runPrivateKey, runPublicKey, accessSecret }
) => {
+ /**
+ * @typedef {import('express').Application} Application
+ */
+
+ const app = /** @type {Application} */ (_app)
+
try {
const Http = Axios.create({
httpsAgent: new httpsAgent.Agent(
@@ -73,15 +78,78 @@ module.exports = async (
return message
}
+ const unlockWallet = password =>
+ new Promise((resolve, reject) => {
+ try {
+ const args = {
+ wallet_password: Buffer.from(password, 'utf-8')
+ }
+ const { walletUnlocker } = LightningServices.services
+ walletUnlocker.unlockWallet(args, (unlockErr, unlockResponse) => {
+ if (unlockErr) {
+ reject(unlockErr)
+ return
+ }
+
+ resolve(unlockResponse)
+ })
+ } catch (err) {
+ if (err.message === 'unknown service lnrpc.WalletUnlocker') {
+ resolve({
+ field: 'walletUnlocker',
+ message: 'Wallet already unlocked'
+ })
+ return
+ }
+
+ logger.error('Unlock Error:', err)
+
+ reject({
+ field: 'wallet',
+ code: err.code,
+ message: sanitizeLNDError(err.message)
+ })
+ }
+ })
+
const getAvailableService = () => {
return lndErrorManager.getAvailableService()
}
+ // Hack to check whether or not a wallet exists
+ const walletExists = async () => {
+ try {
+ const availableService = await getAvailableService()
+ if (availableService.service === 'lightning') {
+ return true
+ }
+
+ if (availableService.service === 'walletUnlocker') {
+ const randomPassword = Crypto.randomBytes(4).toString('hex')
+ try {
+ await unlockWallet(randomPassword)
+ return true
+ } catch (err) {
+ if (err.message.indexOf('invalid passphrase') > -1) {
+ return true
+ }
+ return false
+ }
+ }
+ } catch (err) {
+ logger.error('LND error:', err)
+ return false
+ }
+ }
+
const checkHealth = async () => {
let LNDStatus = {}
try {
const serviceStatus = await getAvailableService()
- LNDStatus = serviceStatus
+ LNDStatus = {
+ ...serviceStatus,
+ walletExists: await walletExists()
+ }
} catch (e) {
LNDStatus = {
message: e.message,
@@ -96,7 +164,8 @@ module.exports = async (
const APIStatus = {
message: APIHealth.data,
responseTime: APIHealth.headers['x-response-time'],
- success: true
+ success: true,
+ encryptionPublicKey: runPublicKey.toString('base64')
}
return {
LNDStatus,
@@ -108,7 +177,8 @@ module.exports = async (
const APIStatus = {
message: err?.response?.data,
responseTime: err?.response?.headers['x-response-time'],
- success: false
+ success: false,
+ encryptionPublicKey: runPublicKey.toString('base64')
}
logger.warn('Failed to retrieve API status', APIStatus)
return {
@@ -155,66 +225,6 @@ module.exports = async (
next()
}
- const unlockWallet = password =>
- new Promise((resolve, reject) => {
- try {
- const args = {
- wallet_password: Buffer.from(password, 'utf-8')
- }
- const { walletUnlocker } = LightningServices.services
- walletUnlocker.unlockWallet(args, (unlockErr, unlockResponse) => {
- if (unlockErr) {
- reject(unlockErr)
- return
- }
-
- resolve(unlockResponse)
- })
- } catch (err) {
- if (err.message === 'unknown service lnrpc.WalletUnlocker') {
- resolve({
- field: 'walletUnlocker',
- message: 'Wallet already unlocked'
- })
- return
- }
-
- logger.error('Unlock Error:', err)
-
- reject({
- field: 'wallet',
- code: err.code,
- message: sanitizeLNDError(err.message)
- })
- }
- })
-
- // Hack to check whether or not a wallet exists
- const walletExists = async () => {
- try {
- const availableService = await getAvailableService()
- if (availableService.service === 'lightning') {
- return true
- }
-
- if (availableService.service === 'walletUnlocker') {
- const randomPassword = Crypto.randomBytes(4).toString('hex')
- try {
- await unlockWallet(randomPassword)
- return true
- } catch (err) {
- if (err.message.indexOf('invalid passphrase') > -1) {
- return true
- }
- return false
- }
- }
- } catch (err) {
- logger.error('LND error:', err)
- return false
- }
- }
-
app.use(
cors({
credentials: true,
@@ -237,7 +247,7 @@ module.exports = async (
return next()
}
- if (!deviceId) {
+ if (typeof deviceId !== 'string' || !deviceId) {
const error = {
field: 'deviceId',
message: 'Please specify a device ID'
@@ -266,13 +276,15 @@ module.exports = async (
logger.info('Decrypting ECC message...')
- const decryptedMessage = await ECC.decryptMessage({
- deviceId,
- encryptedMessage: req.body
- })
+ const asBuffers = await ECC.convertToEncryptedMessage(req.body)
+
+ const decryptedMessage = await ECCrypto.decrypt(
+ runPrivateKey,
+ asBuffers
+ )
// eslint-disable-next-line
- req.body = JSON.parse(decryptedMessage)
+ req.body = JSON.parse(decryptedMessage.toString('utf8'))
return next()
} catch (err) {
@@ -301,7 +313,7 @@ module.exports = async (
} else {
try {
const response = await auth.validateToken(
- req.headers.authorization.replace('Bearer ', '')
+ (req.headers.authorization || '').replace('Bearer ', '')
)
if (response.valid) {
next()
@@ -442,12 +454,28 @@ module.exports = async (
app.post('/api/encryption/exchange', async (req, res) => {
try {
- const { publicKey, deviceId } = req.body
+ let { publicKey, deviceId } = req.body
- if (!publicKey) {
- return res.status(400).json({
+ if (Buffer.isBuffer(accessSecret)) {
+ logger.info('Will decrypt public key and device ID for key exchange.')
+
+ publicKey = await ECCrypto.decrypt(
+ accessSecret,
+ ECC.convertToEncryptedMessage(publicKey)
+ )
+ deviceId = await ECCrypto.decrypt(
+ accessSecret,
+ ECC.convertToEncryptedMessage(deviceId)
+ )
+
+ publicKey = publicKey.toString('utf8')
+ deviceId = deviceId.toString('utf8')
+ }
+
+ if (typeof publicKey !== 'string' || !publicKey) {
+ return res.status(500).json({
field: 'publicKey',
- message: 'Please provide a valid public key'
+ errorMessage: 'Please provide a valid public key'
})
}
@@ -457,22 +485,23 @@ module.exports = async (
deviceId
)
) {
- return res.status(400).json({
+ return res.status(500).json({
field: 'deviceId',
- message: 'Please provide a valid device ID'
+ errorMessage: 'Please provide a valid device ID'
})
}
- const authorizedDevice = await ECC.authorizeDevice({
+ await ECC.authorizeDevice({
deviceId,
publicKey
})
- return res.json(authorizedDevice)
+ res.sendStatus(200)
} catch (err) {
logger.error(err)
return res.status(401).json({
field: 'unknown',
- message: err
+ message: err,
+ errorMessage: err.message || err
})
}
})
@@ -507,240 +536,194 @@ module.exports = async (
}
}
+ /**
+ * Get the latest channel backups before subscribing.
+ */
+ const saveChannelsBackup = async () => {
+ const { getUser } = require('../services/gunDB/Mediator')
+ const { lightning } = LightningServices.services
+ const SEA = require('../services/gunDB/Mediator').mySEA
+ await Common.Utils.makePromise((res, rej) => {
+ lightning.exportAllChannelBackups({}, (err, channelBackups) => {
+ if (err) {
+ return rej(new Error(err.details))
+ }
+
+ res(
+ GunActions.saveChannelsBackup(
+ JSON.stringify(channelBackups),
+ getUser(),
+ SEA
+ )
+ )
+ })
+ })
+ }
+
+ /**
+ * Register to listen for channel backups.
+ */
+ const onNewChannelBackup = () => {
+ const { getUser } = require('../services/gunDB/Mediator')
+ const { lightning } = LightningServices.services
+ const SEA = require('../services/gunDB/Mediator').mySEA
+
+ logger.warn('Subscribing to channel backup ...')
+
+ const stream = lightning.SubscribeChannelBackups({})
+
+ stream.on('data', data => {
+ logger.info(' New channel backup data')
+ GunActions.saveChannelsBackup(JSON.stringify(data), getUser(), SEA)
+ })
+ stream.on('end', () => {
+ logger.info('Channel backup stream ended, starting a new one...')
+ // Prevents call stack overflow exceptions
+ //process.nextTick(onNewChannelBackup)
+ })
+ stream.on('error', err => {
+ logger.error('Channel backup stream error:', err)
+ })
+ stream.on('status', status => {
+ logger.error('Channel backup stream status:', status)
+ switch (status.code) {
+ case 0: {
+ logger.info('Channel backup stream ok')
+ break
+ }
+ case 2: {
+ //Happens to fire when the grpc client lose access to macaroon file
+ logger.warn('Channel backup got UNKNOWN error status')
+ break
+ }
+ case 12: {
+ logger.warn(
+ 'Channel backup LND locked, new registration in 60 seconds'
+ )
+ process.nextTick(() =>
+ setTimeout(() => onNewChannelBackup(), 60000)
+ )
+ break
+ }
+ case 13: {
+ //https://grpc.github.io/grpc/core/md_doc_statuscodes.html
+ logger.error('Channel backup INTERNAL LND error')
+ break
+ }
+ case 14: {
+ logger.error(
+ 'Channel backup LND disconnected, sockets reconnecting in 30 seconds...'
+ )
+ process.nextTick(() =>
+ setTimeout(() => onNewChannelBackup(), 30000)
+ )
+ break
+ }
+ default: {
+ logger.error('[event:transaction:new] UNKNOWN LND error')
+ }
+ }
+ })
+ }
+
app.post('/api/lnd/auth', async (req, res) => {
try {
const health = await checkHealth()
const walletInitialized = await walletExists()
- // If we're connected to lnd, unlock the wallet using the password supplied
- // and generate an auth token if that operation was successful.
- if (health.LNDStatus.success && walletInitialized) {
- const { alias, password, invite, accessSecret } = req.body
+ const { alias, pass } = req.body
+ const lndUp = health.LNDStatus.success
+ const walletUnlocked = health.LNDStatus.walletStatus === 'unlocked'
+ const { authorization = '' } = req.headers
+ const allowUnlockedLND = process.env.ALLOW_UNLOCKED_LND === 'true'
+ const { lightning } = LightningServices.services
- await recreateLnServices()
-
- if (GunDB.isAuthenticated()) {
- GunDB.logoff()
- }
-
- const publicKey = await GunDB.authenticate(alias, password)
-
- if (!publicKey) {
- res.status(401).json({
- field: 'alias',
- errorMessage: 'Invalid alias/password combination',
- success: false
- })
- return false
- }
-
- const trustedKeysEnabled =
- process.env.TRUSTED_KEYS === 'true' || !process.env.TRUSTED_KEYS
- const trustedKeys = await Storage.get('trustedPKs')
- // Falls back to true if trusted keys is disabled in .env
- const [isKeyTrusted = !trustedKeysEnabled] = (
- trustedKeys || []
- ).filter(trustedKey => trustedKey === publicKey)
- const walletUnlocked = health.LNDStatus.walletStatus === 'unlocked'
- const { authorization = '' } = req.headers
-
- if (!isKeyTrusted) {
- logger.warn('Untrusted public key!')
- }
-
- if (!walletUnlocked) {
- await unlockWallet(password)
- }
- let secretUsed = null
- if (accessSecret) {
- secretUsed = await Storage.get(
- `UnlockedAccessSecrets/${accessSecret}`
- )
- }
- if (
- walletUnlocked &&
- !authorization &&
- !isKeyTrusted &&
- (process.env.ALLOW_UNLOCKED_LND !== 'true' || secretUsed !== false)
- ) {
- res.status(401).json({
- field: 'alias',
- errorMessage:
- 'Invalid alias/password combination (Untrusted Device)',
- success: false
- })
- return
- }
-
- if (
- walletUnlocked &&
- !isKeyTrusted &&
- (process.env.ALLOW_UNLOCKED_LND !== 'true' || secretUsed !== false)
- ) {
- const validatedToken = await validateToken(
- authorization.replace('Bearer ', '')
- )
-
- if (!validatedToken) {
- res.status(401).json({
- field: 'alias',
- errorMessage:
- 'Invalid alias/password combination (Untrusted Auth Token)',
- success: false
- })
- return
- }
- }
-
- if (secretUsed === false) {
- await Storage.setItem(`UnlockedAccessSecrets/${accessSecret}`, true)
- }
-
- if (!isKeyTrusted) {
- await Storage.set('trustedPKs', [...(trustedKeys || []), publicKey])
- }
-
- const { lightning } = LightningServices.services
-
- // Generate auth token and send it as a JSON response
- const token = await auth.generateToken()
-
- // wait for wallet to warm up
- await Common.Utils.makePromise((res, rej) => {
- let tries = 0
- let intervalID = null
-
- intervalID = setInterval(() => {
- if (tries === 7) {
- rej(new Error(`Wallet did not warm up in under 7 seconds.`))
-
- clearInterval(intervalID)
- return
- }
-
- tries++
-
- lightning.listInvoices({}, err => {
- if (!err) {
- clearInterval(intervalID)
- res()
- }
- })
- }, 1000)
- })
-
- //get the latest channel backups before subscribing
- const user = require('../services/gunDB/Mediator').getUser()
- const SEA = require('../services/gunDB/Mediator').mySEA
-
- await Common.Utils.makePromise((res, rej) => {
- lightning.exportAllChannelBackups({}, (err, channelBackups) => {
- if (err) {
- return rej(new Error(err.details))
- }
-
- res(
- GunActions.saveChannelsBackup(
- JSON.stringify(channelBackups),
- user,
- SEA
- )
- )
- })
- })
-
- // Send an event to update lightning's status
- mySocketsEvents.emit('updateLightning')
-
- //register to listen for channel backups
- const onNewChannelBackup = () => {
- logger.warn('Subscribing to channel backup ...')
- const stream = lightning.SubscribeChannelBackups({})
- stream.on('data', data => {
- logger.info(' New channel backup data')
- GunActions.saveChannelsBackup(JSON.stringify(data), user, SEA)
- })
- stream.on('end', () => {
- logger.info('Channel backup stream ended, starting a new one...')
- // Prevents call stack overflow exceptions
- //process.nextTick(onNewChannelBackup)
- })
- stream.on('error', err => {
- logger.error('Channel backup stream error:', err)
- })
- stream.on('status', status => {
- logger.error('Channel backup stream status:', status)
- switch (status.code) {
- case 0: {
- logger.info('Channel backup stream ok')
- break
- }
- case 2: {
- //Happens to fire when the grpc client lose access to macaroon file
- logger.warn('Channel backup got UNKNOWN error status')
- break
- }
- case 12: {
- logger.warn(
- 'Channel backup LND locked, new registration in 60 seconds'
- )
- process.nextTick(() =>
- setTimeout(() => onNewChannelBackup(), 60000)
- )
- break
- }
- case 13: {
- //https://grpc.github.io/grpc/core/md_doc_statuscodes.html
- logger.error('Channel backup INTERNAL LND error')
- break
- }
- case 14: {
- logger.error(
- 'Channel backup LND disconnected, sockets reconnecting in 30 seconds...'
- )
- process.nextTick(() =>
- setTimeout(() => onNewChannelBackup(), 30000)
- )
- break
- }
- default: {
- logger.error('[event:transaction:new] UNKNOWN LND error')
- }
- }
- })
- }
-
- onNewChannelBackup()
-
- setTimeout(() => {
- channelRequest(invite)
- }, 30 * 1000)
- res.json({
- authorization: token,
- user: {
- alias,
- publicKey
- }
- })
-
- return true
+ if (!lndUp) {
+ throw new Error(health.LNDStatus.message)
}
if (!walletInitialized) {
- res.status(500).json({
- field: 'wallet',
- errorMessage: 'Please create a wallet before authenticating',
- success: false
- })
- return false
+ throw new Error('Please create a wallet before authenticating')
}
- res.status(500)
- res.json({
- field: 'health',
- errorMessage: sanitizeLNDError(health.LNDStatus.message),
- success: false
+ await recreateLnServices()
+
+ if (GunDB.isAuthenticated()) {
+ GunDB.logoff()
+ }
+
+ const publicKey = await GunDB.authenticate(alias, pass)
+
+ if (!publicKey) {
+ throw new Error('Invalid alias/password combination')
+ }
+
+ if (!walletUnlocked) {
+ await unlockWallet(pass)
+ }
+
+ if (walletUnlocked && !authorization && !allowUnlockedLND) {
+ throw new Error(
+ 'Invalid alias/password combination (Untrusted Device)'
+ )
+ }
+
+ if (walletUnlocked && !allowUnlockedLND) {
+ const validatedToken = await validateToken(
+ authorization.replace('Bearer ', '')
+ )
+
+ if (!validatedToken) {
+ throw new Error(
+ 'Invalid alias/password combination (Untrusted Auth Token)'
+ )
+ }
+ }
+
+ // Generate auth token and send it as a JSON response
+ const token = await auth.generateToken()
+
+ // wait for wallet to warm up
+ await Common.Utils.makePromise((res, rej) => {
+ let tries = 0
+ let intervalID = null
+
+ intervalID = setInterval(() => {
+ if (tries === 7) {
+ rej(new Error(`Wallet did not warm up in under 7 seconds.`))
+
+ clearInterval(intervalID)
+ return
+ }
+
+ tries++
+
+ lightning.listInvoices({}, err => {
+ if (!err) {
+ clearInterval(intervalID)
+ res()
+ }
+ })
+ }, 1000)
+ })
+
+ saveChannelsBackup()
+
+ // Send an event to update lightning's status
+ mySocketsEvents.emit('updateLightning')
+
+ onNewChannelBackup()
+
+ setTimeout(() => {
+ channelRequest()
+ }, 30 * 1000)
+
+ res.json({
+ authorization: token,
+ user: {
+ alias,
+ publicKey
+ }
})
- return false
} catch (err) {
logger.error('Unlock Error:', err)
res.status(400)
@@ -756,8 +739,10 @@ module.exports = async (
app.post('/api/lnd/wallet', async (req, res) => {
try {
const { walletUnlocker } = LightningServices.services
- const { password, alias, invite } = req.body
+ const { password, alias } = req.body
const healthResponse = await checkHealth()
+ const isUnlocked = healthResponse.LNDStatus.service !== 'walletUnlocker'
+
if (!alias) {
return res.status(400).json({
field: 'alias',
@@ -780,139 +765,108 @@ module.exports = async (
})
}
- if (healthResponse.LNDStatus.service !== 'walletUnlocker') {
- return res.status(400).json({
- field: 'wallet',
- errorMessage: 'Wallet is already unlocked'
- })
+ if (isUnlocked) {
+ throw new Error('Wallet is already unlocked')
}
- walletUnlocker.genSeed({}, async (genSeedErr, genSeedResponse) => {
- try {
- if (genSeedErr) {
- logger.debug('GenSeed Error:', genSeedErr)
+ const [genSeedErr, genSeedResponse] = await new Promise(res => {
+ walletUnlocker.genSeed({}, (_genSeedErr, _genSeedResponse) => {
+ res([_genSeedErr, _genSeedResponse])
+ })
+ })
- const healthResponse = await checkHealth()
- if (healthResponse.LNDStatus.success) {
- const message = genSeedErr.details
- return res.status(400).json({
- field: 'GenSeed',
- errorMessage: message,
- success: false
- })
+ if (genSeedErr) {
+ logger.debug('GenSeed Error:', genSeedErr)
+
+ const healthResponse = await checkHealth()
+ if (healthResponse.LNDStatus.success) {
+ const message = genSeedErr.details
+ throw new Error(message)
+ }
+
+ throw new Error('LND is down')
+ }
+
+ logger.debug('GenSeed:', genSeedResponse)
+
+ const mnemonicPhrase = genSeedResponse.cipher_seed_mnemonic
+ const walletArgs = {
+ wallet_password: Buffer.from(password, 'utf8'),
+ cipher_seed_mnemonic: mnemonicPhrase
+ }
+
+ // Register user before creating wallet
+ const publicKey = await GunDB.register(alias, password)
+
+ await GunActions.saveSeedBackup(
+ mnemonicPhrase,
+ GunDB.getUser(),
+ GunDB.mySEA
+ )
+
+ const [initWalletErr, initWalletResponse] = await new Promise(res => {
+ walletUnlocker.initWallet(
+ walletArgs,
+ (_initWalletErr, _initWalletResponse) => {
+ res([_initWalletErr, _initWalletResponse])
+ }
+ )
+ })
+
+ if (initWalletErr) {
+ logger.error('initWallet Error:', initWalletErr.message)
+ const healthResponse = await checkHealth()
+ if (healthResponse.LNDStatus.success) {
+ const errorMessage = initWalletErr.details
+
+ throw new Error(errorMessage)
+ }
+ throw new Error('LND is down')
+ }
+
+ logger.info('initWallet:', initWalletResponse)
+
+ const waitUntilFileExists = seconds => {
+ logger.info(
+ `Waiting for admin.macaroon to be created. Seconds passed: ${seconds} Path: ${LightningServices.servicesConfig.macaroonPath}`
+ )
+ setTimeout(async () => {
+ try {
+ const macaroonExists = await FS.access(
+ LightningServices.servicesConfig.macaroonPath
+ )
+
+ if (!macaroonExists) {
+ return waitUntilFileExists(seconds + 1)
}
- return res.status(500).json({
- field: 'health',
- errorMessage: 'LND is down',
- success: false
+ logger.info('admin.macaroon file created')
+
+ await LightningServices.init()
+
+ const token = await auth.generateToken()
+ setTimeout(() => {
+ channelRequest()
+ }, 30 * 1000)
+ return res.json({
+ mnemonicPhrase,
+ authorization: token,
+ user: {
+ alias,
+ publicKey
+ }
+ })
+ } catch (err) {
+ logger.error(err)
+ res.status(500).json({
+ field: 'unknown',
+ errorMessage: sanitizeLNDError(err.message)
})
}
+ }, 1000)
+ }
- logger.debug('GenSeed:', genSeedResponse)
- const mnemonicPhrase = genSeedResponse.cipher_seed_mnemonic
- const walletArgs = {
- wallet_password: Buffer.from(password, 'utf8'),
- cipher_seed_mnemonic: mnemonicPhrase
- }
-
- // Register user before creating wallet
- const publicKey = await GunDB.register(alias, password)
-
- await GunActions.saveSeedBackup(
- mnemonicPhrase,
- GunDB.getUser(),
- GunDB.mySEA
- )
-
- const trustedKeys = await Storage.get('trustedPKs')
- await Storage.setItem('trustedPKs', [
- ...(trustedKeys || []),
- publicKey
- ])
-
- walletUnlocker.initWallet(
- walletArgs,
- async (initWalletErr, initWalletResponse) => {
- try {
- if (initWalletErr) {
- logger.error('initWallet Error:', initWalletErr.message)
- const healthResponse = await checkHealth()
- if (healthResponse.LNDStatus.success) {
- const errorMessage = initWalletErr.details
-
- return res.status(400).json({
- field: 'initWallet',
- errorMessage,
- success: false
- })
- }
- return res.status(500).json({
- field: 'health',
- errorMessage: 'LND is down',
- success: false
- })
- }
- logger.info('initWallet:', initWalletResponse)
-
- const waitUntilFileExists = seconds => {
- logger.info(
- `Waiting for admin.macaroon to be created. Seconds passed: ${seconds} Path: ${LightningServices.servicesConfig.macaroonPath}`
- )
- setTimeout(async () => {
- try {
- const macaroonExists = await FS.access(
- LightningServices.servicesConfig.macaroonPath
- )
-
- if (!macaroonExists) {
- return waitUntilFileExists(seconds + 1)
- }
-
- logger.info('admin.macaroon file created')
-
- await LightningServices.init()
-
- const token = await auth.generateToken()
- setTimeout(() => {
- channelRequest(invite)
- }, 30 * 1000)
- return res.json({
- mnemonicPhrase,
- authorization: token,
- user: {
- alias,
- publicKey
- }
- })
- } catch (err) {
- logger.error(err)
- res.status(400).json({
- field: 'unknown',
- errorMessage: sanitizeLNDError(err.message)
- })
- }
- }, 1000)
- }
-
- waitUntilFileExists(1)
- } catch (err) {
- logger.error(err)
- return res.status(500).json({
- field: 'unknown',
- errorMessage: err
- })
- }
- }
- )
- } catch (err) {
- logger.error(err)
- return res.status(500).json({
- field: 'unknown',
- errorMessage: err.message || err
- })
- }
- })
+ waitUntilFileExists(1)
} catch (err) {
logger.error(err)
return res.status(500).json({
@@ -924,14 +878,14 @@ module.exports = async (
app.post('/api/lnd/wallet/existing', async (req, res) => {
try {
- const { password, alias, invite, accessSecret } = req.body
+ const { password, alias } = req.body
const healthResponse = await checkHealth()
const exists = await walletExists()
+ const allowUnlockedLND = process.env.ALLOW_UNLOCKED_LND === 'true'
+ const isLocked = healthResponse.LNDStatus.service === 'walletUnlocker'
+
if (!exists) {
- return res.status(500).json({
- field: 'wallet',
- errorMessage: 'LND wallet does not exist, please create a new one'
- })
+ throw new Error('LND wallet does not exist, please create a new one')
}
if (!alias) {
@@ -955,47 +909,28 @@ module.exports = async (
"Please specify a password that's longer than 8 characters"
})
}
- let secretUsed = null
- if (accessSecret) {
- secretUsed = await Storage.get(
- `UnlockedAccessSecrets/${accessSecret}`
+
+ if (!isLocked && !allowUnlockedLND) {
+ throw new Error(
+ 'Wallet is already unlocked. Please restart your LND instance and try again.'
)
}
- if (
- healthResponse.LNDStatus.service !== 'walletUnlocker' &&
- (process.env.ALLOW_UNLOCKED_LND !== 'true' || secretUsed !== false)
- ) {
- return res.status(400).json({
- field: 'wallet',
- errorMessage:
- 'Wallet is already unlocked. Please restart your LND instance and try again.'
- })
- }
- if (secretUsed === false) {
- await Storage.setItem(`UnlockedAccessSecrets/${accessSecret}`, true)
- }
try {
- if (healthResponse.LNDStatus.service === 'walletUnlocker') {
+ if (isLocked) {
await unlockWallet(password)
}
- } catch (err) {
- return res.status(401).json({
- field: 'wallet',
- errorMessage: 'Invalid LND wallet password'
- })
+ } catch (_) {
+ throw new Error('Invalid LND wallet password')
}
// Register user after verifying wallet password
const publicKey = await GunDB.register(alias, password)
- const trustedKeys = await Storage.get('trustedPKs')
- await Storage.setItem('trustedPKs', [...(trustedKeys || []), publicKey])
-
// Generate Access Token
const token = await auth.generateToken()
setTimeout(() => {
- channelRequest(invite)
+ channelRequest()
}, 30 * 1000)
res.json({
authorization: token,
@@ -1193,6 +1128,12 @@ module.exports = async (
app.get('/api/lnd/unifiedTrx', (req, res) => {
const { lightning } = LightningServices.services
const { itemsPerPage, page, reversed = true } = req.query
+ if (typeof itemsPerPage !== 'number') {
+ throw new TypeError('itemsPerPage not a number')
+ }
+ if (typeof page !== 'number') {
+ throw new TypeError('page not a number')
+ }
const offset = (page - 1) * itemsPerPage
lightning.listPayments({}, (err, { payments = [] } = {}) => {
if (err) {
@@ -1307,8 +1248,15 @@ module.exports = async (
app.get('/api/lnd/listpayments', (req, res) => {
const { lightning } = LightningServices.services
const { itemsPerPage, page, paginate = true } = req.query
+ if (typeof itemsPerPage !== 'number') {
+ throw new TypeError('itemsPerPage not a number')
+ }
+ if (typeof page !== 'number') {
+ throw new TypeError('page not a number')
+ }
lightning.listPayments(
{
+ // TODO
include_incomplete: !!req.include_incomplete
},
(err, { payments = [] } = {}) => {
@@ -1340,7 +1288,8 @@ module.exports = async (
max_payments: x => Number(x),
reversed: x => x === 'true'
},
- req.query
+ // TODO Validate
+ /** @type {any} */ (req.query)
))
if (typeof include_incomplete !== 'boolean') {
@@ -1385,11 +1334,23 @@ module.exports = async (
app.get('/api/lnd/listinvoices', (req, res) => {
const { lightning } = LightningServices.services
const { page, itemsPerPage, reversed = true } = req.query
+ if (typeof itemsPerPage !== 'number') {
+ throw new TypeError('itemsPerPage not a number')
+ }
+ if (typeof page !== 'number') {
+ throw new TypeError('page not a number')
+ }
const offset = (page - 1) * itemsPerPage
// const limit = page * itemsPerPage;
lightning.listInvoices(
{ reversed, index_offset: offset, num_max_invoices: itemsPerPage },
- async (err, { invoices, last_index_offset } = {}) => {
+ async (
+ err,
+ { invoices, last_index_offset } = {
+ invoices: [],
+ last_index_offset: 1
+ }
+ ) => {
if (err) {
logger.debug('ListInvoices Error:', err)
const health = await checkHealth()
@@ -1963,6 +1924,12 @@ module.exports = async (
app.get('/api/lnd/transactions', (req, res) => {
const { lightning } = LightningServices.services
const { page, paginate = true, itemsPerPage } = req.query
+ if (typeof page !== 'number') {
+ throw new TypeError('page is not a number')
+ }
+ if (typeof itemsPerPage !== 'number') {
+ throw new TypeError('itemsPerPage is not a number')
+ }
lightning.getTransactions({}, (err, { transactions = [] } = {}) => {
if (err) {
return handleError(res, err)
@@ -1994,6 +1961,10 @@ module.exports = async (
app.get('/api/lnd/closedchannels', (req, res) => {
const { lightning } = LightningServices.services
const { closeTypeFilters = [] } = req.query
+ if (!Array.isArray(closeTypeFilters)) {
+ throw new TypeError('closeTypeFilters not an Array')
+ }
+ // @ts-expect-error I dunno what's going on here, all arrays have reduce()
const lndFilters = closeTypeFilters.reduce(
(filters, filter) => ({ ...filters, [filter]: true }),
{}
@@ -2033,8 +2004,6 @@ module.exports = async (
})
})
- const GunEvent = Common.Constants.Event
-
app.get('/api/gun/lndchanbackups', async (req, res) => {
try {
const user = require('../services/gunDB/Mediator').getUser()
@@ -2042,6 +2011,11 @@ module.exports = async (
const SEA = require('../services/gunDB/Mediator').mySEA
const mySecret = require('../services/gunDB/Mediator').getMySecret()
const encBackup = await user.get(Key.CHANNELS_BACKUP).then()
+ if (typeof encBackup !== 'string') {
+ throw new TypeError(
+ 'Encrypted backup fetched from gun not an string.'
+ )
+ }
const backup = await SEA.decrypt(encBackup, mySecret)
logger.info(backup)
res.json({ data: backup })
@@ -2062,23 +2036,23 @@ module.exports = async (
feeLimit,
sessionUuid
} = req.body
- logger.info('handling spont pay')
+ logger.info('handling spontaneous pay')
if (!feeLimit) {
logger.error(
- 'please provide a "feeLimit" to the send spont payment request'
+ 'please provide a "feeLimit" to the send spontaneous payment request'
)
return res.status(500).json({
errorMessage:
- 'please provide a "feeLimit" to the send spont payment request'
+ 'please provide a "feeLimit" to the send spontaneous payment request'
})
}
if (!recipientPub || !amount) {
logger.info(
- 'please provide a "recipientPub" and "amount" to the send spont payment request'
+ 'please provide a "recipientPub" and "amount" to the send spontaneous payment request'
)
return res.status(500).json({
errorMessage:
- 'please provide a "recipientPub" and "amount" to the send spont payment request'
+ 'please provide a "recipientPub" and "amount" to the send spontaneous payment request'
})
}
try {
@@ -2087,12 +2061,13 @@ module.exports = async (
amount,
memo,
feeLimit,
+ // TODO
maxParts,
timeoutSeconds
)
res.json({ preimage, sessionUuid })
} catch (err) {
- logger.info('spont pay err:', err)
+ logger.info('spontaneous pay err:', err)
return res.status(500).json({
errorMessage: err.message
})
@@ -2102,12 +2077,10 @@ module.exports = async (
app.post(`/api/gun/wall/`, async (req, res) => {
try {
const { tags, title, contentItems, enableTipsOverlay } = req.body
- const SEA = require('../services/gunDB/Mediator').mySEA
const postRes = await GunActions.createPostNew(
tags,
title,
- contentItems,
- SEA
+ contentItems
)
if (enableTipsOverlay) {
const [postID] = postRes
@@ -2150,8 +2123,6 @@ module.exports = async (
* @typedef {import('express-serve-static-core').RequestHandler} RequestHandler
*/
- const ap = /** @type {Application} */ (app)
-
/**
* @typedef {object} FollowsRouteParams
* @prop {(string|undefined)=} publicKey
@@ -2167,14 +2138,14 @@ module.exports = async (
throw new Error(`Missing publicKey route param.`)
}
- await GunActions.follow(req.params.publicKey, false)
+ await GunActions.follow(publicKey, false)
// 201 would be extraneous here. Implement it inside app.put
- return res.status(200).json({
+ res.status(200).json({
ok: true
})
} catch (err) {
- return res.status(500).json({
+ res.status(500).json({
errorMessage: err.message || 'Unknown error inside /api/gun/follows/'
})
}
@@ -2190,19 +2161,19 @@ module.exports = async (
throw new Error(`Missing publicKey route param.`)
}
- await GunActions.unfollow(req.params.publicKey)
+ await GunActions.unfollow(publicKey)
- return res.status(200).json({
+ res.status(200).json({
ok: true
})
} catch (err) {
- return res.status(500).json({
+ res.status(500).json({
errorMessage: err.message || 'Unknown error inside /api/gun/follows/'
})
}
}
- ap.get('/api/gun/initwall', async (req, res) => {
+ app.get('/api/gun/initwall', async (req, res) => {
try {
await GunActions.initWall()
res.json({ ok: true })
@@ -2213,8 +2184,8 @@ module.exports = async (
})
}
})
- ap.put(`/api/gun/follows/:publicKey`, apiGunFollowsPut)
- ap.delete(`/api/gun/follows/:publicKey`, apiGunFollowsDelete)
+ app.put(`/api/gun/follows/:publicKey`, apiGunFollowsPut)
+ app.delete(`/api/gun/follows/:publicKey`, apiGunFollowsDelete)
/**
* @type {RequestHandler<{}>}
@@ -2258,20 +2229,20 @@ module.exports = async (
await GunActions.generateHandshakeAddress()
}
- return res.status(200).json({
+ res.status(200).json({
ok: true
})
} catch (err) {
logger.error(err)
- return res.status(500).json({
+ res.status(500).json({
errorMessage: err.message
})
}
}
- ap.put(`/api/gun/me`, apiGunMePut)
+ app.put(`/api/gun/me`, apiGunMePut)
- ap.get(`/api/gun/auth`, (_, res) => {
+ app.get(`/api/gun/auth`, (_, res) => {
const { isAuthenticated } = require('../services/gunDB/Mediator')
return res.status(200).json({
@@ -2329,7 +2300,6 @@ module.exports = async (
}
if (type === 'once') node.once(listener)
- if (type === 'load') node.load(listener)
if (type === 'specialOnce') node.specialOnce(listener)
})
}
@@ -2343,7 +2313,7 @@ module.exports = async (
*/
const EPUB_FOR_DECRYPT_HEADER = 'epub-for-decryption'
- ap.get('/api/gun/once/:path', async (req, res) => {
+ app.get('/api/gun/once/:path', async (req, res) => {
try {
const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER)
const epubForDecryption = req.header(EPUB_FOR_DECRYPT_HEADER)
@@ -2367,7 +2337,7 @@ module.exports = async (
}
})
- ap.get('/api/gun/specialOnce/:path', async (req, res) => {
+ app.get('/api/gun/specialOnce/:path', async (req, res) => {
try {
const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER)
const epubForDecryption = req.header(EPUB_FOR_DECRYPT_HEADER)
@@ -2391,7 +2361,7 @@ module.exports = async (
}
})
- ap.get('/api/gun/load/:path', async (req, res) => {
+ app.get('/api/gun/load/:path', async (req, res) => {
try {
const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER)
const epubForDecryption = req.header(EPUB_FOR_DECRYPT_HEADER)
@@ -2415,7 +2385,7 @@ module.exports = async (
}
})
- ap.get('/api/gun/user/once/:path', async (req, res) => {
+ app.get('/api/gun/user/once/:path', async (req, res) => {
try {
const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER)
const epubForDecryption = req.header(EPUB_FOR_DECRYPT_HEADER)
@@ -2439,7 +2409,7 @@ module.exports = async (
}
})
- ap.get('/api/gun/user/specialOnce/:path', async (req, res) => {
+ app.get('/api/gun/user/specialOnce/:path', async (req, res) => {
try {
const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER)
const epubForDecryption = req.header(EPUB_FOR_DECRYPT_HEADER)
@@ -2463,7 +2433,7 @@ module.exports = async (
}
})
- ap.get('/api/gun/user/load/:path', async (req, res) => {
+ app.get('/api/gun/user/load/:path', async (req, res) => {
try {
const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER)
const epubForDecryption = req.header(EPUB_FOR_DECRYPT_HEADER)
@@ -2487,9 +2457,9 @@ module.exports = async (
}
})
- ap.get('/api/gun/otheruser/:publicKey/:type/:path', async (req, res) => {
+ app.get('/api/gun/otheruser/:publicKey/:type/:path', async (req, res) => {
try {
- const allowedTypes = ['once', 'load', 'open', 'specialOnce']
+ const allowedTypes = ['once', 'open', 'specialOnce']
const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER)
const epubForDecryption = req.header(EPUB_FOR_DECRYPT_HEADER)
const { path /*:rawPath*/, publicKey, type } = req.params
@@ -2511,6 +2481,7 @@ module.exports = async (
const data = await handleGunFetch({
path,
startFromUserGraph: false,
+ // @ts-expect-error Validated above
type,
publicKey,
publicKeyForDecryption,
@@ -2537,7 +2508,7 @@ module.exports = async (
}
})
- ap.post('/api/lnd/cb/:methodName', (req, res) => {
+ app.post('/api/lnd/cb/:methodName', (req, res) => {
try {
const { lightning } = LightningServices.services
const { methodName } = req.params
@@ -2568,7 +2539,7 @@ module.exports = async (
}
})
- ap.post('/api/gun/put', async (req, res) => {
+ app.post('/api/gun/put', async (req, res) => {
try {
const { path, value } = req.body
logger.info(`gun PUT: ${path}`)
@@ -2589,7 +2560,7 @@ module.exports = async (
}
})
- ap.post('/api/gun/set', async (req, res) => {
+ app.post('/api/gun/set', async (req, res) => {
try {
const { path, value } = req.body
logger.info(`gun SET: ${path}`)
@@ -2611,14 +2582,16 @@ module.exports = async (
}
})
- ap.get('/api/log', async (_, res) => {
+ app.get('/api/log', async (_, res) => {
try {
// https://github.com/winstonjs/winston#querying-logs
/**
* @type {import('winston').QueryOptions}
*/
const options = {
- from: new Date() - 1 * 60 * 60 * 1000,
+ // @ts-expect-error Winston's typings don't account for supporting
+ // numbers here.
+ from: (new Date()).valueOf() - 1 * 60 * 60 * 1000,
until: new Date()
}
@@ -2643,7 +2616,7 @@ module.exports = async (
}
})
//this is for OBS notifications, not wired with UI.
- ap.get('/api/subscribeStream', (req, res) => {
+ app.get('/api/subscribeStream', (req, res) => {
try {
res.sendFile(path.join(__dirname, '../public/obsOverlay.html'))
} catch (e) {
@@ -2653,7 +2626,7 @@ module.exports = async (
})
}
})
- ap.post('/api/enableNotificationsOverlay', (req, res) => {
+ app.post('/api/enableNotificationsOverlay', (req, res) => {
try {
const { postID } = req.body
if (!postID) {
@@ -2673,7 +2646,7 @@ module.exports = async (
}
})
//this is for wasLive/isLive status
- ap.post('/api/listenStream', (req, res) => {
+ app.post('/api/listenStream', (req, res) => {
try {
startedStream(req.body)
return res.status(200).json({
@@ -2687,7 +2660,7 @@ module.exports = async (
})
}
})
- ap.post('/api/stopStream', (req, res) => {
+ app.post('/api/stopStream', (req, res) => {
try {
endStream(req.body)
return res.status(200).json({
@@ -2702,7 +2675,7 @@ module.exports = async (
}
})
- ap.get('/', (req, res) => {
+ app.get('/', (req, res) => {
try {
res.sendFile(path.join(__dirname, '../public/localHomepage.html'))
} catch (e) {
@@ -2713,8 +2686,8 @@ module.exports = async (
}
})
- ap.get('/qrCodeGenerator', (req, res) => {
- console.log('qrrerrr')
+ app.get('/qrCodeGenerator', (req, res) => {
+ console.log('/qrCodeGenerator')
try {
res.sendFile(path.join(__dirname, '../public/qrcode.min.js'))
} catch (e) {
@@ -2725,7 +2698,7 @@ module.exports = async (
}
})
- ap.get('/api/accessInfo', async (req, res) => {
+ app.get('/api/accessInfo', async (req, res) => {
if (req.ip !== '127.0.0.1') {
res.json({
field: 'origin',
@@ -2778,7 +2751,7 @@ module.exports = async (
}
})
- ap.post('/api/initUserInformation', async (req, res) => {
+ app.post('/api/initUserInformation', async (req, res) => {
try {
const user = require('../services/gunDB/Mediator').getUser()
await UserInitializer.InitUserData(user)
diff --git a/src/server.js b/src/server.js
index 69a7a427..cc667f77 100644
--- a/src/server.js
+++ b/src/server.js
@@ -1,6 +1,21 @@
/**
* @prettier
*/
+// @ts-check
+
+const ECCrypto = require('eccrypto')
+
+const ECC = require('../utils/ECC')
+
+/**
+ * This API run's private key.
+ */
+const runPrivateKey = ECCrypto.generatePrivate()
+/**
+ * This API run's public key.
+ */
+const runPublicKey = ECCrypto.getPublic(runPrivateKey)
+
process.on('uncaughtException', e => {
console.log('something bad happened!')
console.log(e)
@@ -20,7 +35,6 @@ const server = program => {
const { Logger: CommonLogger } = require('shock-common')
const binaryParser = require('socket.io-msgpack-parser')
- const ECC = require('../utils/ECC')
const LightningServices = require('../utils/lightningServices')
const app = Express()
@@ -31,11 +45,17 @@ const server = program => {
const qrcode = require('qrcode-terminal')
const relayClient = require('hybrid-relay-client/build')
const {
- unprotectedRoutes,
sensitiveRoutes,
nonEncryptedRoutes
} = require('../utils/protectedRoutes')
+ /**
+ * An offline-only private key used for authenticating a client's key
+ * exchange. Neither the tunnel nor the WWW should see this private key, it
+ * should only be served through STDOUT (via QR or else).
+ */
+ const accessSecret = program.tunnel ? ECCrypto.generatePrivate() : null
+
// load app default configuration data
const defaults = require('../config/defaults')(program.mainnet)
const rootFolder = program.rootPath || process.resourcesPath || __dirname
@@ -82,41 +102,6 @@ const server = program => {
.digest('hex')
}
- const cacheCheck = ({ req, res, args, send }) => {
- if (
- (process.env.SHOCK_CACHE === 'true' || !process.env.SHOCK_CACHE) &&
- req.method === 'GET'
- ) {
- const dataHash = hashData(args[0]).slice(-8)
- res.set('shock-cache-hash', dataHash)
-
- logger.debug('shock-cache-hash:', req.headers['shock-cache-hash'])
- logger.debug('Data Hash:', dataHash)
- if (
- !req.headers['shock-cache-hash'] &&
- (process.env.CACHE_HEADERS_MANDATORY === 'true' ||
- !process.env.CACHE_HEADERS_MANDATORY)
- ) {
- logger.warn(
- "Request is missing 'shock-cache-hash' header, please make sure to include that in each GET request in order to benefit from reduced data usage"
- )
- return { cached: false, hash: dataHash }
- }
-
- if (req.headers['shock-cache-hash'] === dataHash) {
- logger.debug('Same Hash Detected!')
- args[0] = null
- res.status(304)
- send.apply(res, args)
- return { cached: true, hash: dataHash }
- }
-
- return { cached: false, hash: dataHash }
- }
-
- return { cached: false, hash: null }
- }
-
/**
* @param {Express.Request} req
* @param {Express.Response} res
@@ -140,6 +125,7 @@ const server = program => {
return
}
+ // @ts-expect-error
res.send = (...args) => {
if (args[0] && args[0].ciphertext && args[0].iv) {
logger.warn('Response loop detected!')
@@ -147,20 +133,23 @@ const server = program => {
return
}
- const authorized = ECC.isAuthorizedDevice({
- deviceId
- })
+ if (typeof deviceId !== 'string' || !deviceId) {
+ // TODO
+ }
+
+ const authorized = ECC.devicePublicKeys.has(deviceId)
// Using classic promises syntax to avoid
// modifying res.send's return type
if (authorized && process.env.SHOCK_ENCRYPTION_ECC !== 'false') {
- ECC.encryptMessage({
- deviceId,
- message: args[0]
- }).then(encryptedMessage => {
- args[0] = JSON.stringify(encryptedMessage)
- oldSend.apply(res, args)
- })
+ const devicePub = Buffer.from(ECC.devicePublicKeys.get(deviceId))
+
+ ECCrypto.encrypt(devicePub, Buffer.from(args[0], 'utf-8')).then(
+ encryptedMessage => {
+ args[0] = JSON.stringify(encryptedMessage)
+ oldSend.apply(res, args)
+ }
+ )
}
if (!authorized || process.env.SHOCK_ENCRYPTION_ECC === 'false') {
@@ -195,7 +184,7 @@ const server = program => {
await LightningServices.init()
}
- await new Promise((resolve, reject) => {
+ await /** @type {Promise} */ (new Promise((resolve, reject) => {
LightningServices.services.lightning.getInfo({}, (err, res) => {
if (
err &&
@@ -207,9 +196,7 @@ const server = program => {
resolve()
}
})
- })
-
- const auth = require('../services/auth/auth')
+ }))
app.use(compression())
@@ -320,7 +307,7 @@ const server = program => {
return httpsServer
}
- const httpServer = Http.Server(app)
+ const httpServer = new Http.Server(app)
return httpServer
} catch (err) {
logger.error(err.message)
@@ -328,7 +315,7 @@ const server = program => {
'An error has occurred while finding an LND cert to use to open an HTTPS server'
)
logger.warn('Falling back to opening an HTTP server...')
- const httpServer = Http.Server(app)
+ const httpServer = new Http.Server(app)
return httpServer
}
}
@@ -366,11 +353,13 @@ const server = program => {
},
Sockets,
{
- serverHost,
serverPort,
useTLS: program.useTLS,
CA,
- CA_KEY
+ CA_KEY,
+ runPrivateKey,
+ runPublicKey,
+ accessSecret
}
)
@@ -408,12 +397,12 @@ const server = program => {
Storage.setItem('relay/url', noProtocolAddress)
])
const dataToQr = JSON.stringify({
- internalIP: `${params.relayId}@${noProtocolAddress}`,
- walletPort: 443,
- externalIP: `${params.relayId}@${noProtocolAddress}`
+ URI: `https://${params.relayId}@${noProtocolAddress}`,
+ // Null-check is just to please typescript
+ accessSecret: accessSecret && accessSecret.toString('base64')
})
- qrcode.generate(dataToQr, { small: true })
- logger.info(`connect to ${params.relayId}@${noProtocolAddress}`)
+ qrcode.generate(dataToQr, { small: false })
+ logger.info(`connect to ${params.relayId}@${noProtocolAddress}:443`)
} else {
logger.error('!! Relay did not connect to server !!')
}
@@ -436,6 +425,7 @@ const server = program => {
}
serverInstance.listen(serverPort, serverHost)
logger.info('App listening on ' + serverHost + ' port ' + serverPort)
+ // @ts-expect-error
module.server = serverInstance
} catch (err) {
logger.error({ exception: err, message: err.message, code: err.code })
diff --git a/utils/ECC/index.js b/utils/ECC/index.js
index 3d30c2b9..b31f01d1 100644
--- a/utils/ECC/index.js
+++ b/utils/ECC/index.js
@@ -1 +1,8 @@
-module.exports = require('./ECC')
\ No newline at end of file
+/**
+ * @format
+ */
+//@ts-check
+
+module.exports = require('./ECC')
+
+module.exports.convertToEncryptedMessage = require('./crypto').convertToEncryptedMessage
diff --git a/utils/lightningServices/types.ts b/utils/lightningServices/types.ts
index ca0d034f..fb180e22 100644
--- a/utils/lightningServices/types.ts
+++ b/utils/lightningServices/types.ts
@@ -134,15 +134,15 @@ export interface Services {
}
export interface ListChannelsReq {
- active_only: boolean
- inactive_only: boolean
- public_only: boolean
- private_only: boolean
+ active_only?: boolean
+ inactive_only?: boolean
+ public_only?: boolean
+ private_only?: boolean
/**
* Filters the response for channels with a target peer's pubkey. If peer is
* empty, all channels will be returned.
*/
- peer: Common.Bytes
+ peer?: Common.Bytes
}
/**
@@ -193,4 +193,8 @@ export interface AddInvoiceRes {
* all payments for this invoice as we require it for end to end security.
*/
payment_addr: Common.Bytes
+ /**
+ * Custom property, by us.
+ */
+ liquidityCheck?: boolean
}