From a4ebba93c5aede1b2c70f2f8007a571842600754 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Fri, 27 Mar 2020 18:53:49 -0400 Subject: [PATCH 01/10] EncSpontPayment --- services/gunDB/contact-api/schema-types.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 services/gunDB/contact-api/schema-types.ts diff --git a/services/gunDB/contact-api/schema-types.ts b/services/gunDB/contact-api/schema-types.ts new file mode 100644 index 00000000..2d96ab21 --- /dev/null +++ b/services/gunDB/contact-api/schema-types.ts @@ -0,0 +1,15 @@ +/** + * @format + * Contains types that are used throughout the application. Written in + * typescript for easier implementation. + * + * Nominal types are archieved through the enum method as outlined here: + * https://basarat.gitbook.io/typescript/main-1/nominaltyping + */ +enum _EncSpontPayment { + _ = '' +} +/** + * Spontaneous payment as found inside a chat message body. + */ +export type EncSpontPayment = _EncSpontPayment & string From b5d2ddf2bc4bc10e6983fa13df3631589975c2b6 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Fri, 27 Mar 2020 18:55:03 -0400 Subject: [PATCH 02/10] handle encoding/decoding spontpays --- services/gunDB/contact-api/schema.js | 107 +++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/services/gunDB/contact-api/schema.js b/services/gunDB/contact-api/schema.js index 435c0ec4..9f2035ef 100644 --- a/services/gunDB/contact-api/schema.js +++ b/services/gunDB/contact-api/schema.js @@ -1,6 +1,11 @@ /** * @format */ +const logger = require('winston') +const isFinite = require('lodash/isFinite') +const isNumber = require('lodash/isNumber') +const isNaN = require('lodash/isNaN') + /** * @typedef {object} HandshakeRequest * @prop {string} from Public key of the requestor. @@ -410,3 +415,105 @@ exports.isOrderResponse = o => { return obj.type === 'err' || obj.type === 'invoice' } + +/** + * @typedef {import('./schema-types').EncSpontPayment} EncSpontPayment + */ + +const ENC_SPONT_PAYMENT_PREFIX = '$$__SHOCKWALLET__SPONT__PAYMENT__' + +/** + * @param {string} s + * @returns {s is EncSpontPayment} + */ +const isEncodedSpontPayment = s => s.indexOf(ENC_SPONT_PAYMENT_PREFIX) === 0 + +exports.isEncodedSpontPayment = isEncodedSpontPayment + +/** + * @typedef {object} SpontaneousPayment + * @prop {number} amt + * @prop {string} memo + * @prop {string} preimage + */ + +/** + * + * @param {EncSpontPayment} sp + * @throws {Error} If decoding fails. + * @returns {SpontaneousPayment} + */ +exports.decodeSpontPayment = sp => { + try { + const [preimage, amtStr, memo] = sp + .slice(ENC_SPONT_PAYMENT_PREFIX.length) + .split('__') + + if (typeof preimage !== 'string') { + throw new TypeError('Could not parse preimage') + } + + if (typeof amtStr !== 'string') { + throw new TypeError('Could not parse amt') + } + + if (typeof memo !== 'string') { + throw new TypeError('Could not parse memo') + } + + const amt = Number(amtStr) + + if (!isNumber(amt)) { + throw new TypeError(`Could parse amount as a number, not a number?`) + } + + if (isNaN(amt)) { + throw new TypeError(`Could not amount as a number, got NaN.`) + } + + if (!isFinite(amt)) { + throw new TypeError( + `Amount was correctly parsed, but got a non finite number.` + ) + } + + return { + amt, + memo, + preimage + } + } catch (err) { + logger.debug(`Encoded spontaneous payment: ${sp}`) + logger.error(err) + throw err + } +} + +/** + * + * @param {number} amt + * @param {string} memo + * @param {string} preimage + * @returns {EncSpontPayment} + */ +exports.encodeSpontaneousPayment = (amt, memo, preimage) => { + if (amt <= 0) { + throw new RangeError('Amt must be greater than zero') + } + + if (memo.length < 1) { + throw new TypeError('Memo must be populated') + } + + if (preimage.length < 1) { + throw new TypeError('preimage must be populated') + } + + const enc = `${ENC_SPONT_PAYMENT_PREFIX}__${amt}__${memo}__${preimage}` + + if (isEncodedSpontPayment(enc)) { + return enc + } + + throw new Error('isEncodedSpontPayment(enc) false') +} From 18eba25fdf0418d39eec541433ee650cddd526f9 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Fri, 27 Mar 2020 21:33:52 -0400 Subject: [PATCH 03/10] remove unused imports --- services/gunDB/contact-api/actions.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/services/gunDB/contact-api/actions.js b/services/gunDB/contact-api/actions.js index 94e405ca..1cdc4265 100644 --- a/services/gunDB/contact-api/actions.js +++ b/services/gunDB/contact-api/actions.js @@ -3,9 +3,6 @@ */ const uuidv1 = require('uuid/v1') const logger = require('winston') -const isFinite = require('lodash/isFinite') -const isNumber = require('lodash/isNumber') -const isNaN = require('lodash/isNaN') const LightningServices = require('../../../utils/lightningServices') From 65b6e9e0b22fc4d47eb6fefb59b1ac05a33e2426 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Fri, 27 Mar 2020 21:36:52 -0400 Subject: [PATCH 04/10] place encoded spont payment in chat --- services/gunDB/contact-api/actions.js | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/services/gunDB/contact-api/actions.js b/services/gunDB/contact-api/actions.js index 1cdc4265..7f758808 100644 --- a/services/gunDB/contact-api/actions.js +++ b/services/gunDB/contact-api/actions.js @@ -11,7 +11,11 @@ const Getters = require('./getters') const Key = require('./key') const Utils = require('./utils') // const { promisifyGunNode: p } = Utils -const { isHandshakeRequest, isOrderResponse } = require('./schema') +const { + isHandshakeRequest, + isOrderResponse, + encodeSpontaneousPayment +} = require('./schema') /** * @typedef {import('./SimpleGUN').GUNNode} GUNNode * @typedef {import('./SimpleGUN').ISEA} ISEA @@ -1024,6 +1028,7 @@ const sendPayment = async (to, amount, memo) => { * @typedef {object} SendResponse * @prop {string|null} payment_error * @prop {any[]|null} payment_route + * @prop {string} payment_preimage */ logger.info('Will now send payment through lightning') @@ -1033,7 +1038,8 @@ const sendPayment = async (to, amount, memo) => { payment_request: orderResponse.response } - await new Promise((resolve, rej) => { + /** @type {string} */ + const preimage = await new Promise((resolve, rej) => { lightning.sendPaymentSync(sendPaymentSyncArgs, ( /** @type {SendErr=} */ err, /** @type {SendResponse} */ res @@ -1047,22 +1053,31 @@ const sendPayment = async (to, amount, memo) => { `sendPaymentSync error response: ${JSON.stringify(res)}` ) ) - } else if (!res.payment_route) { + } else if (!res.payment_route || !res.payment_preimage) { rej( new Error( - `sendPaymentSync no payment route response: ${JSON.stringify( + `sendPaymentSync no payment route response or preimage: ${JSON.stringify( res )}` ) ) } else { - resolve() + resolve(res.payment_preimage) } } else { rej(new Error('no error or response received from sendPaymentSync')) } }) }) + + if (Utils.successfulHandshakeAlreadyExists(to)) { + await sendMessage( + to, + encodeSpontaneousPayment(to, memo || 'no memo', preimage), + require('../Mediator').getUser(), + require('../Mediator').mySEA + ) + } } catch (e) { logger.error('Error inside sendPayment()') logger.error(e) From 1c3520f7850814a6926bf9ec33bfbe35d7c16e57 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Fri, 3 Apr 2020 16:05:12 -0400 Subject: [PATCH 05/10] typo --- services/gunDB/contact-api/actions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/gunDB/contact-api/actions.js b/services/gunDB/contact-api/actions.js index 7f758808..aef38070 100644 --- a/services/gunDB/contact-api/actions.js +++ b/services/gunDB/contact-api/actions.js @@ -1073,7 +1073,7 @@ const sendPayment = async (to, amount, memo) => { if (Utils.successfulHandshakeAlreadyExists(to)) { await sendMessage( to, - encodeSpontaneousPayment(to, memo || 'no memo', preimage), + encodeSpontaneousPayment(amount, memo || 'no memo', preimage), require('../Mediator').getUser(), require('../Mediator').mySEA ) From 72ca45c853acf38fa297397d253bdb8ec49d9384 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Fri, 3 Apr 2020 16:06:09 -0400 Subject: [PATCH 06/10] return preimage from sendPayment() --- services/gunDB/contact-api/actions.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/gunDB/contact-api/actions.js b/services/gunDB/contact-api/actions.js index aef38070..9ddb0502 100644 --- a/services/gunDB/contact-api/actions.js +++ b/services/gunDB/contact-api/actions.js @@ -876,12 +876,13 @@ const sendHRWithInitialMsg = async ( } /** + * Returns the preimage corresponding to the payment. * @param {string} to * @param {number} amount * @param {string} memo * @throws {Error} If no response in less than 20 seconds from the recipient, or * lightning cannot find a route for the payment. - * @returns {Promise} + * @returns {Promise} The payment's preimage. */ const sendPayment = async (to, amount, memo) => { try { @@ -1078,6 +1079,8 @@ const sendPayment = async (to, amount, memo) => { require('../Mediator').mySEA ) } + + return preimage } catch (e) { logger.error('Error inside sendPayment()') logger.error(e) From 43f9795dc79dde48923b9e0c48ab84776d1c35cd Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Fri, 3 Apr 2020 16:07:54 -0400 Subject: [PATCH 07/10] typo --- services/gunDB/Mediator/index.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/services/gunDB/Mediator/index.js b/services/gunDB/Mediator/index.js index 263da404..9b88fa5e 100644 --- a/services/gunDB/Mediator/index.js +++ b/services/gunDB/Mediator/index.js @@ -776,14 +776,7 @@ class Mediator { await throwOnInvalidToken(token) - await API.Actions.sendPayment( - recipientPub, - amount, - memo, - gun, - user, - mySEA - ) + await API.Actions.sendPayment(recipientPub, amount, memo) this.socket.emit(Action.SEND_PAYMENT, { ok: true, From d89b1284286c9df6754a0af74359487461ea197c Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Fri, 3 Apr 2020 16:24:44 -0400 Subject: [PATCH 08/10] typings and typechecking --- services/gunDB/Mediator/index.js | 8 ++++---- utils/encryptionStore.js | 28 +++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/services/gunDB/Mediator/index.js b/services/gunDB/Mediator/index.js index 9b88fa5e..0e34ba4e 100644 --- a/services/gunDB/Mediator/index.js +++ b/services/gunDB/Mediator/index.js @@ -495,7 +495,7 @@ class Mediator { field: 'deviceId', message: 'Please specify a device ID' } - logger.error(error) + logger.error(JSON.stringify(error)) return false } @@ -925,7 +925,7 @@ class Mediator { API.Events.onChats(chats => { if (Config.SHOW_LOG) { logger.info('---chats---') - logger.info(chats) + logger.info(JSON.stringify(chats)) logger.info('-----------------------') } @@ -957,7 +957,7 @@ class Mediator { API.Events.onDisplayName(displayName => { if (Config.SHOW_LOG) { logger.info('---displayName---') - logger.info(displayName) + logger.info(displayName || 'null or empty string') logger.info('-----------------------') } @@ -989,7 +989,7 @@ class Mediator { API.Events.onCurrentHandshakeAddress(addr => { if (Config.SHOW_LOG) { logger.info('---addr---') - logger.info(addr) + logger.info(addr || 'null or empty string') logger.info('-----------------------') } diff --git a/utils/encryptionStore.js b/utils/encryptionStore.js index ba4f8191..a946bc2f 100644 --- a/utils/encryptionStore.js +++ b/utils/encryptionStore.js @@ -16,7 +16,14 @@ const nonEncryptedEvents = [ ] const Encryption = { + /** + * @param {string} event + * @returns {boolean} + */ isNonEncrypted: event => nonEncryptedEvents.includes(event), + /** + * @param {{ deviceId: string , message: string }} arg0 + */ encryptKey: ({ deviceId, message }) => { if (!authorizedDevices.has(deviceId)) { throw { field: 'deviceId', message: 'Unknown Device ID' } @@ -33,6 +40,9 @@ const Encryption = { ) return encryptedData.toString('base64') }, + /** + * @param {{ deviceId: string , message: string }} arg0 + */ decryptKey: ({ deviceId, message }) => { if (!authorizedDevices.has(deviceId)) { throw { field: 'deviceId', message: 'Unknown Device ID' } @@ -49,6 +59,9 @@ const Encryption = { return encryptedData.toString() }, + /** + * @param {{ deviceId: string , message: any , metadata?: any}} arg0 + */ encryptMessage: ({ deviceId, message, metadata }) => { const parsedMessage = typeof message === 'object' ? JSON.stringify(message) : message @@ -68,6 +81,9 @@ const Encryption = { const encryptedData = encryptedBuffer.toString('base64') return { encryptedData, encryptedKey, iv: iv.toString('hex'), metadata } }, + /** + * @param {{ message: string , key: string , iv: string }} arg0 + */ decryptMessage: ({ message, key, iv }) => { const data = Buffer.from(message, 'base64') const cipher = Crypto.createDecipheriv( @@ -84,6 +100,9 @@ const Encryption = { return decryptedData.toString() }, + /** + * @param {{ deviceId: string }} arg0 + */ isAuthorizedDevice: ({ deviceId }) => { if (authorizedDevices.has(deviceId)) { return true @@ -91,6 +110,9 @@ const Encryption = { return false }, + /** + * @param {{ deviceId: string , publicKey: string }} arg0 + */ authorizeDevice: ({ deviceId, publicKey }) => new Promise((resolve, reject) => { authorizedDevices.set(deviceId, publicKey) @@ -109,9 +131,10 @@ const Encryption = { }, (err, publicKey, privateKey) => { if (err) { + // @ts-ignore logger.error(err) reject(err) - return err + return } const exportedKey = { @@ -127,6 +150,9 @@ const Encryption = { } ) }), + /** + * @param {{ deviceId: string }} arg0 + */ unAuthorizeDevice: ({ deviceId }) => { authorizedDevices.delete(deviceId) } From 36b07e87a4f968506a58e8e1fb899a0c99aba1e0 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Fri, 3 Apr 2020 16:25:02 -0400 Subject: [PATCH 09/10] send preimage for sendpayment action --- services/gunDB/Mediator/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/gunDB/Mediator/index.js b/services/gunDB/Mediator/index.js index 0e34ba4e..43627866 100644 --- a/services/gunDB/Mediator/index.js +++ b/services/gunDB/Mediator/index.js @@ -776,11 +776,11 @@ class Mediator { await throwOnInvalidToken(token) - await API.Actions.sendPayment(recipientPub, amount, memo) + const preimage = await API.Actions.sendPayment(recipientPub, amount, memo) this.socket.emit(Action.SEND_PAYMENT, { ok: true, - msg: null, + msg: preimage, origBody: reqBody }) } catch (err) { From c314c3f4e05ed835cf3959b76fce46b9b3612e78 Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Sat, 4 Apr 2020 16:54:21 -0400 Subject: [PATCH 10/10] use logger --- src/routes.js | 4 ++-- src/server.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/routes.js b/src/routes.js index 9077799d..0ce9e40a 100644 --- a/src/routes.js +++ b/src/routes.js @@ -212,7 +212,7 @@ module.exports = async ( app.use((req, res, next) => { const deviceId = req.headers["x-shockwallet-device-id"]; - console.log("Decrypting route...") + logger.debug("Decrypting route...") try { if (nonEncryptedRoutes.includes(req.path)) { return next(); @@ -1671,7 +1671,7 @@ module.exports = async ( try { const user = require('../services/gunDB/Mediator').getUser() const data = await timeout5(user.get(Key.BIO).then()) - console.log(data) + logger.debug(data) res.json({ data }) diff --git a/src/server.js b/src/server.js index 1afa8104..54f184f9 100644 --- a/src/server.js +++ b/src/server.js @@ -77,10 +77,10 @@ const server = program => { const dataHash = hashData(args[0]).slice(-8) res.set('ETag', dataHash) - console.log('ETag:', req.headers.etag) - console.log('Data Hash:', dataHash) + logger.debug('ETag:', req.headers.etag) + logger.debug('Data Hash:', dataHash) if (req.headers.etag === dataHash) { - console.log('Same Hash Detected!') + logger.debug('Same Hash Detected!') args[0] = null res.status(304) oldSend.apply(res, args)