Merge branch 'master' into feature/end-to-end-encryption

This commit is contained in:
Emad-salah 2020-01-28 06:07:34 -05:00 committed by GitHub
commit d621e659be
14 changed files with 694 additions and 40 deletions

View file

@ -62,7 +62,10 @@
"no-shadow": "off", "no-shadow": "off",
// We're usually throwing objects throughout the API to allow for more detailed error messages // We're usually throwing objects throughout the API to allow for more detailed error messages
"no-throw-literal": "off" "no-throw-literal": "off",
// lightning has sync methods and this rule bans them
"no-sync": "off"
}, },
"parser": "babel-eslint", "parser": "babel-eslint",
"env": { "env": {

View file

@ -156,6 +156,7 @@ const authenticate = async (alias, pass) => {
} }
// move this to a subscription; implement off() ? todo // move this to a subscription; implement off() ? todo
API.Jobs.onAcceptedRequests(user, mySEA) API.Jobs.onAcceptedRequests(user, mySEA)
API.Jobs.onOrders(user, gun, mySEA)
return user._.sea.pub return user._.sea.pub
} }
@ -179,6 +180,7 @@ const authenticate = async (alias, pass) => {
throw new Error(ack.err) throw new Error(ack.err)
} else if (typeof ack.sea === 'object') { } else if (typeof ack.sea === 'object') {
API.Jobs.onAcceptedRequests(user, mySEA) API.Jobs.onAcceptedRequests(user, mySEA)
API.Jobs.onOrders(user, gun, mySEA)
const mySec = await mySEA.secret(user._.sea.epub, user._.sea) const mySec = await mySEA.secret(user._.sea.epub, user._.sea)
if (typeof mySec !== 'string') { if (typeof mySec !== 'string') {
@ -301,6 +303,8 @@ class Mediator {
this.socket.on(Action.SEND_MESSAGE, this.sendMessage) this.socket.on(Action.SEND_MESSAGE, this.sendMessage)
this.socket.on(Action.SET_AVATAR, this.setAvatar) this.socket.on(Action.SET_AVATAR, this.setAvatar)
this.socket.on(Action.SET_DISPLAY_NAME, this.setDisplayName) this.socket.on(Action.SET_DISPLAY_NAME, this.setDisplayName)
this.socket.on(Action.SEND_PAYMENT, this.sendPayment)
this.socket.on(Action.SET_BIO, this.setBio)
this.socket.on(Event.ON_AVATAR, this.onAvatar) this.socket.on(Event.ON_AVATAR, this.onAvatar)
this.socket.on(Event.ON_BLACKLIST, this.onBlacklist) this.socket.on(Event.ON_BLACKLIST, this.onBlacklist)
@ -309,6 +313,8 @@ class Mediator {
this.socket.on(Event.ON_HANDSHAKE_ADDRESS, this.onHandshakeAddress) this.socket.on(Event.ON_HANDSHAKE_ADDRESS, this.onHandshakeAddress)
this.socket.on(Event.ON_RECEIVED_REQUESTS, this.onReceivedRequests) this.socket.on(Event.ON_RECEIVED_REQUESTS, this.onReceivedRequests)
this.socket.on(Event.ON_SENT_REQUESTS, this.onSentRequests) this.socket.on(Event.ON_SENT_REQUESTS, this.onSentRequests)
this.socket.on(Event.ON_BIO, this.onBio)
this.socket.on(Event.ON_SEED_BACKUP, this.onSeedBackup)
this.socket.on(IS_GUN_AUTH, this.isGunAuth) this.socket.on(IS_GUN_AUTH, this.isGunAuth)
} }
@ -640,6 +646,39 @@ class Mediator {
} }
} }
/**
* @param {Readonly<{ uuid: string, recipientPub: string, amount: number, memo: string, token: string }>} reqBody
*/
sendPayment = async reqBody => {
try {
const { recipientPub, amount, memo, token } = reqBody
await throwOnInvalidToken(token)
await API.Actions.sendPayment(
recipientPub,
amount,
memo,
gun,
user,
mySEA
)
this.socket.emit(Action.SEND_PAYMENT, {
ok: true,
msg: null,
origBody: reqBody
})
} catch (err) {
console.log(err)
this.socket.emit(Action.SEND_PAYMENT, {
ok: false,
msg: err.message,
origBody: reqBody
})
}
}
/** /**
* @param {Readonly<{ avatar: string|null , token: string }>} body * @param {Readonly<{ avatar: string|null , token: string }>} body
*/ */
@ -934,6 +973,88 @@ class Mediator {
}) })
} }
} }
/**
* @param {Readonly<{ token: string }>} body
*/
onBio = async body => {
try {
const { token } = body
await throwOnInvalidToken(token)
API.Events.onBio(bio => {
this.socket.emit(Event.ON_BIO, {
msg: bio,
ok: true,
origBody: body
})
}, user)
} catch (err) {
console.log(err)
this.socket.emit(Event.ON_BIO, {
ok: false,
msg: err.message,
origBody: body
})
}
}
/**
* @param {Readonly<{ bio: string|null , token: string }>} body
*/
setBio = async body => {
try {
const { bio, token } = body
await throwOnInvalidToken(token)
await API.Actions.setBio(bio, user)
this.socket.emit(Action.SET_BIO, {
ok: true,
msg: null,
origBody: body
})
} catch (err) {
console.log(err)
this.socket.emit(Action.SET_BIO, {
ok: false,
msg: err.message,
origBody: body
})
}
}
/**
* @param {Readonly<{ token: string }>} body
*/
onSeedBackup = async body => {
try {
const { token } = body
await throwOnInvalidToken(token)
await API.Events.onSeedBackup(
seedBackup => {
this.socket.emit(Event.ON_SEED_BACKUP, {
ok: true,
msg: seedBackup,
origBody: body
})
},
user,
mySEA
)
} catch (err) {
console.log(err)
this.socket.emit(Event.ON_SEED_BACKUP, {
ok: false,
msg: err.message,
origBody: body
})
}
}
} }
/** /**
@ -996,6 +1117,7 @@ const register = async (alias, pass) => {
return authenticate(alias, pass).then(async pub => { return authenticate(alias, pass).then(async pub => {
await API.Actions.setDisplayName('anon' + pub.slice(0, 8), user) await API.Actions.setDisplayName('anon' + pub.slice(0, 8), user)
await API.Actions.generateHandshakeAddress(user) await API.Actions.generateHandshakeAddress(user)
await API.Actions.generateOrderAddress(user)
return pub return pub
}) })
} }
@ -1033,5 +1155,6 @@ module.exports = {
register, register,
instantiateGun, instantiateGun,
getGun, getGun,
getUser getUser,
mySEA
} }

View file

@ -5,8 +5,10 @@ const Actions = {
SEND_HANDSHAKE_REQUEST: "SEND_HANDSHAKE_REQUEST", SEND_HANDSHAKE_REQUEST: "SEND_HANDSHAKE_REQUEST",
SEND_HANDSHAKE_REQUEST_WITH_INITIAL_MSG: "SEND_HANDSHAKE_REQUEST_WITH_INITIAL_MSG", SEND_HANDSHAKE_REQUEST_WITH_INITIAL_MSG: "SEND_HANDSHAKE_REQUEST_WITH_INITIAL_MSG",
SEND_MESSAGE: "SEND_MESSAGE", SEND_MESSAGE: "SEND_MESSAGE",
SEND_PAYMENT: "SEND_PAYMENT",
SET_AVATAR: "SET_AVATAR", SET_AVATAR: "SET_AVATAR",
SET_DISPLAY_NAME: "SET_DISPLAY_NAME" SET_DISPLAY_NAME: "SET_DISPLAY_NAME",
SET_BIO: "SET_BIO"
}; };
module.exports = Actions; module.exports = Actions;

View file

@ -2,7 +2,11 @@
* @format * @format
*/ */
const uuidv1 = require('uuid/v1') const uuidv1 = require('uuid/v1')
const LightningServices = require('../../../utils/lightningServices')
const ErrorCode = require('./errorCode') const ErrorCode = require('./errorCode')
const Getters = require('./getters')
const Key = require('./key') const Key = require('./key')
const Utils = require('./utils') const Utils = require('./utils')
const { isHandshakeRequest } = require('./schema') const { isHandshakeRequest } = require('./schema')
@ -15,6 +19,8 @@ const { isHandshakeRequest } = require('./schema')
* @typedef {import('./schema').Message} Message * @typedef {import('./schema').Message} Message
* @typedef {import('./schema').Outgoing} Outgoing * @typedef {import('./schema').Outgoing} Outgoing
* @typedef {import('./schema').PartialOutgoing} PartialOutgoing * @typedef {import('./schema').PartialOutgoing} PartialOutgoing
* @typedef {import('./schema').Order} Order
* @typedef {import('./SimpleGUN').Ack} Ack
*/ */
/** /**
@ -408,18 +414,8 @@ const sendHandshakeRequest = async (recipientPublicKey, gun, user, SEA) => {
console.log('sendHR() -> before mySecret') console.log('sendHR() -> before mySecret')
const mySecret = await SEA.secret(user._.sea.epub, user._.sea) const mySecret = await SEA.secret(user._.sea.epub, user._.sea)
if (typeof mySecret !== 'string') {
throw new TypeError(
"sendHandshakeRequest() -> typeof mySecret !== 'string'"
)
}
console.log('sendHR() -> before ourSecret') console.log('sendHR() -> before ourSecret')
const ourSecret = await SEA.secret(recipientEpub, user._.sea) const ourSecret = await SEA.secret(recipientEpub, user._.sea)
if (typeof ourSecret !== 'string') {
throw new TypeError(
"sendHandshakeRequest() -> typeof ourSecret !== 'string'"
)
}
// check if successful handshake is present // check if successful handshake is present
@ -660,7 +656,65 @@ const sendMessage = async (recipientPublicKey, body, user, SEA) => {
.get(Key.MESSAGES) .get(Key.MESSAGES)
.set(newMessage, ack => { .set(newMessage, ack => {
if (ack.err) { if (ack.err) {
rej(ack.err) rej(new Error(ack.err))
} else {
res()
}
})
})
}
/**
* @param {string} recipientPub
* @param {string} msgID
* @param {UserGUNNode} user
* @param {ISEA} SEA
* @returns {Promise<void>}
*/
const deleteMessage = async (recipientPub, msgID, user, SEA) => {
if (!user.is) {
throw new Error(ErrorCode.NOT_AUTH)
}
if (typeof recipientPub !== 'string') {
throw new TypeError(
`expected recipientPublicKey to be an string, but instead got: ${typeof recipientPub}`
)
}
if (recipientPub.length === 0) {
throw new TypeError(
'expected recipientPublicKey to be an string of length greater than zero'
)
}
if (typeof msgID !== 'string') {
throw new TypeError(
`expected msgID to be an string, instead got: ${typeof msgID}`
)
}
if (msgID.length === 0) {
throw new TypeError(
'expected msgID to be an string of length greater than zero'
)
}
const outgoingID = await Utils.recipientToOutgoingID(recipientPub, user, SEA)
if (outgoingID === null) {
throw new Error(`Could not fetch an outgoing id for user: ${recipientPub}`)
}
return new Promise((res, rej) => {
user
.get(Key.OUTGOINGS)
.get(outgoingID)
.get(Key.MESSAGES)
.get(msgID)
.put(null, ack => {
if (ack.err) {
rej(new Error(ack.err))
} else { } else {
res() res()
} }
@ -783,6 +837,220 @@ const sendHRWithInitialMsg = async (
await sendMessage(recipientPublicKey, initialMsg, user, SEA) await sendMessage(recipientPublicKey, initialMsg, user, SEA)
} }
/**
* @param {string} to
* @param {number} amount
* @param {string} memo
* @param {GUNNode} gun
* @param {UserGUNNode} user
* @param {ISEA} SEA
* @throws {Error} If no response in less than 20 seconds from the recipient, or
* lightning cannot find a route for the payment.
* @returns {Promise<void>}
*/
const sendPayment = async (to, amount, memo, gun, user, SEA) => {
if (!user.is) {
throw new Error(ErrorCode.NOT_AUTH)
}
const recipientEpub = await Utils.pubToEpub(to)
const ourSecret = await SEA.secret(recipientEpub, user._.sea)
if (amount < 1) {
throw new RangeError('Amount must be at least 1 sat.')
}
/** @type {Order} */
const order = {
amount: await SEA.encrypt(amount.toString(), ourSecret),
from: user._.sea.pub,
memo: await SEA.encrypt(memo || 'no memo', ourSecret),
timestamp: Date.now()
}
const currOrderAddress = await Getters.currentOrderAddress(to)
order.timestamp = Date.now()
/** @type {string} */
const orderID = await new Promise((res, rej) => {
const ord = gun
.get(Key.ORDER_NODES)
.get(currOrderAddress)
.set(order, ack => {
if (ack.err) {
rej(
new Error(
`Error writing order to order node: ${currOrderAddress} for pub: ${to}: ${ack.err}`
)
)
} else {
res(ord._.get)
}
})
})
const bob = gun.user(to)
/** @type {string} */
const invoice = await Promise.race([
new Promise((res, rej) => {
bob
.get(Key.ORDER_TO_RESPONSE)
.get(orderID)
.on(data => {
if (typeof data !== 'string') {
rej(
`Expected order response from pub ${to} to be an string, instead got: ${typeof data}`
)
} else {
res(data)
}
})
}),
new Promise((_, rej) => {
setTimeout(() => {
rej(new Error(ErrorCode.ORDER_NOT_ANSWERED_IN_TIME))
}, 20000)
})
])
const decInvoice = await SEA.decrypt(invoice, ourSecret)
const {
services: { lightning }
} = LightningServices
/**
* @typedef {object} SendErr
* @prop {string} details
*/
/**
* Partial
* https://api.lightning.community/#grpc-response-sendresponse-2
* @typedef {object} SendResponse
* @prop {string|null} payment_error
* @prop {any[]|null} payment_route
*/
await new Promise((resolve, rej) => {
lightning.sendPaymentSync(
{
payment_request: decInvoice
},
(/** @type {SendErr=} */ err, /** @type {SendResponse} */ res) => {
if (err) {
rej(new Error(err.details))
} else if (res) {
if (res.payment_error) {
rej(
new Error(
`sendPaymentSync error response: ${JSON.stringify(res)}`
)
)
} else if (!res.payment_route) {
rej(
new Error(
`sendPaymentSync no payment route response: ${JSON.stringify(
res
)}`
)
)
} else {
resolve()
}
} else {
rej(new Error('no error or response received from sendPaymentSync'))
}
}
)
})
}
/**
* @param {UserGUNNode} user
* @returns {Promise<void>}
*/
const generateOrderAddress = user =>
new Promise((res, rej) => {
if (!user.is) {
throw new Error(ErrorCode.NOT_AUTH)
}
const address = uuidv1()
user.get(Key.CURRENT_ORDER_ADDRESS).put(address, ack => {
if (ack.err) {
rej(new Error(ack.err))
} else {
res()
}
})
})
/**
* @param {string|null} bio
* @param {UserGUNNode} user
* @throws {TypeError} Rejects if avatar is not an string or an empty string.
* @returns {Promise<void>}
*/
const setBio = (bio, user) =>
new Promise((resolve, reject) => {
if (!user.is) {
throw new Error(ErrorCode.NOT_AUTH)
}
if (typeof bio === 'string' && bio.length === 0) {
throw new TypeError(
"'bio' must be an string and have length greater than one or be null"
)
}
if (typeof bio !== 'string' && bio !== null) {
throw new TypeError(
"'bio' must be an string and have length greater than one or be null"
)
}
user.get(Key.BIO).put(bio, ack => {
if (ack.err) {
reject(new Error(ack.err))
} else {
resolve()
}
})
})
/**
* @param {string[]} mnemonicPhrase
* @param {UserGUNNode} user
* @param {ISEA} SEA
* @returns {Promise<void>}
*/
const saveSeedBackup = async (mnemonicPhrase, user, SEA) => {
if (
!Array.isArray(mnemonicPhrase) ||
mnemonicPhrase.some(word => typeof word !== 'string') ||
mnemonicPhrase.length === 0
) {
throw new TypeError('expected mnemonicPhrase to be an string array')
}
const mySecret = await SEA.secret(user._.sea.epub, user._.sea)
const encryptedSeed = await SEA.encrypt(mnemonicPhrase.join(' '), mySecret)
return new Promise((res, rej) => {
user.get(Key.SEED_BACKUP).put(encryptedSeed, ack => {
if (ack.err) {
rej(ack.err)
} else {
res()
}
})
})
}
module.exports = { module.exports = {
INITIAL_MSG, INITIAL_MSG,
__createOutgoingFeed, __createOutgoingFeed,
@ -791,8 +1059,13 @@ module.exports = {
blacklist, blacklist,
generateHandshakeAddress, generateHandshakeAddress,
sendHandshakeRequest, sendHandshakeRequest,
deleteMessage,
sendMessage, sendMessage,
sendHRWithInitialMsg, sendHRWithInitialMsg,
setAvatar, setAvatar,
setDisplayName setDisplayName,
sendPayment,
generateOrderAddress,
setBio,
saveSeedBackup
} }

View file

@ -41,3 +41,5 @@ exports.ALREADY_REQUESTED_HANDSHAKE = 'ALREADY_REQUESTED_HANDSHAKE'
exports.STALE_HANDSHAKE_ADDRESS = 'STALE_HANDSHAKE_ADDRESS' exports.STALE_HANDSHAKE_ADDRESS = 'STALE_HANDSHAKE_ADDRESS'
exports.TIMEOUT_ERR = 'TIMEOUT_ERR' exports.TIMEOUT_ERR = 'TIMEOUT_ERR'
exports.ORDER_NOT_ANSWERED_IN_TIME = 'ORDER_NOT_ANSWERED_IN_TIME'

View file

@ -1204,6 +1204,60 @@ const onSimplerSentRequests = async (cb, gun, user, SEA) => {
}) })
} }
/** @type {string|null} */
let currentBio = null
/**
* @param {(bio: string|null) => void} cb
* @param {UserGUNNode} user Pass only for testing purposes.
* @throws {Error} If user hasn't been auth.
* @returns {void}
*/
const onBio = (cb, user) => {
if (!user.is) {
throw new Error(ErrorCode.NOT_AUTH)
}
const callb = debounce(cb, DEBOUNCE_WAIT_TIME)
// Initial value if avvatar is undefined in gun
callb(currentBio)
user.get(Key.BIO).on(bio => {
if (typeof bio === 'string' || bio === null) {
currentBio = bio
callb(bio)
}
})
}
/** @type {string|null} */
let currentSeedBackup = null
/**
* @param {(seedBackup: string|null) => void} cb
* @param {UserGUNNode} user
* @param {ISEA} SEA
* @throws {Error} If user hasn't been auth.
* @returns {Promise<void>}
*/
const onSeedBackup = async (cb, user, SEA) => {
if (!user.is) {
throw new Error(ErrorCode.NOT_AUTH)
}
const mySecret = await SEA.secret(user._.sea.epub, user._.sea)
const callb = debounce(cb, DEBOUNCE_WAIT_TIME)
callb(currentSeedBackup)
user.get(Key.SEED_BACKUP).on(async seedBackup => {
if (typeof seedBackup === 'string') {
currentSeedBackup = await SEA.decrypt(seedBackup, mySecret)
callb(currentSeedBackup)
}
})
}
module.exports = { module.exports = {
__onSentRequestToUser, __onSentRequestToUser,
__onUserToIncoming, __onUserToIncoming,
@ -1215,5 +1269,7 @@ module.exports = {
onOutgoing, onOutgoing,
onChats, onChats,
onSimplerReceivedRequests, onSimplerReceivedRequests,
onSimplerSentRequests onSimplerSentRequests,
onBio,
onSeedBackup
} }

View file

@ -0,0 +1,16 @@
const Key = require('./key')
const Utils = require('./utils')
/**
* @param {string} pub
* @returns {Promise<string>}
*/
exports.currentOrderAddress = async (pub) => {
const currAddr = await Utils.tryAndWait((gun) => gun.user(pub).get(Key.CURRENT_ORDER_ADDRESS).then())
if (typeof currAddr !== 'string') {
throw new TypeError('Expected user.currentOrderAddress to be an string')
}
return currAddr
}

View file

@ -0,0 +1,18 @@
/**
* @format
* Jobs are subscriptions to events that perform actions (write to GUN) on
* response to certain ways events can happen. These tasks need to be fired up
* at app launch otherwise certain features won't work as intended. Tasks should
* ideally be idempotent, that is, if they were to be fired up after a certain
* amount of time after app launch, everything should work as intended. For this
* to work, special care has to be put into how these respond to events. These
* tasks accept factories that are homonymous to the events on this same module.
*/
const onAcceptedRequests = require('./onAcceptedRequests')
const onOrders = require('./onOrders')
module.exports = {
onAcceptedRequests,
onOrders
}

View file

@ -1,24 +1,15 @@
/** /**
* @prettier * @format
* Taks are subscriptions to events that perform actions (write to GUN) on
* response to certain ways events can happen. These tasks need to be fired up
* at app launch otherwise certain features won't work as intended. Tasks should
* ideally be idempotent, that is, if they were to be fired up after a certain
* amount of time after app launch, everything should work as intended. For this
* to work, special care has to be put into how these respond to events. These
* tasks could be hardcoded inside events but then they wouldn't be easily
* auto-testable. These tasks accept factories that are homonymous to the events
* on the same
*/ */
const ErrorCode = require('./errorCode') const ErrorCode = require('../errorCode')
const Key = require('./key') const Key = require('../key')
const Schema = require('./schema') const Schema = require('../schema')
const Utils = require('./utils') const Utils = require('../utils')
/** /**
* @typedef {import('./SimpleGUN').GUNNode} GUNNode * @typedef {import('../SimpleGUN').GUNNode} GUNNode
* @typedef {import('./SimpleGUN').ISEA} ISEA * @typedef {import('../SimpleGUN').ISEA} ISEA
* @typedef {import('./SimpleGUN').UserGUNNode} UserGUNNode * @typedef {import('../SimpleGUN').UserGUNNode} UserGUNNode
*/ */
/** /**
@ -148,6 +139,4 @@ const onAcceptedRequests = async (user, SEA) => {
}) })
} }
module.exports = { module.exports = onAcceptedRequests
onAcceptedRequests
}

View file

@ -0,0 +1,116 @@
/**
* @format
*/
const LightningServices = require('../../../../utils/lightningServices')
const ErrorCode = require('../errorCode')
const Key = require('../key')
const Schema = require('../schema')
const Utils = require('../utils')
/**
* @typedef {import('../SimpleGUN').GUNNode} GUNNode
* @typedef {import('../SimpleGUN').ListenerData} ListenerData
* @typedef {import('../SimpleGUN').ISEA} ISEA
* @typedef {import('../SimpleGUN').UserGUNNode} UserGUNNode
*/
let currentOrderAddr = ''
/**
* @param {string} addr
* @param {UserGUNNode} user
* @param {ISEA} SEA
* @returns {(order: ListenerData, orderID: string) => void}
*/
const listenerForAddr = (addr, user, SEA) => async (order, orderID) => {
try {
if (addr !== currentOrderAddr) {
return
}
if (!Schema.isOrder(order)) {
throw new Error(`Expected an order instead got: ${JSON.stringify(order)}`)
}
const orderToResponse = user.get(Key.ORDER_TO_RESPONSE)
if (await orderToResponse.get(orderID).then()) {
return
}
const senderEpub = await Utils.pubToEpub(order.from)
const secret = await SEA.secret(senderEpub, user._.sea)
const amount = Number(await SEA.decrypt(order.amount, secret))
const memo = await SEA.decrypt(order.memo, secret)
/**
* @type {string}
*/
const invoice = await new Promise((resolve, rej) => {
const {
services: { lightning }
} = LightningServices
lightning.addInvoice(
{
expiry: 36000,
memo,
value: amount
},
(
/** @type {any} */ error,
/** @type {{ payment_request: string }} */ response
) => {
if (error) {
rej(error)
} else {
resolve(response.payment_request)
}
}
)
})
const encInvoice = await SEA.encrypt(invoice, secret)
orderToResponse.get(orderID).put(encInvoice, ack => {
if (ack.err) {
console.error(`error saving order response: ${ack.err}`)
}
})
} catch (err) {
console.error('error inside onOrders:')
console.error(err)
}
}
/**
* @param {UserGUNNode} user
* @param {GUNNode} gun
* @param {ISEA} SEA
* @throws {Error} NOT_AUTH
* @returns {void}
*/
const onOrders = (user, gun, SEA) => {
if (!user.is) {
throw new Error(ErrorCode.NOT_AUTH)
}
user.get(Key.CURRENT_ORDER_ADDRESS).on(addr => {
if (typeof addr !== 'string') {
throw new TypeError('Expected current order address to be an string')
}
currentOrderAddr = addr
gun
.get(Key.ORDER_NODES)
.get(addr)
.map()
.on(listenerForAddr(currentOrderAddr, user, SEA))
})
}
module.exports = onOrders

View file

@ -24,3 +24,13 @@ exports.DISPLAY_NAME = 'displayName'
* Maps user to the last request sent to them. * Maps user to the last request sent to them.
*/ */
exports.USER_TO_LAST_REQUEST_SENT = 'USER_TO_LAST_REQUEST_SENT' exports.USER_TO_LAST_REQUEST_SENT = 'USER_TO_LAST_REQUEST_SENT'
exports.CURRENT_ORDER_ADDRESS = 'currentOrderAddress'
exports.ORDER_NODES = 'orderNodes'
exports.ORDER_TO_RESPONSE = 'orderToResponse'
exports.BIO = 'bio'
exports.SEED_BACKUP = 'seedBackup'

View file

@ -1,5 +1,5 @@
/** /**
* @prettier * @format
*/ */
/** /**
* @typedef {object} HandshakeRequest * @typedef {object} HandshakeRequest
@ -316,7 +316,6 @@ exports.isPartialOutgoing = item => {
} }
/** /**
*
* @param {any} item * @param {any} item
* @returns {item is Outgoing} * @returns {item is Outgoing}
*/ */
@ -337,3 +336,41 @@ exports.isOutgoing = item => {
return typeof obj.with === 'string' && messagesAreMessages return typeof obj.with === 'string' && messagesAreMessages
} }
/**
* @typedef {object} Order
* @prop {string} from Public key of sender.
* @prop {string} amount Encrypted
* @prop {string} memo Encrypted
* @prop {number} timestamp
*/
/**
* @param {any} item
* @returns {item is Order}
*/
exports.isOrder = item => {
if (typeof item !== 'object') {
return false
}
if (item === null) {
return false
}
const obj = /** @type {Order} */ (item)
if (typeof obj.amount !== 'string') {
return false
}
if (typeof obj.from !== 'string') {
return false
}
if (typeof obj.memo !== 'string') {
return false
}
return typeof obj.timestamp === 'number'
}

View file

@ -5,7 +5,9 @@ const Events = {
ON_DISPLAY_NAME: "ON_DISPLAY_NAME", ON_DISPLAY_NAME: "ON_DISPLAY_NAME",
ON_HANDSHAKE_ADDRESS: "ON_HANDSHAKE_ADDRESS", ON_HANDSHAKE_ADDRESS: "ON_HANDSHAKE_ADDRESS",
ON_RECEIVED_REQUESTS: "ON_RECEIVED_REQUESTS", ON_RECEIVED_REQUESTS: "ON_RECEIVED_REQUESTS",
ON_SENT_REQUESTS: "ON_SENT_REQUESTS" ON_SENT_REQUESTS: "ON_SENT_REQUESTS",
ON_BIO: "ON_BIO",
ON_SEED_BACKUP: "ON_SEED_BACKUP",
}; };
module.exports = Events; module.exports = Events;

View file

@ -18,6 +18,7 @@ const Encryption = require("../utils/encryptionStore");
const LightningServices = require("../utils/lightningServices"); const LightningServices = require("../utils/lightningServices");
const GunDB = require("../services/gunDB/Mediator"); const GunDB = require("../services/gunDB/Mediator");
const { unprotectedRoutes, nonEncryptedRoutes } = require("../utils/protectedRoutes"); const { unprotectedRoutes, nonEncryptedRoutes } = require("../utils/protectedRoutes");
const GunActions = require("../services/gunDB/contact-api/actions")
const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10; const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10;
const SESSION_ID = uuid(); const SESSION_ID = uuid();
@ -537,6 +538,12 @@ module.exports = async (
// Register user before creating wallet // Register user before creating wallet
const publicKey = await GunDB.register(alias, password); const publicKey = await GunDB.register(alias, password);
await GunActions.saveSeedBackup(
mnemonicPhrase,
GunDB.getUser(),
GunDB.mySEA
)
walletUnlocker.initWallet( walletUnlocker.initWallet(
walletArgs, walletArgs,
async (initWalletErr, initWalletResponse) => { async (initWalletErr, initWalletResponse) => {