From 68a8db6ab33498f17ede5eb64ff0238cabb55816 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Fri, 4 Sep 2020 14:27:17 -0400 Subject: [PATCH 01/74] new payments REST endpoint --- src/routes.js | 59 +++++++++++++++++++++++++++++++++-- utils/lightningServices/v2.js | 47 +++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/src/routes.js b/src/routes.js index 65fc1a68..e42d852c 100644 --- a/src/routes.js +++ b/src/routes.js @@ -14,7 +14,7 @@ const Common = require('shock-common') const isARealUsableNumber = require('lodash/isFinite') const Big = require('big.js') const size = require('lodash/size') -const { range, flatten } = require('ramda') +const { range, flatten, evolve } = require('ramda') const getListPage = require('../utils/paginate') const auth = require('../services/auth/auth') @@ -31,7 +31,8 @@ const GunGetters = require('../services/gunDB/contact-api/getters') const GunKey = require('../services/gunDB/contact-api/key') const { sendPaymentV2Keysend, - sendPaymentV2Invoice + sendPaymentV2Invoice, + listPayments } = require('../utils/lightningServices/v2') const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10 @@ -1238,6 +1239,60 @@ module.exports = async ( ) }) + app.get('/api/lnd/payments', async (req, res) => { + const { + include_incomplete, + index_offset, + max_payments, + reversed + } = /** @type {Common.APISchema.ListPaymentsRequest} */ (evolve( + { + include_incomplete: x => x === 'true', + index_offset: x => Number(x), + max_payments: x => Number(x), + reversed: x => x === 'true' + }, + req.query + )) + + if (typeof include_incomplete !== 'boolean') { + return res.status(400).json({ + field: 'include_incomplete', + errorMessage: 'include_incomplete not a boolean' + }) + } + + if (!isARealUsableNumber(index_offset)) { + return res.status(400).json({ + field: 'index_offset', + errorMessage: 'index_offset not a number' + }) + } + + if (!isARealUsableNumber(max_payments)) { + return res.status(400).json({ + field: 'max_payments', + errorMessage: 'max_payments not a number' + }) + } + + if (typeof reversed !== 'boolean') { + return res.status(400).json({ + field: 'reversed', + errorMessage: 'reversed not a boolean' + }) + } + + return res.status(200).json( + await listPayments({ + include_incomplete, + index_offset, + max_payments, + reversed + }) + ) + }) + // get lnd node invoices list app.get('/api/lnd/listinvoices', (req, res) => { const { lightning } = LightningServices.services diff --git a/utils/lightningServices/v2.js b/utils/lightningServices/v2.js index 18bf4fef..0f6a19e3 100644 --- a/utils/lightningServices/v2.js +++ b/utils/lightningServices/v2.js @@ -3,6 +3,8 @@ */ const Crypto = require('crypto') const logger = require('winston') +const Common = require('shock-common') +const Ramda = require('ramda') const lightningServices = require('./lightning-services') /** @@ -337,7 +339,50 @@ const sendPaymentV2Invoice = params => { }) } +/** + * @param {Common.APISchema.ListPaymentsRequest} req + * @throws {TypeError} + * @returns {Promise} + */ +const listPayments = req => { + return Common.Utils.makePromise((res, rej) => { + lightningServices.lightning.listPayments( + req, + /** + * @param {{ details: any; }} err + * @param {unknown} lpres + */ (err, lpres) => { + if (err) { + return rej(new Error(err.details || err)) + } + + if (!Common.APISchema.isListPaymentsResponse(lpres)) { + return rej(new TypeError(`Response from LND not in expected format.`)) + } + + /** @type {Common.APISchema.ListPaymentsResponseParsed} */ + // @ts-expect-error + const parsed = Ramda.evolve( + { + first_index_offset: x => Number(x), + last_index_offset: x => Number(x), + payments: x => x + }, + lpres + ) + + if (Common.APISchema.isListPaymentsResponseParsed(parsed)) { + return res(parsed) + } + + return rej(new TypeError(`could not parse response from LND`)) + } + ) + }) +} + module.exports = { sendPaymentV2Keysend, - sendPaymentV2Invoice + sendPaymentV2Invoice, + listPayments } From fbb2396e60fa7b75442e138195d92843a42a9788 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Sun, 6 Sep 2020 17:55:43 -0400 Subject: [PATCH 02/74] rpc-style endpoints for gun --- src/routes.js | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/src/routes.js b/src/routes.js index 65fc1a68..7febbac8 100644 --- a/src/routes.js +++ b/src/routes.js @@ -2832,4 +2832,103 @@ module.exports = async ( data: isAuthenticated() }) }) + + /** + * @typedef {object} HandleGunFetchParams + * @prop {'once'|'load'} type + * @prop {boolean} startFromUserGraph + * @prop {string} path + * @prop {string=} publicKey + */ + /** + * @param {HandleGunFetchParams} args0 + * @returns {Promise} + */ + const handleGunFetch = ({ type, startFromUserGraph, path, publicKey }) => { + const keys = path.split('.') + const { tryAndWait } = require('../services/gunDB/contact-api/utils') + console.log(keys) + return tryAndWait((gun, user) => { + // eslint-disable-next-line no-nested-ternary + let node = startFromUserGraph + ? user + : publicKey + ? gun.user(publicKey) + : gun + keys.forEach(key => (node = node.get(key))) + + return new Promise(res => { + if (type === 'once') node.once(data => res(data)) + if (type === 'load') node.load(data => res(data)) + }) + }) + } + + ap.get('/api/gun/once/:path', async (req, res) => { + const { path } = req.params + res.status(200).json({ + data: await handleGunFetch({ + path, + startFromUserGraph: false, + type: 'once' + }) + }) + }) + + ap.get('/api/gun/load/:path', async (req, res) => { + const { path } = req.params + res.status(200).json({ + data: await handleGunFetch({ + path, + startFromUserGraph: false, + type: 'load' + }) + }) + }) + + ap.get('/api/gun/user/once/:path', async (req, res) => { + const { path } = req.params + res.status(200).json({ + data: await handleGunFetch({ + path, + startFromUserGraph: true, + type: 'once' + }) + }) + }) + + ap.get('/api/gun/user/load/:path', async (req, res) => { + const { path } = req.params + res.status(200).json({ + data: await handleGunFetch({ + path, + startFromUserGraph: true, + type: 'load' + }) + }) + }) + + ap.get('/api/gun/otheruser/:publicKey/once/:path', async (req, res) => { + const { path, publicKey } = req.params + res.status(200).json({ + data: await handleGunFetch({ + path, + startFromUserGraph: false, + type: 'once', + publicKey + }) + }) + }) + + ap.get('/api/gun/otheruser/:publicKey/load/:path', async (req, res) => { + const { path, publicKey } = req.params + res.status(200).json({ + data: await handleGunFetch({ + path, + startFromUserGraph: false, + type: 'load', + publicKey + }) + }) + }) } From 8edbe85e537fcfd15d3c74203bf8264a1d2c86e7 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Wed, 9 Sep 2020 15:58:08 -0400 Subject: [PATCH 03/74] typo --- services/gunDB/contact-api/jobs/onOrders.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/gunDB/contact-api/jobs/onOrders.js b/services/gunDB/contact-api/jobs/onOrders.js index bf3f45a2..a297c058 100644 --- a/services/gunDB/contact-api/jobs/onOrders.js +++ b/services/gunDB/contact-api/jobs/onOrders.js @@ -195,6 +195,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { getUser() .get(Key.ORDER_TO_RESPONSE) .get(orderID) + // @ts-expect-error .put(orderResponse, ack => { if (ack.err && typeof ack.err !== 'number') { rej( @@ -232,6 +233,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { getUser() .get(Key.ORDER_TO_RESPONSE) .get(orderID) + // @ts-expect-error .put(orderResponse, ack => { if (ack.err && typeof ack.err !== 'number') { logger.error( From fc49249984894966c9c680845040c8e538081b03 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Wed, 9 Sep 2020 15:58:44 -0400 Subject: [PATCH 04/74] rpc-style lnd endpoint --- src/routes.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/routes.js b/src/routes.js index 998f983e..dc81fe89 100644 --- a/src/routes.js +++ b/src/routes.js @@ -2986,4 +2986,24 @@ module.exports = async ( }) }) }) + + ap.get('/api/lnd/cb/:methodName', (req, res) => { + const { lightning } = LightningServices.services + const { methodName } = req.params + const args = req.body + + lightning[methodName](args, (err, lres) => { + if (err) { + res.status(500).json({ + errorMessage: err.details + }) + } else if (lres) { + res.status(200).json(lres) + } else { + res.status(500).json({ + errorMessage: 'Unknown error' + }) + } + }) + }) } From c03e1478b45edb15b6921a6a3ba0b3adc8237c51 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Thu, 10 Sep 2020 20:15:46 -0400 Subject: [PATCH 05/74] post instead of get --- src/routes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes.js b/src/routes.js index dc81fe89..9a907a02 100644 --- a/src/routes.js +++ b/src/routes.js @@ -2987,7 +2987,7 @@ module.exports = async ( }) }) - ap.get('/api/lnd/cb/:methodName', (req, res) => { + ap.post('/api/lnd/cb/:methodName', (req, res) => { const { lightning } = LightningServices.services const { methodName } = req.params const args = req.body From ac3f47e99d7aa6ca8d7169f7af453e47b2a0bdf5 Mon Sep 17 00:00:00 2001 From: hatim boufnichel Date: Mon, 14 Sep 2020 14:00:45 +0200 Subject: [PATCH 06/74] fix function name, more log fix --- src/routes.js | 7 +++++-- src/sockets.js | 20 +++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/routes.js b/src/routes.js index f4933ffa..81588e1b 100644 --- a/src/routes.js +++ b/src/routes.js @@ -654,7 +654,7 @@ module.exports = async ( 'Channel backup LND locked, new registration in 60 seconds' ) process.nextTick(() => - setTimeout(() => onNewTransaction(socket, subID), 60000) + setTimeout(() => onNewChannelBackup(), 60000) ) break } @@ -668,10 +668,13 @@ module.exports = async ( 'Channel backup LND disconnected, sockets reconnecting in 30 seconds...' ) process.nextTick(() => - setTimeout(() => onNewTransaction(socket, subID), 30000) + setTimeout(() => onNewChannelBackup(), 30000) ) break } + default: { + logger.error('[event:transaction:new] UNKNOWN LND error') + } } }) } diff --git a/src/sockets.js b/src/sockets.js index fd317c61..2fd93ecc 100644 --- a/src/sockets.js +++ b/src/sockets.js @@ -134,6 +134,12 @@ module.exports = ( logger.info('[event:invoice:new] stream ok') break } + case 1: { + logger.info( + '[event:invoice:new] stream canceled, probably socket disconnected' + ) + break + } case 2: { logger.warn('[event:invoice:new] got UNKNOWN error status') break @@ -161,6 +167,9 @@ module.exports = ( ) break } + default: { + logger.error('[event:invoice:new] UNKNOWN LND error') + } } }) return () => { @@ -184,12 +193,18 @@ module.exports = ( logger.error('New transactions stream error:' + subID, err) }) stream.on('status', status => { - logger.error('New transactions stream status:' + subID, status) + logger.info('New transactions stream status:' + subID, status) switch (status.code) { case 0: { logger.info('[event:transaction:new] stream ok') break } + case 1: { + logger.info( + '[event:transaction:new] stream canceled, probably socket disconnected' + ) + break + } case 2: { //Happens to fire when the grpc client lose access to macaroon file logger.warn('[event:transaction:new] got UNKNOWN error status') @@ -218,6 +233,9 @@ module.exports = ( ) break } + default: { + logger.error('[event:transaction:new] UNKNOWN LND error') + } } }) return () => { From d7ea399f2440385fa4b70b8112df38e03a7cd14d Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Thu, 17 Sep 2020 15:33:04 -0400 Subject: [PATCH 07/74] follows at auth --- src/routes.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes.js b/src/routes.js index 284ca048..aa1e00ed 100644 --- a/src/routes.js +++ b/src/routes.js @@ -694,7 +694,8 @@ module.exports = async ( user: { alias, publicKey - } + }, + follows: await GunGetters.Follows.currentFollows() }) return true From b80f0dec53828652b18649abdac91f6d60c0ef67 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Sun, 20 Sep 2020 12:24:59 -0400 Subject: [PATCH 08/74] socket ping canary --- src/sockets.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sockets.js b/src/sockets.js index 2fd93ecc..6c5b11df 100644 --- a/src/sockets.js +++ b/src/sockets.js @@ -5,6 +5,18 @@ const logger = require('winston') const Encryption = require('../utils/encryptionStore') const LightningServices = require('../utils/lightningServices') +const onPing = (socket, subID) => { + logger.warn('Subscribing to pings socket...' + subID) + + const intervalID = setInterval(() => { + socket.emit('shockping') + }, 3000) + + return () => { + clearInterval(intervalID) + } +} + module.exports = ( /** @type {import('socket.io').Server} */ io @@ -286,10 +298,12 @@ module.exports = ( logger.info('[LND] New LND Socket created:' + isNotifications + subID) const cancelInvoiceStream = onNewInvoice(socket, subID) const cancelTransactionStream = onNewTransaction(socket, subID) + const cancelPingStream = onPing(socket, subID) socket.on('disconnect', () => { logger.info('LND socket disconnected:' + isNotifications + subID) cancelInvoiceStream() cancelTransactionStream() + cancelPingStream() }) return } From 71c239d432c3a618f4b5e8fbfabd94e9f6509c5d Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Mon, 28 Sep 2020 11:56:21 -0400 Subject: [PATCH 09/74] invoices boostrap at auth --- src/routes.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/routes.js b/src/routes.js index aa1e00ed..2a1b7182 100644 --- a/src/routes.js +++ b/src/routes.js @@ -695,7 +695,24 @@ module.exports = async ( alias, publicKey }, - follows: await GunGetters.Follows.currentFollows() + follows: await GunGetters.Follows.currentFollows(), + data: { + invoices: await Common.makePromise((res, rej) => { + lightning.listInvoices( + { + reversed: true, + num_max_invoices: 50 + }, + (err, lres) => { + if (err) { + rej(new Error(err.details)) + } else { + res(lres) + } + } + ) + }) + } }) return true From 288ba30a163fe8d70ab94393e5324a2a0222939d Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Mon, 28 Sep 2020 11:56:30 -0400 Subject: [PATCH 10/74] catch errors --- src/routes.js | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/routes.js b/src/routes.js index 2a1b7182..46db0778 100644 --- a/src/routes.js +++ b/src/routes.js @@ -3086,22 +3086,32 @@ module.exports = async ( }) ap.post('/api/lnd/cb/:methodName', (req, res) => { - const { lightning } = LightningServices.services - const { methodName } = req.params - const args = req.body + try { + const { lightning } = LightningServices.services + const { methodName } = req.params + const args = req.body - lightning[methodName](args, (err, lres) => { - if (err) { - res.status(500).json({ - errorMessage: err.details - }) - } else if (lres) { - res.status(200).json(lres) - } else { - res.status(500).json({ - errorMessage: 'Unknown error' - }) - } - }) + lightning[methodName](args, (err, lres) => { + if (err) { + res.status(500).json({ + errorMessage: err.details + }) + } else if (lres) { + res.status(200).json(lres) + } else { + res.status(500).json({ + errorMessage: 'Unknown error' + }) + } + }) + } catch (err) { + logger.warn(`Error inside api cb:`) + logger.error(err) + logger.error(err.message) + + return res.status(500).json({ + errorMessage: err.message + }) + } }) } From f184f7be942560e1178a74ca79e1b27de4b0f5bf Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Mon, 28 Sep 2020 14:23:33 -0400 Subject: [PATCH 11/74] remove old socket code --- src/sockets.js | 62 +++++++++++--------------------------------------- 1 file changed, 13 insertions(+), 49 deletions(-) diff --git a/src/sockets.js b/src/sockets.js index 6c5b11df..f7add4d7 100644 --- a/src/sockets.js +++ b/src/sockets.js @@ -1,5 +1,5 @@ /** @prettier */ -// app/sockets.js +// @ts-check const logger = require('winston') const Encryption = require('../utils/encryptionStore') @@ -21,8 +21,6 @@ module.exports = ( /** @type {import('socket.io').Server} */ io ) => { - const Mediator = require('../services/gunDB/Mediator/index.js') - // This should be used for encrypting and emitting your data const emitEncryptedEvent = ({ eventName, data, socket }) => { try { @@ -260,61 +258,27 @@ module.exports = ( logger.info('socket.handshake', socket.handshake) - const isOneTimeUseSocket = !!socket.handshake.query.IS_GUN_AUTH const isLNDSocket = !!socket.handshake.query.IS_LND_SOCKET const isNotificationsSocket = !!socket.handshake.query .IS_NOTIFICATIONS_SOCKET + if (!isLNDSocket) { /** printing out the client who joined */ logger.info('New socket client connected (id=' + socket.id + ').') } - if (isOneTimeUseSocket) { - logger.info('New socket is one time use') - socket.on('IS_GUN_AUTH', () => { - try { - const isGunAuth = Mediator.isAuthenticated() - socket.emit('IS_GUN_AUTH', { - ok: true, - msg: { - isGunAuth - }, - origBody: {} - }) - socket.disconnect() - } catch (err) { - socket.emit('IS_GUN_AUTH', { - ok: false, - msg: err.message, - origBody: {} - }) - socket.disconnect() - } - }) - } else { - if (isLNDSocket) { - const subID = Math.floor(Math.random() * 1000).toString() - const isNotifications = isNotificationsSocket ? 'notifications' : '' - logger.info('[LND] New LND Socket created:' + isNotifications + subID) - const cancelInvoiceStream = onNewInvoice(socket, subID) - const cancelTransactionStream = onNewTransaction(socket, subID) - const cancelPingStream = onPing(socket, subID) - socket.on('disconnect', () => { - logger.info('LND socket disconnected:' + isNotifications + subID) - cancelInvoiceStream() - cancelTransactionStream() - cancelPingStream() - }) - return - } - logger.info('New socket is NOT one time use') - // this is where we create the websocket connection - // with the GunDB service. - Mediator.createMediator(socket) - - /** listening if client has disconnected */ + if (isLNDSocket) { + const subID = Math.floor(Math.random() * 1000).toString() + const isNotifications = isNotificationsSocket ? 'notifications' : '' + logger.info('[LND] New LND Socket created:' + isNotifications + subID) + const cancelInvoiceStream = onNewInvoice(socket, subID) + const cancelTransactionStream = onNewTransaction(socket, subID) + const cancelPingStream = onPing(socket, subID) socket.on('disconnect', () => { - logger.info('client disconnected (id=' + socket.id + ').') + logger.info('LND socket disconnected:' + isNotifications + subID) + cancelInvoiceStream() + cancelTransactionStream() + cancelPingStream() }) } }) From b815a881499f52a8fd62d2cb632509defcb35b65 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Wed, 30 Sep 2020 20:10:15 -0400 Subject: [PATCH 12/74] eslint rules --- .eslintrc.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index 00f512ef..dd2485bf 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -83,7 +83,12 @@ "no-undefined": "off", - "no-process-env": "off" + "no-process-env": "off", + + // I am now convinced TODO comments closer to the relevant code are better + // than GH issues. Especially when it only concerns a single function / + // routine. + "no-warning-comments": "off" }, "parser": "babel-eslint", "env": { From dcf176429ea1abc45824a3438cac6baf2551e9f2 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Wed, 30 Sep 2020 20:50:49 -0400 Subject: [PATCH 13/74] deafult namespace for sockets --- src/sockets.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sockets.js b/src/sockets.js index f7add4d7..5d19a7c2 100644 --- a/src/sockets.js +++ b/src/sockets.js @@ -253,9 +253,8 @@ module.exports = ( } } - io.on('connection', socket => { + io.of('default').on('connection', socket => { logger.info(`io.onconnection`) - logger.info('socket.handshake', socket.handshake) const isLNDSocket = !!socket.handshake.query.IS_LND_SOCKET From 3e059591a9cc1d63079bb2a5c7200540d626f9e4 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Wed, 30 Sep 2020 20:52:17 -0400 Subject: [PATCH 14/74] gun rpc namespace --- src/sockets.js | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/sockets.js b/src/sockets.js index 5d19a7c2..a6bc07ff 100644 --- a/src/sockets.js +++ b/src/sockets.js @@ -4,6 +4,11 @@ const logger = require('winston') const Encryption = require('../utils/encryptionStore') const LightningServices = require('../utils/lightningServices') +const { + getGun, + getUser, + isAuthenticated +} = require('../services/gunDB/Mediator') const onPing = (socket, subID) => { logger.warn('Subscribing to pings socket...' + subID) @@ -282,5 +287,65 @@ module.exports = ( } }) + io.of('gun').on('connect', socket => { + // TODO: off() + + try { + if (!isAuthenticated()) { + socket.emit('$shock', 'NOT_AUTH') + return + } + + const { $shock } = socket.handshake.query + + const [root, path, method] = $shock.split('::') + + // eslint-disable-next-line init-declarations + let node + + if (root === '$gun') { + node = getGun() + } else if (root === '$user') { + node = getUser() + } else { + node = getGun().user(root) + } + + for (const bit of path.split('.')) { + node = node.get(bit) + } + + /** + * @param {unknown} data + * @param {string} key + */ + const listener = (data, key) => { + try { + socket.emit('$shock', data, key) + } catch (err) { + logger.error( + `Error for gun rpc socket, query ${$shock} -> ${err.message}` + ) + } + } + + if (method === 'on') { + node.on(listener) + } else if (method === 'open') { + node.open(listener) + } else if (method === 'map.on') { + node.map().on(listener) + } else if (method === 'map.once') { + node.map().once(listener) + } else { + throw new TypeError( + `Invalid method for gun rpc call : ${method}, query: ${$shock}` + ) + } + } catch (err) { + logger.error('GUNRPC: ' + err.message) + } + }) + return io } From d0af6532a3e827567cd0894af528f0fb14ab0851 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Fri, 2 Oct 2020 11:54:51 -0400 Subject: [PATCH 15/74] socket LND RPC --- src/sockets.js | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/sockets.js b/src/sockets.js index a6bc07ff..52af968e 100644 --- a/src/sockets.js +++ b/src/sockets.js @@ -347,5 +347,46 @@ module.exports = ( } }) + io.of('/lndstreaming').on('connect', socket => { + // TODO: unsubscription + + /** + * Streaming stuff in LND uses these events: data, status, end, error. + */ + + try { + const { services } = LightningServices + + const { service, method, args: unParsed } = socket.handshake.query + + const args = JSON.parse(unParsed) + + const call = services[service][method](args) + + call.on('data', data => { + socket.emit('data', data) + }) + + call.on('status', status => { + socket.emit('status', status) + }) + + call.on('end', () => { + socket.emit('end') + }) + + call.on('error', err => { + socket.emit('error', err) + }) + + // Possibly allow streaming writes such as sendPaymentV2 + socket.on('write', args => { + call.write(args) + }) + } catch (err) { + logger.error('LNDRPC: ' + err.message) + } + }) + return io } From 892890bbfdf70a059be1e7d4866c07608505b135 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Fri, 2 Oct 2020 14:35:25 -0400 Subject: [PATCH 16/74] use non-restricted event name --- src/sockets.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sockets.js b/src/sockets.js index 52af968e..4feecfdc 100644 --- a/src/sockets.js +++ b/src/sockets.js @@ -376,7 +376,8 @@ module.exports = ( }) call.on('error', err => { - socket.emit('error', err) + // 'error' is a reserved event name we can't use it + socket.emit('$error', err) }) // Possibly allow streaming writes such as sendPaymentV2 From c8dc984469f3bbf8c23511d6301a2c5cb1f3127a Mon Sep 17 00:00:00 2001 From: emad-salah Date: Sat, 3 Oct 2020 16:11:19 +0100 Subject: [PATCH 17/74] Post Tipping --- package.json | 2 +- services/gunDB/contact-api/actions.js | 3 +- services/gunDB/contact-api/jobs/onOrders.js | 67 +++++-- services/gunDB/contact-api/key.js | 5 + src/routes.js | 2 + src/server.js | 28 +-- utils/lndJobs.js | 196 ++++++++++++++++++++ yarn.lock | 7 +- 8 files changed, 267 insertions(+), 43 deletions(-) create mode 100644 utils/lndJobs.js diff --git a/package.json b/package.json index 81443eb5..f2f0196a 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "request-promise": "^4.2.2", "response-time": "^2.3.2", "shelljs": "^0.8.2", - "shock-common": "8.0.0", + "shock-common": "shocknet/shock-common#49aa269c723b2c2ee803662c98ba2ddd1f68f57e", "socket.io": "2.1.1", "text-encoding": "^0.7.0", "tingodb": "^0.6.1", diff --git a/services/gunDB/contact-api/actions.js b/services/gunDB/contact-api/actions.js index 74cc77b0..b0fa8070 100644 --- a/services/gunDB/contact-api/actions.js +++ b/services/gunDB/contact-api/actions.js @@ -935,7 +935,8 @@ const sendSpontaneousPayment = async (to, amount, memo, feeLimit) => { amount: amount.toString(), from: getUser()._.sea.pub, memo: memo || 'no memo', - timestamp: Date.now() + timestamp: Date.now(), + targetType: 'user' } logger.info(JSON.stringify(order)) diff --git a/services/gunDB/contact-api/jobs/onOrders.js b/services/gunDB/contact-api/jobs/onOrders.js index a297c058..ca72f2b8 100644 --- a/services/gunDB/contact-api/jobs/onOrders.js +++ b/services/gunDB/contact-api/jobs/onOrders.js @@ -2,6 +2,7 @@ * @format */ +const { performance } = require('perf_hooks') const logger = require('winston') const isFinite = require('lodash/isFinite') const isNumber = require('lodash/isNumber') @@ -38,9 +39,18 @@ const ordersProcessed = new Set() * @prop {boolean} private */ +/** + * @typedef {object} InvoiceResponse + * @prop {string} payment_request + * @prop {Buffer} r_hash + */ + let currentOrderAddr = '' -/** @param {InvoiceRequest} invoiceReq */ +/** + * @param {InvoiceRequest} invoiceReq + * @returns {Promise} + */ const _addInvoice = invoiceReq => new Promise((resolve, rej) => { const { @@ -49,12 +59,12 @@ const _addInvoice = invoiceReq => lightning.addInvoice(invoiceReq, ( /** @type {any} */ error, - /** @type {{ payment_request: string }} */ response + /** @type {InvoiceResponse} */ response ) => { if (error) { rej(error) } else { - resolve(response.payment_request) + resolve(response) } }) }) @@ -85,7 +95,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { return } - const listenerStartTime = Date.now() + const listenerStartTime = performance.now() ordersProcessed.add(orderID) @@ -95,7 +105,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { )} -- addr: ${addr}` ) - const orderAnswerStartTime = Date.now() + const orderAnswerStartTime = performance.now() const alreadyAnswered = await getUser() .get(Key.ORDER_TO_RESPONSE) @@ -107,11 +117,11 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { return } - const orderAnswerEndTime = Date.now() - orderAnswerStartTime + const orderAnswerEndTime = performance.now() - orderAnswerStartTime logger.info(`[PERF] Order Already Answered: ${orderAnswerEndTime}ms`) - const decryptStartTime = Date.now() + const decryptStartTime = performance.now() const senderEpub = await Utils.pubToEpub(order.from) const secret = await SEA.secret(senderEpub, getUser()._.sea) @@ -121,7 +131,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { SEA.decrypt(order.memo, secret) ]) - const decryptEndTime = Date.now() - decryptStartTime + const decryptEndTime = performance.now() - decryptStartTime logger.info(`[PERF] Decrypt invoice info: ${decryptEndTime}ms`) @@ -156,14 +166,11 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { `onOrders() -> Will now create an invoice : ${JSON.stringify(invoiceReq)}` ) - const invoiceStartTime = Date.now() + const invoiceStartTime = performance.now() - /** - * @type {string} - */ const invoice = await _addInvoice(invoiceReq) - const invoiceEndTime = Date.now() - invoiceStartTime + const invoiceEndTime = performance.now() - invoiceStartTime logger.info(`[PERF] LND Invoice created in ${invoiceEndTime}ms`) @@ -171,11 +178,11 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { 'onOrders() -> Successfully created the invoice, will now encrypt it' ) - const invoiceEncryptStartTime = Date.now() + const invoiceEncryptStartTime = performance.now() - const encInvoice = await SEA.encrypt(invoice, secret) + const encInvoice = await SEA.encrypt(invoice.payment_request, secret) - const invoiceEncryptEndTime = Date.now() - invoiceEncryptStartTime + const invoiceEncryptEndTime = performance.now() - invoiceEncryptStartTime logger.info(`[PERF] Invoice encrypted in ${invoiceEncryptEndTime}ms`) @@ -189,7 +196,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { type: 'invoice' } - const invoicePutStartTime = Date.now() + const invoicePutStartTime = performance.now() await new Promise((res, rej) => { getUser() @@ -209,13 +216,35 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { }) }) - const invoicePutEndTime = Date.now() - invoicePutStartTime + const invoicePutEndTime = performance.now() - invoicePutStartTime logger.info(`[PERF] Added invoice to GunDB in ${invoicePutEndTime}ms`) - const listenerEndTime = Date.now() - listenerStartTime + const listenerEndTime = performance.now() - listenerStartTime logger.info(`[PERF] Invoice generation completed in ${listenerEndTime}ms`) + + const hash = invoice.r_hash.toString('base64') + + if (order.targetType === 'post') { + getUser() + .get(Key.TIPS_PAYMENT_STATUS) + .get(hash) + .put( + { + hash, + state: 'OPEN', + targetType: order.targetType, + // @ts-ignore + postID: order.postID, + // @ts-ignore + postPage: order.postPage + }, + response => { + console.log(response) + } + ) + } } catch (err) { logger.error( `error inside onOrders, orderAddr: ${addr}, orderID: ${orderID}, order: ${JSON.stringify( diff --git a/services/gunDB/contact-api/key.js b/services/gunDB/contact-api/key.js index 0c130712..5bc5ad04 100644 --- a/services/gunDB/contact-api/key.js +++ b/services/gunDB/contact-api/key.js @@ -55,3 +55,8 @@ exports.CONTENT_ITEMS = 'contentItems' exports.FOLLOWS = 'follows' exports.POSTS = 'posts' + +// Tips counter for posts +exports.TOTAL_TIPS = 'totalTips' + +exports.TIPS_PAYMENT_STATUS = 'tipsPaymentStatus' diff --git a/src/routes.js b/src/routes.js index aa1e00ed..f537a502 100644 --- a/src/routes.js +++ b/src/routes.js @@ -35,6 +35,7 @@ const { sendPaymentV2Invoice, listPayments } = require('../utils/lightningServices/v2') +const { startTipStatusJob } = require('../utils/lndJobs') const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10 const SESSION_ID = uuid() @@ -686,6 +687,7 @@ module.exports = async ( } onNewChannelBackup() + startTipStatusJob() // Generate auth token and send it as a JSON response const token = await auth.generateToken() diff --git a/src/server.js b/src/server.js index c741ae45..9d908d84 100644 --- a/src/server.js +++ b/src/server.js @@ -26,15 +26,16 @@ const server = program => { sensitiveRoutes, nonEncryptedRoutes } = require('../utils/protectedRoutes') + // load app default configuration data const defaults = require('../config/defaults')(program.mainnet) const rootFolder = process.resourcesPath || __dirname - // define useful global variables ====================================== + + // define env variables Dotenv.config() - module.useTLS = program.usetls - module.serverPort = program.serverport || defaults.serverPort - module.httpsPort = module.serverPort - module.serverHost = program.serverhost || defaults.serverHost + + const serverPort = program.serverport || defaults.serverPort + const serverHost = program.serverhost || defaults.serverHost // setup winston logging ========== const logger = require('../config/log')( @@ -274,8 +275,8 @@ const server = program => { const Sockets = require('./sockets')(io) require('./routes')(app, defaults, Sockets, { - serverHost: module.serverHost, - serverPort: module.serverPort, + serverHost, + serverPort, usetls: program.usetls, CA, CA_KEY @@ -290,20 +291,11 @@ const server = program => { app.use(modifyResponseBody) } - serverInstance.listen(module.serverPort, module.serverhost) + serverInstance.listen(serverPort, serverHost) - logger.info( - 'App listening on ' + module.serverHost + ' port ' + module.serverPort - ) + logger.info('App listening on ' + serverHost + ' port ' + serverPort) module.server = serverInstance - - // const localtunnel = require('localtunnel'); - // - // const tunnel = localtunnel(port, (err, t) => { - // logger.info('err', err); - // logger.info('t', t.url); - // }); } catch (err) { logger.info(err) logger.info('Restarting server in 30 seconds...') diff --git a/utils/lndJobs.js b/utils/lndJobs.js new file mode 100644 index 00000000..97f5062f --- /dev/null +++ b/utils/lndJobs.js @@ -0,0 +1,196 @@ +/** + * @prettier + */ +const Logger = require('winston') +const Key = require('../services/gunDB/contact-api/key') +const { getUser } = require('../services/gunDB/Mediator') +const LightningServices = require('./lightningServices') + +const ERROR_TRIES_THRESHOLD = 3 +const INVOICE_STATE = { + OPEN: 'OPEN', + SETTLED: 'SETTLED', + CANCELLED: 'CANCELLED', + ACCEPTED: 'ACCEPTED' +} + +const _lookupInvoice = hash => + new Promise((resolve, reject) => { + const { lightning } = LightningServices.services + lightning.lookupInvoice({ r_hash: hash }, (err, response) => { + if (err) { + Logger.error( + '[TIP] An error has occurred while trying to lookup invoice:', + err, + '\nInvoice Hash:', + hash + ) + reject(err) + return + } + + Logger.info('[TIP] Invoice lookup result:', response) + resolve(response) + }) + }) + +const _getPostTipInfo = ({ postID, page }) => + new Promise((resolve, reject) => { + getUser() + .get(Key.WALL) + .get(Key.PAGES) + .get(page) + .get(Key.POSTS) + .get(postID) + .once(post => { + if (post && post.date) { + const { tipCounter, tipValue } = post + console.log(post) + resolve({ + tipCounter: typeof tipCounter === 'number' ? tipCounter : 0, + tipValue: typeof tipValue === 'number' ? tipValue : 0 + }) + } + + resolve(post) + }) + }) + +const _incrementPost = ({ postID, page, orderAmount }) => + new Promise((resolve, reject) => { + const parsedAmount = parseFloat(orderAmount) + + if (typeof parsedAmount !== 'number') { + reject(new Error('Invalid order amount specified')) + return + } + + Logger.info('[POST TIP] Getting Post Tip Values...') + + return _getPostTipInfo({ postID, page }) + .then(({ tipValue, tipCounter }) => { + const updatedTip = { + tipCounter: tipCounter + 1, + tipValue: tipValue + parsedAmount + } + + getUser() + .get(Key.WALL) + .get(Key.PAGES) + .get(page) + .get(Key.POSTS) + .get(postID) + .put(updatedTip, () => { + Logger.info('[POST TIP] Successfully updated Post tip info') + resolve(updatedTip) + }) + }) + .catch(err => { + Logger.error(err) + reject(err) + }) + }) + +const _updateTipData = (invoiceHash, data) => + new Promise((resolve, reject) => { + try { + getUser() + .get(Key.TIPS_PAYMENT_STATUS) + .get(invoiceHash) + .put(data, tip => { + if (tip === undefined) { + reject(new Error('Tip update failed')) + return + } + + console.log(tip) + + resolve(tip) + }) + } catch (err) { + Logger.error('An error has occurred while updating tip^data') + throw err + } + }) + +const _getTipData = invoiceHash => + new Promise((resolve, reject) => { + getUser() + .get(Key.TIPS_PAYMENT_STATUS) + .get(invoiceHash) + .once(tip => { + if (tip === undefined) { + reject(new Error('Malformed data')) + return + } + + resolve(tip) + }) + }) + +const executeTipAction = (tip, invoice) => { + if (invoice.state !== INVOICE_STATE.SETTLED) { + return + } + + // Execute actions once invoice is settled + Logger.info('Invoice settled!', invoice) + + if (tip.targetType === 'post') { + _incrementPost({ + postID: tip.postID, + page: tip.postPage, + orderAmount: invoice.amt_paid_sat + }) + } +} + +const updateUnverifiedTips = () => { + getUser() + .get(Key.TIPS_PAYMENT_STATUS) + .map() + .once(async (tip, id) => { + try { + if ( + !tip || + tip.state !== INVOICE_STATE.OPEN || + (tip._errorCount && tip._errorCount >= ERROR_TRIES_THRESHOLD) + ) { + return + } + Logger.info('Unverified invoice found!', tip) + const invoice = await _lookupInvoice(tip.hash) + Logger.info('Invoice located:', invoice) + if (invoice.state !== tip.state) { + await _updateTipData(id, { state: invoice.state }) + + // Actions to be executed when the tip's state is updated + executeTipAction(tip, invoice) + } + } catch (err) { + Logger.error('[TIP] An error has occurred while updating invoice', err) + const errorCount = tip._errorCount ? tip._errorCount : 0 + _updateTipData(id, { + _errorCount: errorCount + 1 + }) + } + }) +} + +const startTipStatusJob = () => { + const { lightning } = LightningServices.services + const stream = lightning.subscribeInvoices({}) + updateUnverifiedTips() + stream.on('data', async invoice => { + const hash = invoice.r_hash.toString('base64') + const tip = await _getTipData(hash) + if (tip.state !== invoice.state) { + await _updateTipData(hash, { state: invoice.state }) + executeTipAction(tip, invoice) + } + }) +} + +module.exports = { + startTipStatusJob +} diff --git a/yarn.lock b/yarn.lock index 097d1bed..79d65d2b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6016,10 +6016,9 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -shock-common@8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-8.0.0.tgz#4dbc8c917adfb221a00b6d1e815c4d26d205ce66" - integrity sha512-X9jkSxNUjQOcVdEAGBl6dlBgBxF9MpjV50Cih4hoqLqeGfrAYHK/iqgXgDyaHkLraHRxdP6FWJ2DoWOpuBgpDQ== +shock-common@shocknet/shock-common#49aa269c723b2c2ee803662c98ba2ddd1f68f57e: + version "10.0.0" + resolved "https://codeload.github.com/shocknet/shock-common/tar.gz/49aa269c723b2c2ee803662c98ba2ddd1f68f57e" dependencies: immer "^6.0.6" lodash "^4.17.19" From f9e1404ea4a5898968fc2a177563902b214d1e38 Mon Sep 17 00:00:00 2001 From: emad-salah Date: Sat, 3 Oct 2020 16:24:38 +0100 Subject: [PATCH 18/74] Switch back to shock common npm package --- package.json | 2 +- yarn.lock | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f2f0196a..09d9c109 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "request-promise": "^4.2.2", "response-time": "^2.3.2", "shelljs": "^0.8.2", - "shock-common": "shocknet/shock-common#49aa269c723b2c2ee803662c98ba2ddd1f68f57e", + "shock-common": "6.0.0", "socket.io": "2.1.1", "text-encoding": "^0.7.0", "tingodb": "^0.6.1", diff --git a/yarn.lock b/yarn.lock index 79d65d2b..c954bc9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6016,9 +6016,10 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -shock-common@shocknet/shock-common#49aa269c723b2c2ee803662c98ba2ddd1f68f57e: - version "10.0.0" - resolved "https://codeload.github.com/shocknet/shock-common/tar.gz/49aa269c723b2c2ee803662c98ba2ddd1f68f57e" +shock-common@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-6.0.0.tgz#77701dabb344830046b85ece0103de21775197b9" + integrity sha512-kbSkLTtWTK8qVcbW6uViWtar3otW2S5oJIMO/1twPs6Iuv9ATF0QviDtUiU1YN/Y4bX/SXAMm/jRZ3OJ3PLyUA== dependencies: immer "^6.0.6" lodash "^4.17.19" From b5317db2daf451de115a0a672007ec46473d0b82 Mon Sep 17 00:00:00 2001 From: emad-salah Date: Sat, 3 Oct 2020 18:20:21 +0100 Subject: [PATCH 19/74] Upgraded shock-common --- package.json | 2 +- services/gunDB/contact-api/jobs/onOrders.js | 35 ++++++++++++--------- yarn.lock | 8 ++--- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 09d9c109..89a82a2b 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "request-promise": "^4.2.2", "response-time": "^2.3.2", "shelljs": "^0.8.2", - "shock-common": "6.0.0", + "shock-common": "11.0.0", "socket.io": "2.1.1", "text-encoding": "^0.7.0", "tingodb": "^0.6.1", diff --git a/services/gunDB/contact-api/jobs/onOrders.js b/services/gunDB/contact-api/jobs/onOrders.js index ca72f2b8..3d9380c0 100644 --- a/services/gunDB/contact-api/jobs/onOrders.js +++ b/services/gunDB/contact-api/jobs/onOrders.js @@ -45,6 +45,15 @@ const ordersProcessed = new Set() * @prop {Buffer} r_hash */ +/** + * @typedef {object} TipPaymentStatus + * @prop {string} hash + * @prop {import('shock-common').Schema.InvoiceState} state + * @prop {string} targetType + * @prop {(string)=} postID + * @prop {(number)=} postPage + */ + let currentOrderAddr = '' /** @@ -227,23 +236,21 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => { const hash = invoice.r_hash.toString('base64') if (order.targetType === 'post') { + /** @type {TipPaymentStatus} */ + const paymentStatus = { + hash, + state: 'OPEN', + targetType: order.targetType, + postID: order.postID, + postPage: order.postPage + } getUser() .get(Key.TIPS_PAYMENT_STATUS) .get(hash) - .put( - { - hash, - state: 'OPEN', - targetType: order.targetType, - // @ts-ignore - postID: order.postID, - // @ts-ignore - postPage: order.postPage - }, - response => { - console.log(response) - } - ) + // @ts-ignore + .put(paymentStatus, response => { + console.log(response) + }) } } catch (err) { logger.error( diff --git a/yarn.lock b/yarn.lock index c954bc9b..4a2e9fb2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6016,10 +6016,10 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -shock-common@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-6.0.0.tgz#77701dabb344830046b85ece0103de21775197b9" - integrity sha512-kbSkLTtWTK8qVcbW6uViWtar3otW2S5oJIMO/1twPs6Iuv9ATF0QviDtUiU1YN/Y4bX/SXAMm/jRZ3OJ3PLyUA== +shock-common@11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-11.0.0.tgz#7bac5b614df2a62f29ec9ba116bb0f49d4dc8894" + integrity sha512-0ie3WY5Fovqh6k/oIc1z3ia4r9COhpH0LsF1BXNy2n9MAwnk5zkXG7K0qSppPsBXZ0aN8RFXvvmTXssRTrXcSA== dependencies: immer "^6.0.6" lodash "^4.17.19" From 7a4d31dd6d4818d2f861cf0a047f5f989e7197be Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Tue, 6 Oct 2020 12:42:58 -0400 Subject: [PATCH 20/74] gun rpc for writes --- services/gunDB/rpc.js | 106 ++++++++++++++++++++++++++++++++++++++++++ src/routes.js | 38 +++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 services/gunDB/rpc.js diff --git a/services/gunDB/rpc.js b/services/gunDB/rpc.js new file mode 100644 index 00000000..3a4bf36d --- /dev/null +++ b/services/gunDB/rpc.js @@ -0,0 +1,106 @@ +/** + * @format + */ +// @ts-check +const { makePromise, Constants } = require('shock-common') + +const { getGun, getUser } = require('./Mediator') + +/** + * @param {string} rawPath + * @param {import('./contact-api/SimpleGUN').ValidDataValue} value + * @returns {Promise} + */ +const put = async (rawPath, value) => { + const [root, ...path] = rawPath.split('.') + + const node = (() => { + // eslint-disable-next-line init-declarations + let _node + + if (root === '$gun') { + _node = getGun() + } else if (root === '$user') { + const u = getUser() + + if (!u.is) { + throw new Error(Constants.ErrorCode.NOT_AUTH) + } + + _node = u + } else { + throw new TypeError( + `Unknown kind of root, expected $gun or $user but got: ${root}` + ) + } + + for (const bit of path) { + _node = _node.get(bit) + } + + return _node + })() + + await makePromise((res, rej) => { + node.put(value, ack => { + if (ack.err && typeof ack.err !== 'number') { + rej(new Error(ack.err)) + } else { + res() + } + }) + }) +} + +/** + * @param {string} rawPath + * @param {import('./contact-api/SimpleGUN').ValidDataValue} value + * @returns {Promise} + */ +const set = async (rawPath, value) => { + const [root, ...path] = rawPath.split('.') + + const node = (() => { + // eslint-disable-next-line init-declarations + let _node + + if (root === '$gun') { + _node = getGun() + } else if (root === '$user') { + const u = getUser() + + if (!u.is) { + throw new Error(Constants.ErrorCode.NOT_AUTH) + } + + _node = u + } else { + throw new TypeError( + `Unknown kind of root, expected $gun or $user but got: ${root}` + ) + } + + for (const bit of path) { + _node = _node.get(bit) + } + + return _node + })() + + const id = await makePromise((res, rej) => { + const subNode = node.set(value, ack => { + if (ack.err && typeof ack.err !== 'number') { + rej(new Error(ack.err)) + } else { + res(subNode._.get) + } + }) + }) + + return id +} + +module.exports = { + put, + set +} diff --git a/src/routes.js b/src/routes.js index 8aa1fc18..eb78f6da 100644 --- a/src/routes.js +++ b/src/routes.js @@ -36,6 +36,7 @@ const { listPayments } = require('../utils/lightningServices/v2') const { startTipStatusJob } = require('../utils/lndJobs') +const GunWriteRPC = require('../services/gunDB/rpc') const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10 const SESSION_ID = uuid() @@ -3116,4 +3117,41 @@ module.exports = async ( }) } }) + + ap.post('/api/gun/put', async (req, res) => { + try { + const { path, value } = req.body + + await GunWriteRPC.put(path, value) + + res.status(200).json({ + ok: true + }) + } catch (err) { + res + .status(err.message === Common.Constants.ErrorCode.NOT_AUTH ? 401 : 500) + .json({ + errorMessage: err.message + }) + } + }) + + ap.post('/api/gun/set', async (req, res) => { + try { + const { path, value } = req.body + + const id = await GunWriteRPC.set(path, value) + + res.status(200).json({ + ok: true, + id + }) + } catch (err) { + res + .status(err.message === Common.Constants.ErrorCode.NOT_AUTH ? 401 : 500) + .json({ + errorMessage: err.message + }) + } + }) } From 4fe541b02cc29272bff14e580e33976496a0896e Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Tue, 6 Oct 2020 13:16:03 -0400 Subject: [PATCH 21/74] refactor --- services/gunDB/rpc.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/gunDB/rpc.js b/services/gunDB/rpc.js index 3a4bf36d..4257ef3b 100644 --- a/services/gunDB/rpc.js +++ b/services/gunDB/rpc.js @@ -3,12 +3,15 @@ */ // @ts-check const { makePromise, Constants } = require('shock-common') +/** + * @typedef {import('./contact-api/SimpleGUN').ValidDataValue} ValidDataValue + */ const { getGun, getUser } = require('./Mediator') /** * @param {string} rawPath - * @param {import('./contact-api/SimpleGUN').ValidDataValue} value + * @param {ValidDataValue} value * @returns {Promise} */ const put = async (rawPath, value) => { @@ -54,7 +57,7 @@ const put = async (rawPath, value) => { /** * @param {string} rawPath - * @param {import('./contact-api/SimpleGUN').ValidDataValue} value + * @param {ValidDataValue} value * @returns {Promise} */ const set = async (rawPath, value) => { From b39f5f796a5a62d8c65394e79823f83037a5705a Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Tue, 6 Oct 2020 14:08:37 -0400 Subject: [PATCH 22/74] remove unused dep --- package.json | 1 - yarn.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/package.json b/package.json index 89a82a2b..7b5ca66a 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ }, "devDependencies": { "@babel/plugin-proposal-class-properties": "^7.5.5", - "@types/bluebird": "*", "@types/dotenv": "^6.1.1", "@types/express": "^4.17.1", "@types/gun": "^0.9.2", diff --git a/yarn.lock b/yarn.lock index 4a2e9fb2..2ac52f51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -565,11 +565,6 @@ dependencies: "@babel/types" "^7.3.0" -"@types/bluebird@*": - version "3.5.31" - resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.31.tgz#d17fa0ec242b51c3db302481c557ce3813bf45cb" - integrity sha512-0PKlnDIxOh3xJHwJpVONR2PP11LhdM+QYiLJGLIbzMqRwLAPxN6lQar2RpdRhfIEh/HjVMgMdhHWJA0CgC5X6w== - "@types/body-parser@*": version "1.17.1" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.1.tgz#18fcf61768fb5c30ccc508c21d6fd2e8b3bf7897" From fe49ab2afcc8909b40655af9729e881696c998c8 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Tue, 6 Oct 2020 14:10:25 -0400 Subject: [PATCH 23/74] add bluebird --- package.json | 2 ++ yarn.lock | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/package.json b/package.json index 7b5ca66a..914a4fbd 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "basic-auth": "^2.0.0", "big.js": "^5.2.2", "bitcore-lib": "^0.15.0", + "bluebird": "^3.7.2", "body-parser": "^1.16.0", "colors": "^1.4.0", "command-exists": "^1.2.6", @@ -57,6 +58,7 @@ }, "devDependencies": { "@babel/plugin-proposal-class-properties": "^7.5.5", + "@types/bluebird": "^3.5.32", "@types/dotenv": "^6.1.1", "@types/express": "^4.17.1", "@types/gun": "^0.9.2", diff --git a/yarn.lock b/yarn.lock index 2ac52f51..a9473748 100644 --- a/yarn.lock +++ b/yarn.lock @@ -565,6 +565,11 @@ dependencies: "@babel/types" "^7.3.0" +"@types/bluebird@^3.5.32": + version "3.5.32" + resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.32.tgz#381e7b59e39f010d20bbf7e044e48f5caf1ab620" + integrity sha512-dIOxFfI0C+jz89g6lQ+TqhGgPQ0MxSnh/E4xuC0blhFtyW269+mPG5QeLgbdwst/LvdP8o1y0o/Gz5EHXLec/g== + "@types/body-parser@*": version "1.17.1" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.1.tgz#18fcf61768fb5c30ccc508c21d6fd2e8b3bf7897" @@ -1330,6 +1335,11 @@ bluebird@^3.5.0: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== +bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + bn.js@=4.11.8, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" From 85c493e18f9a752e15cd11eeb9c80789a7780ea2 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Tue, 6 Oct 2020 14:35:54 -0400 Subject: [PATCH 24/74] support encryption --- services/gunDB/rpc.js | 51 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/services/gunDB/rpc.js b/services/gunDB/rpc.js index 4257ef3b..66213000 100644 --- a/services/gunDB/rpc.js +++ b/services/gunDB/rpc.js @@ -2,12 +2,51 @@ * @format */ // @ts-check -const { makePromise, Constants } = require('shock-common') +const { makePromise, Constants, Schema } = require('shock-common') +const mapValues = require('lodash/mapValues') +const Bluebird = require('bluebird') + +const { pubToEpub } = require('./contact-api/utils') +const { getGun, getUser, mySEA: SEA, getMySecret } = require('./Mediator') /** * @typedef {import('./contact-api/SimpleGUN').ValidDataValue} ValidDataValue */ -const { getGun, getUser } = require('./Mediator') +/** + * @param {ValidDataValue} value + * @returns {Promise} + */ +const deepEncryptIfNeeded = async value => { + const u = getUser() + + if (!u.is) { + throw new Error(Constants.ErrorCode.NOT_AUTH) + } + + if (!Schema.isObj(value)) { + return value + } + + const pk = /** @type {string|undefined} */ (value.$$__ENCRYPT__FOR) + + if (!pk) { + return Bluebird.props(mapValues(value, deepEncryptIfNeeded)) + } + + const actualValue = /** @type {string} */ (value.value) + + let encryptedValue = '' + + if (pk === u.is.pub) { + encryptedValue = await SEA.encrypt(actualValue, getMySecret()) + } else { + const sec = await SEA.secret(await pubToEpub(pk), u._.sea) + + encryptedValue = await SEA.encrypt(actualValue, sec) + } + + return encryptedValue +} /** * @param {string} rawPath @@ -44,8 +83,10 @@ const put = async (rawPath, value) => { return _node })() + const encryptedIfNeededValue = await deepEncryptIfNeeded(value) + await makePromise((res, rej) => { - node.put(value, ack => { + node.put(encryptedIfNeededValue, ack => { if (ack.err && typeof ack.err !== 'number') { rej(new Error(ack.err)) } else { @@ -90,8 +131,10 @@ const set = async (rawPath, value) => { return _node })() + const encryptedIfNeededValue = await deepEncryptIfNeeded(value) + const id = await makePromise((res, rej) => { - const subNode = node.set(value, ack => { + const subNode = node.set(encryptedIfNeededValue, ack => { if (ack.err && typeof ack.err !== 'number') { rej(new Error(ack.err)) } else { From 1eb6c659f731e15cb9fad8d13bc17a3702b730fc Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Wed, 7 Oct 2020 10:06:14 -0400 Subject: [PATCH 25/74] export constant --- services/gunDB/Mediator/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/gunDB/Mediator/index.js b/services/gunDB/Mediator/index.js index 009ef65f..e5daf500 100644 --- a/services/gunDB/Mediator/index.js +++ b/services/gunDB/Mediator/index.js @@ -1475,5 +1475,6 @@ module.exports = { getUser, mySEA, getMySecret, - freshGun + freshGun, + $$__SHOCKWALLET__ENCRYPTED__ } From b856b901f684d4cc15aec32c20793754da28d297 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Wed, 7 Oct 2020 14:09:23 -0400 Subject: [PATCH 26/74] socket typings --- services/gunDB/Mediator/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/gunDB/Mediator/index.js b/services/gunDB/Mediator/index.js index e5daf500..cb1cc805 100644 --- a/services/gunDB/Mediator/index.js +++ b/services/gunDB/Mediator/index.js @@ -205,7 +205,7 @@ const Config = require('../config') // TO DO: move to common repo /** * @typedef {object} SimpleSocket - * @prop {(eventName: string, data: Emission|EncryptedEmission) => void} emit + * @prop {(eventName: string, data?: Emission|EncryptedEmission) => void} emit * @prop {(eventName: string, handler: (data: any) => void) => void} on * @prop {{ query: { 'x-shockwallet-device-id': string }}} handshake */ From 3072646c27786024ecf3ccfd3dd6e15f175abe34 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Wed, 7 Oct 2020 14:17:12 -0400 Subject: [PATCH 27/74] deepDecryptIfNeeded() --- services/gunDB/rpc.js | 48 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/services/gunDB/rpc.js b/services/gunDB/rpc.js index 66213000..3611789b 100644 --- a/services/gunDB/rpc.js +++ b/services/gunDB/rpc.js @@ -7,11 +7,53 @@ const mapValues = require('lodash/mapValues') const Bluebird = require('bluebird') const { pubToEpub } = require('./contact-api/utils') -const { getGun, getUser, mySEA: SEA, getMySecret } = require('./Mediator') +const { + getGun, + getUser, + mySEA: SEA, + getMySecret, + $$__SHOCKWALLET__ENCRYPTED__ +} = require('./Mediator') /** * @typedef {import('./contact-api/SimpleGUN').ValidDataValue} ValidDataValue */ +/** + * @param {ValidDataValue} value + * @param {string} publicKey + * @returns {Promise} + */ +const deepDecryptIfNeeded = async (value, publicKey) => { + if (Schema.isObj(value)) { + return Bluebird.props( + mapValues(value, o => deepDecryptIfNeeded(o, publicKey)) + ) + } + + if ( + typeof value === 'string' && + value.indexOf($$__SHOCKWALLET__ENCRYPTED__) === 0 + ) { + const user = getUser() + if (!user.is) { + throw new Error(Constants.ErrorCode.NOT_AUTH) + } + + let sec = '' + if (user.is.pub === publicKey) { + sec = getMySecret() + } else { + sec = await SEA.secret(publicKey, user._.sea) + } + + const decrypted = SEA.decrypt(value, sec) + + return decrypted + } + + return value +} + /** * @param {ValidDataValue} value * @returns {Promise} @@ -148,5 +190,7 @@ const set = async (rawPath, value) => { module.exports = { put, - set + set, + deepDecryptIfNeeded, + deepEncryptIfNeeded } From b8199660ca7d68285ea5427cf12f18598c5719ce Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Wed, 7 Oct 2020 14:18:00 -0400 Subject: [PATCH 28/74] chore --- src/sockets.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sockets.js b/src/sockets.js index 4feecfdc..787e176c 100644 --- a/src/sockets.js +++ b/src/sockets.js @@ -1,7 +1,10 @@ -/** @prettier */ +/** + * @format + */ // @ts-check const logger = require('winston') + const Encryption = require('../utils/encryptionStore') const LightningServices = require('../utils/lightningServices') const { From 9069ae092175c31df98d630f38eb873e90da8c54 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Wed, 7 Oct 2020 15:06:13 -0400 Subject: [PATCH 29/74] decryption for socket gun rpc --- src/sockets.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/sockets.js b/src/sockets.js index 787e176c..5a8b4890 100644 --- a/src/sockets.js +++ b/src/sockets.js @@ -12,7 +12,16 @@ const { getUser, isAuthenticated } = require('../services/gunDB/Mediator') +const { deepDecryptIfNeeded } = require('../services/gunDB/rpc') +/** + * @typedef {import('../services/gunDB/Mediator').SimpleSocket} SimpleSocket + * @typedef {import('../services/gunDB/contact-api/SimpleGUN').ValidDataValue} ValidDataValue + */ +/** + * @param {SimpleSocket} socket + * @param {string} subID + */ const onPing = (socket, subID) => { logger.warn('Subscribing to pings socket...' + subID) @@ -299,7 +308,7 @@ module.exports = ( return } - const { $shock } = socket.handshake.query + const { $shock, publicKeyForDecryption } = socket.handshake.query const [root, path, method] = $shock.split('::') @@ -319,12 +328,21 @@ module.exports = ( } /** - * @param {unknown} data + * @param {ValidDataValue} data * @param {string} key */ - const listener = (data, key) => { + const listener = async (data, key) => { try { - socket.emit('$shock', data, key) + if (publicKeyForDecryption) { + const decData = await deepDecryptIfNeeded( + data, + publicKeyForDecryption + ) + + socket.emit('$shock', decData, key) + } else { + socket.emit('$shock', data, key) + } } catch (err) { logger.error( `Error for gun rpc socket, query ${$shock} -> ${err.message}` From 1ab388b72ff1cd59f471f97ea997c43915a5768b Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Wed, 7 Oct 2020 15:06:36 -0400 Subject: [PATCH 30/74] decryption for gun http rpc --- src/routes.js | 55 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/src/routes.js b/src/routes.js index eb78f6da..70cf450a 100644 --- a/src/routes.js +++ b/src/routes.js @@ -2995,12 +2995,19 @@ module.exports = async ( * @prop {boolean} startFromUserGraph * @prop {string} path * @prop {string=} publicKey + * @prop {string=} publicKeyForDecryption */ /** * @param {HandleGunFetchParams} args0 * @returns {Promise} */ - const handleGunFetch = ({ type, startFromUserGraph, path, publicKey }) => { + const handleGunFetch = ({ + type, + startFromUserGraph, + path, + publicKey, + publicKeyForDecryption + }) => { const keys = path.split('.') const { tryAndWait } = require('../services/gunDB/contact-api/utils') console.log(keys) @@ -3014,76 +3021,106 @@ module.exports = async ( keys.forEach(key => (node = node.get(key))) return new Promise(res => { - if (type === 'once') node.once(data => res(data)) - if (type === 'load') node.load(data => res(data)) + const listener = async data => { + if (publicKeyForDecryption) { + res( + await GunWriteRPC.deepDecryptIfNeeded( + data, + publicKeyForDecryption + ) + ) + } else { + res(data) + } + } + + if (type === 'once') node.once(listener) + if (type === 'load') node.load(listener) }) }) } + /** + * Used decryption of incoming data. + */ + const PUBKEY_FOR_DECRYPT_HEADER = 'public-key-for-decryption' + ap.get('/api/gun/once/:path', async (req, res) => { + const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER) const { path } = req.params res.status(200).json({ data: await handleGunFetch({ path, startFromUserGraph: false, - type: 'once' + type: 'once', + publicKeyForDecryption }) }) }) ap.get('/api/gun/load/:path', async (req, res) => { + const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER) const { path } = req.params res.status(200).json({ data: await handleGunFetch({ path, startFromUserGraph: false, - type: 'load' + type: 'load', + publicKeyForDecryption }) }) }) ap.get('/api/gun/user/once/:path', async (req, res) => { + const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER) const { path } = req.params res.status(200).json({ data: await handleGunFetch({ path, startFromUserGraph: true, - type: 'once' + type: 'once', + publicKeyForDecryption }) }) }) ap.get('/api/gun/user/load/:path', async (req, res) => { + const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER) const { path } = req.params res.status(200).json({ data: await handleGunFetch({ path, startFromUserGraph: true, - type: 'load' + type: 'load', + publicKeyForDecryption }) }) }) ap.get('/api/gun/otheruser/:publicKey/once/:path', async (req, res) => { + const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER) const { path, publicKey } = req.params res.status(200).json({ data: await handleGunFetch({ path, startFromUserGraph: false, type: 'once', - publicKey + publicKey, + publicKeyForDecryption }) }) }) ap.get('/api/gun/otheruser/:publicKey/load/:path', async (req, res) => { + const publicKeyForDecryption = req.header(PUBKEY_FOR_DECRYPT_HEADER) const { path, publicKey } = req.params res.status(200).json({ data: await handleGunFetch({ path, startFromUserGraph: false, type: 'load', - publicKey + publicKey, + publicKeyForDecryption }) }) }) From 717b570ec95ccde9d7c2cee05800f09ef858a182 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Thu, 8 Oct 2020 07:35:56 -0400 Subject: [PATCH 31/74] remove log --- src/routes.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/routes.js b/src/routes.js index 70cf450a..0e51da48 100644 --- a/src/routes.js +++ b/src/routes.js @@ -3010,7 +3010,6 @@ module.exports = async ( }) => { const keys = path.split('.') const { tryAndWait } = require('../services/gunDB/contact-api/utils') - console.log(keys) return tryAndWait((gun, user) => { // eslint-disable-next-line no-nested-ternary let node = startFromUserGraph From 3a5f3bd04d1cdeb4e12dc74f80f82d03d132ea83 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Thu, 8 Oct 2020 07:36:10 -0400 Subject: [PATCH 32/74] use ePub not pub --- services/gunDB/rpc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/gunDB/rpc.js b/services/gunDB/rpc.js index 3611789b..8d56592d 100644 --- a/services/gunDB/rpc.js +++ b/services/gunDB/rpc.js @@ -43,7 +43,7 @@ const deepDecryptIfNeeded = async (value, publicKey) => { if (user.is.pub === publicKey) { sec = getMySecret() } else { - sec = await SEA.secret(publicKey, user._.sea) + sec = await SEA.secret(await pubToEpub(publicKey), user._.sea) } const decrypted = SEA.decrypt(value, sec) From 45b972309018ad4b8ac501bdcde3ed91c0aca3e1 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Thu, 8 Oct 2020 11:36:14 -0400 Subject: [PATCH 33/74] upgrade shock-common --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 914a4fbd..932fa9ff 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "request-promise": "^4.2.2", "response-time": "^2.3.2", "shelljs": "^0.8.2", - "shock-common": "11.0.0", + "shock-common": "12.0.1", "socket.io": "2.1.1", "text-encoding": "^0.7.0", "tingodb": "^0.6.1", diff --git a/yarn.lock b/yarn.lock index a9473748..cbb6dc9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6021,10 +6021,10 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -shock-common@11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-11.0.0.tgz#7bac5b614df2a62f29ec9ba116bb0f49d4dc8894" - integrity sha512-0ie3WY5Fovqh6k/oIc1z3ia4r9COhpH0LsF1BXNy2n9MAwnk5zkXG7K0qSppPsBXZ0aN8RFXvvmTXssRTrXcSA== +shock-common@12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-12.0.1.tgz#e2adef5d16dabdb97fc0ede5b5defbf253ca383e" + integrity sha512-V5yON0QWmLv2TBBwiv3gjgY/IXhCOx92OQ0OHz0quHgq1p9T5Jj1S3hGeKPYQ4UWuFK/5OfJM3UfSh45+MRuIw== dependencies: immer "^6.0.6" lodash "^4.17.19" From e9f12850f2ffeef218155391d71e15ab8b1164e9 Mon Sep 17 00:00:00 2001 From: hatim boufnichel Date: Thu, 8 Oct 2020 20:07:06 +0200 Subject: [PATCH 34/74] unique fileds for encrypton, wall route --- src/routes.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/routes.js b/src/routes.js index 0e51da48..8ed44da5 100644 --- a/src/routes.js +++ b/src/routes.js @@ -306,9 +306,9 @@ module.exports = async ( } } else { encryptedToken = req.body.token - encryptedKey = req.body.encryptionKey + encryptedKey = req.body.encryptionKey || req.body.encryptedKey IV = req.body.iv - reqData = req.body.data + reqData = req.body.data || req.body.encryptedData } const decryptedKey = Encryption.decryptKey({ deviceId, @@ -2367,6 +2367,17 @@ module.exports = async ( } } + ap.get('/api/gun/initwall', async (req, res) => { + try { + await GunActions.initWall() + res.json({ ok: true }) + } catch (err) { + logger.error(err) + return res.status(500).json({ + errorMessage: err.message + }) + } + }) ap.get('/api/gun/follows/', apiGunFollowsGet) ap.get('/api/gun/follows/:publicKey', apiGunFollowsGet) ap.put(`/api/gun/follows/:publicKey`, apiGunFollowsPut) From 429bb85632b11dcaacd1662c8496e710bddb8f81 Mon Sep 17 00:00:00 2001 From: hatim boufnichel Date: Sat, 10 Oct 2020 17:46:13 +0200 Subject: [PATCH 35/74] add delete and and page --- services/gunDB/contact-api/actions.js | 21 ++++++++++++++++++--- src/routes.js | 24 +++++++++++++++++++----- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/services/gunDB/contact-api/actions.js b/services/gunDB/contact-api/actions.js index b0fa8070..18b59f21 100644 --- a/services/gunDB/contact-api/actions.js +++ b/services/gunDB/contact-api/actions.js @@ -1325,6 +1325,7 @@ const createPost = async (tags, title, content) => { date: Date.now(), status: 'publish', tags: tags.join('-'), + page: pageIdx, title }, ack => { @@ -1435,11 +1436,25 @@ const createPost = async (tags, title, content) => { /** * @param {string} postId + * @param {string} page * @returns {Promise} */ -const deletePost = async postId => { - await new Promise(res => { - res(postId) +const deletePost = async (postId, page) => { + await new Promise((res, rej) => { + require('../Mediator') + .getUser() + .get(Key.WALL) + .get(Key.PAGES) + .get(page) + .get(Key.POSTS) + .get(postId) + .put(null, ack => { + if (ack.err && typeof ack.err !== 'number') { + rej(new Error(ack.err)) + } else { + res() + } + }) }) } diff --git a/src/routes.js b/src/routes.js index 8ed44da5..57211dc1 100644 --- a/src/routes.js +++ b/src/routes.js @@ -2289,11 +2289,25 @@ module.exports = async ( } }) - app.delete(`/api/gun/wall/:postID`, (_, res) => - res.status(200).json({ - ok: 'true' - }) - ) + app.delete(`/api/gun/wall/:postInfo`, async (req, res) => { + try { + const { postInfo } = req.params + const parts = postInfo.split('&') + const [page, postId] = parts + if (!page || !postId) { + throw new Error(`please provide a "postId" and a "page"`) + } + await GunActions.deletePost(postId, page) + return res.status(200).json({ + ok: 'true' + }) + } catch (e) { + return res.status(500).json({ + errorMessage: + (typeof e === 'string' ? e : e.message) || 'Unknown error.' + }) + } + }) ///////////////////////////////// /** * @template P From 9c249e73ea4ec76c1cf4721584cac454aafdb1b8 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Mon, 12 Oct 2020 09:23:24 -0400 Subject: [PATCH 36/74] serialize buffers like in http --- src/sockets.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/sockets.js b/src/sockets.js index 5a8b4890..624fe38a 100644 --- a/src/sockets.js +++ b/src/sockets.js @@ -4,6 +4,8 @@ // @ts-check const logger = require('winston') +const Common = require('shock-common') +const mapValues = require('lodash/mapValues') const Encryption = require('../utils/encryptionStore') const LightningServices = require('../utils/lightningServices') @@ -384,7 +386,22 @@ module.exports = ( const call = services[service][method](args) - call.on('data', data => { + call.on('data', _data => { + // socket.io serializes buffers differently from express + const data = (() => { + if (!Common.Schema.isObj(_data)) { + return _data + } + + return mapValues(_data, (item, key) => { + if (!(item instanceof Buffer)) { + return item + } + + return item.toJSON() + }) + })() + socket.emit('data', data) }) From 9aa3c659ba30121ef1458d9096cd740d18a4845f Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Mon, 12 Oct 2020 09:35:56 -0400 Subject: [PATCH 37/74] upgrade shock-common --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 932fa9ff..f86f5033 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "request-promise": "^4.2.2", "response-time": "^2.3.2", "shelljs": "^0.8.2", - "shock-common": "12.0.1", + "shock-common": "^13.0.1", "socket.io": "2.1.1", "text-encoding": "^0.7.0", "tingodb": "^0.6.1", diff --git a/yarn.lock b/yarn.lock index cbb6dc9e..8bc2f20b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6021,10 +6021,10 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -shock-common@12.0.1: - version "12.0.1" - resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-12.0.1.tgz#e2adef5d16dabdb97fc0ede5b5defbf253ca383e" - integrity sha512-V5yON0QWmLv2TBBwiv3gjgY/IXhCOx92OQ0OHz0quHgq1p9T5Jj1S3hGeKPYQ4UWuFK/5OfJM3UfSh45+MRuIw== +shock-common@^13.0.1: + version "13.0.1" + resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-13.0.1.tgz#f0916e51e63311b90509e5831896abc7c9febbcf" + integrity sha512-lXyS1PFrQzE3yPvnPxACkQKTLuuu52s4gqw4hZ9vmmlJX3fh8cV+jZ9E1/zblyxPayZhjQPvEX13NZD2xDDwiQ== dependencies: immer "^6.0.6" lodash "^4.17.19" From 32d7af3621d0c5380963b563bc34d74d302422d1 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Wed, 14 Oct 2020 14:39:13 -0400 Subject: [PATCH 38/74] avatar now inside of profileBinary subnode, remove poll endpoint --- services/gunDB/contact-api/actions.js | 2 +- services/gunDB/contact-api/events/index.js | 2 +- services/gunDB/contact-api/key.js | 2 ++ services/gunDB/contact-api/streams/index.js | 2 +- src/routes.js | 23 --------------------- 5 files changed, 5 insertions(+), 26 deletions(-) diff --git a/services/gunDB/contact-api/actions.js b/services/gunDB/contact-api/actions.js index 18b59f21..9308303e 100644 --- a/services/gunDB/contact-api/actions.js +++ b/services/gunDB/contact-api/actions.js @@ -815,7 +815,7 @@ const setAvatar = (avatar, user) => } user - .get(Key.PROFILE) + .get(Key.PROFILE_BINARY) .get(Key.AVATAR) .put(avatar, ack => { if (ack.err && typeof ack.err !== 'number') { diff --git a/services/gunDB/contact-api/events/index.js b/services/gunDB/contact-api/events/index.js index 24f66a0b..5f7de532 100644 --- a/services/gunDB/contact-api/events/index.js +++ b/services/gunDB/contact-api/events/index.js @@ -114,7 +114,7 @@ const onAvatar = (cb, user) => { if (!avatarSubbed) { avatarSubbed = true user - .get(Key.PROFILE) + .get(Key.PROFILE_BINARY) .get(Key.AVATAR) .on(avatar => { if (typeof avatar === 'string' || avatar === null) { diff --git a/services/gunDB/contact-api/key.js b/services/gunDB/contact-api/key.js index 5bc5ad04..58d43fba 100644 --- a/services/gunDB/contact-api/key.js +++ b/services/gunDB/contact-api/key.js @@ -60,3 +60,5 @@ exports.POSTS = 'posts' exports.TOTAL_TIPS = 'totalTips' exports.TIPS_PAYMENT_STATUS = 'tipsPaymentStatus' + +exports.PROFILE_BINARY = 'profileBinary' diff --git a/services/gunDB/contact-api/streams/index.js b/services/gunDB/contact-api/streams/index.js index d4fab683..0b211bcd 100644 --- a/services/gunDB/contact-api/streams/index.js +++ b/services/gunDB/contact-api/streams/index.js @@ -34,7 +34,7 @@ const onAvatar = (cb, pub) => { require('../../Mediator') .getGun() .user(pub) - .get(Key.PROFILE) + .get(Key.PROFILE_BINARY) .get(Key.AVATAR) .on(av => { if (typeof av === 'string' || av === null) { diff --git a/src/routes.js b/src/routes.js index 57211dc1..1d56f0eb 100644 --- a/src/routes.js +++ b/src/routes.js @@ -2107,29 +2107,6 @@ module.exports = async ( } }) - app.get(`/api/gun/${GunEvent.ON_AVATAR}`, async (_, res) => { - try { - const user = require('../services/gunDB/Mediator').getUser() - const data = await timeout5( - user - .get(Key.PROFILE) - .get(Key.AVATAR) - .then() - ) - res.json({ - data - }) - } catch (err) { - logger.info('Error in Avatar poll:') - logger.error(err) - res - .status(err.message === Common.Constants.ErrorCode.NOT_AUTH ? 401 : 500) - .json({ - errorMessage: typeof err === 'string' ? err : err.message - }) - } - }) - app.get(`/api/gun/${GunEvent.ON_DISPLAY_NAME}`, async (_, res) => { try { const user = require('../services/gunDB/Mediator').getUser() From 4d126ef1807d4c52ad42bd0a2a6cd85b4f986881 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Fri, 16 Oct 2020 10:22:41 -0400 Subject: [PATCH 39/74] default peers --- guntest.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/guntest.html b/guntest.html index 1671aa1b..5ebc98fe 100644 --- a/guntest.html +++ b/guntest.html @@ -21,8 +21,9 @@