Merge pull request #308 from shocknet/merged-coordinates
Merged coordinates
This commit is contained in:
commit
11992beb7c
8 changed files with 253 additions and 72 deletions
|
|
@ -5,3 +5,5 @@ DISABLE_SHOCK_ENCRYPTION=false
|
|||
CACHE_HEADERS_MANDATORY=true
|
||||
SHOCK_CACHE=true
|
||||
TRUSTED_KEYS=true
|
||||
TORRENT_SEED_URL=https://webtorrent.shock.network
|
||||
TORRENT_SEED_TOKEN=jibberish
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
"localtunnel": "^1.9.0",
|
||||
"lodash": "^4.17.20",
|
||||
"method-override": "^2.3.7",
|
||||
"node-fetch": "^2.6.1",
|
||||
"node-persist": "^3.1.0",
|
||||
"promise": "^8.1.0",
|
||||
"ramda": "^0.27.1",
|
||||
|
|
@ -68,6 +69,7 @@
|
|||
"@types/jest": "^24.0.18",
|
||||
"@types/jsonwebtoken": "^8.3.7",
|
||||
"@types/lodash": "^4.14.141",
|
||||
"@types/node-fetch": "^2.5.8",
|
||||
"@types/ramda": "types/npm-ramda#dist",
|
||||
"@types/react": "16.x.x",
|
||||
"@types/socket.io": "^2.1.11",
|
||||
|
|
|
|||
|
|
@ -924,6 +924,7 @@ const sendHRWithInitialMsg = async (
|
|||
* @typedef {object} SpontPaymentOptions
|
||||
* @prop {Common.Schema.OrderTargetType} type
|
||||
* @prop {string=} postID
|
||||
* @prop {string=} ackInfo
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -1296,6 +1297,7 @@ const setLastSeenApp = () =>
|
|||
* @returns {Promise<[string, Common.Schema.RawPost]>}
|
||||
*/
|
||||
const createPostNew = async (tags, title, content) => {
|
||||
const SEA = require('../Mediator').mySEA
|
||||
/** @type {Common.Schema.RawPost} */
|
||||
const newPost = {
|
||||
date: Date.now(),
|
||||
|
|
@ -1311,6 +1313,23 @@ const createPostNew = async (tags, title, content) => {
|
|||
newPost.contentItems[uuid] = c
|
||||
})
|
||||
|
||||
const mySecret = require('../Mediator').getMySecret()
|
||||
|
||||
await Common.Utils.asyncForEach(content, async c => {
|
||||
// @ts-expect-error
|
||||
const uuid = Gun.text.random()
|
||||
newPost.contentItems[uuid] = c
|
||||
if (
|
||||
(c.type === 'image/embedded' || c.type === 'video/embedded') &&
|
||||
c.isPrivate
|
||||
) {
|
||||
const encryptedMagnet = await SEA.encrypt(c.magnetURI, mySecret)
|
||||
newPost.contentItems[uuid] = { ...c, magnetURI: encryptedMagnet }
|
||||
} else {
|
||||
newPost.contentItems[uuid] = c
|
||||
}
|
||||
})
|
||||
|
||||
/** @type {string} */
|
||||
const postID = await Common.makePromise((res, rej) => {
|
||||
const _n = require('../Mediator')
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ const {
|
|||
Constants: { ErrorCode },
|
||||
Schema
|
||||
} = Common
|
||||
const { assertNever } = require('assert-never')
|
||||
const crypto = require('crypto')
|
||||
const fetch = require('node-fetch')
|
||||
|
||||
const LightningServices = require('../../../../utils/lightningServices')
|
||||
const {
|
||||
|
|
@ -20,6 +23,7 @@ const {
|
|||
const { writeCoordinate } = require('../../../coordinates')
|
||||
const Key = require('../key')
|
||||
const Utils = require('../utils')
|
||||
const { gunUUID } = require('../../../../utils')
|
||||
|
||||
const getUser = () => require('../../Mediator').getUser()
|
||||
|
||||
|
|
@ -219,17 +223,178 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
|
|||
/**
|
||||
* @param {Common.InvoiceWhenListed} invoice
|
||||
*/
|
||||
const onData = invoice => {
|
||||
const onData = async invoice => {
|
||||
if (invoice.settled) {
|
||||
writeCoordinate(invoice.r_hash.toString(), coord)
|
||||
|
||||
if (order.targetType === 'tip') {
|
||||
getUser()
|
||||
.get('postToTipCount')
|
||||
// CAST: Checked above.
|
||||
.get(/** @type {string} */ (order.ackInfo))
|
||||
.set(null) // each item in the set is a tip
|
||||
} else if (order.targetType === 'contentReveal') {
|
||||
// -----------------------------------------
|
||||
logger.debug('Content Reveal')
|
||||
|
||||
//assuming digital product that only requires to be unlocked
|
||||
const postID = order.ackInfo
|
||||
|
||||
if (!Common.isPopulatedString(postID)) {
|
||||
logger.error(`Invalid post ID`)
|
||||
logger.error(postID)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: do this reactively
|
||||
const selectedPost = await new Promise(res => {
|
||||
getUser()
|
||||
.get(Key.POSTS_NEW)
|
||||
.get(postID)
|
||||
.load(res)
|
||||
})
|
||||
|
||||
logger.debug(selectedPost)
|
||||
|
||||
if (Common.isPost(selectedPost)) {
|
||||
logger.error('Post id provided does not correspond to a valid post')
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Record<string,string>} <contentID,decryptedRef>
|
||||
*/
|
||||
const contentsToSend = {}
|
||||
const mySecret = require('../../Mediator').getMySecret()
|
||||
logger.debug('SECRET OK')
|
||||
let privateFound = false
|
||||
await Common.Utils.asyncForEach(
|
||||
Object.entries(selectedPost.contentItems),
|
||||
async ([contentID, item]) => {
|
||||
if (
|
||||
item.type !== 'image/embedded' &&
|
||||
item.type !== 'video/embedded'
|
||||
) {
|
||||
return //only visual content can be private
|
||||
}
|
||||
if (!item.isPrivate) {
|
||||
return
|
||||
}
|
||||
privateFound = true
|
||||
const decrypted = await SEA.decrypt(item.magnetURI, mySecret)
|
||||
contentsToSend[contentID] = decrypted
|
||||
}
|
||||
)
|
||||
if (!privateFound) {
|
||||
logger.error(`Post provided does not contain private content`)
|
||||
return
|
||||
}
|
||||
const ackData = { unlockedContents: contentsToSend }
|
||||
const toSend = JSON.stringify(ackData)
|
||||
const encrypted = await SEA.encrypt(toSend, secret)
|
||||
const ordResponse = {
|
||||
type: 'orderAck',
|
||||
response: encrypted
|
||||
}
|
||||
logger.debug('RES READY')
|
||||
|
||||
const uuid = gunUUID()
|
||||
orderResponse.ackNode = uuid
|
||||
|
||||
await /** @type {Promise<void>} */ (new Promise((res, rej) => {
|
||||
getUser()
|
||||
.get(Key.ORDER_TO_RESPONSE)
|
||||
.get(uuid)
|
||||
.put(ordResponse, ack => {
|
||||
if (ack.err && typeof ack.err !== 'number') {
|
||||
rej(
|
||||
new Error(
|
||||
`Error saving encrypted orderAck to order to response usergraph: ${ack}`
|
||||
)
|
||||
)
|
||||
} else {
|
||||
res()
|
||||
}
|
||||
})
|
||||
}))
|
||||
logger.debug('RES SENT CONTENT')
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
} else if (order.targetType === 'spontaneousPayment') {
|
||||
// no action required
|
||||
} else if (order.targetType === 'torrentSeed') {
|
||||
logger.debug('TORRENT')
|
||||
const numberOfTokens = Number(order.ackInfo)
|
||||
if (isNaN(numberOfTokens)) {
|
||||
logger.error('ackInfo provided is not a valid number')
|
||||
return
|
||||
}
|
||||
const seedUrl = process.env.TORRENT_SEED_URL
|
||||
const seedToken = process.env.TORRENT_SEED_TOKEN
|
||||
if (!seedUrl || !seedToken) {
|
||||
logger.error('torrentSeed service not available')
|
||||
return
|
||||
}
|
||||
logger.debug('SEED URL OK')
|
||||
const tokens = Array(numberOfTokens)
|
||||
for (let i = 0; i < numberOfTokens; i++) {
|
||||
tokens[i] = crypto.randomBytes(32).toString('hex')
|
||||
}
|
||||
/**@param {string} token */
|
||||
const enrollToken = async token => {
|
||||
const reqData = {
|
||||
seed_token: seedToken,
|
||||
wallet_token: token
|
||||
}
|
||||
// @ts-expect-error TODO
|
||||
const res = await fetch(`${seedUrl}/api/enroll_token`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(reqData)
|
||||
})
|
||||
if (res.status !== 200) {
|
||||
throw new Error('torrentSeed service currently not available')
|
||||
}
|
||||
}
|
||||
await Promise.all(tokens.map(enrollToken))
|
||||
logger.debug('RES SEED OK')
|
||||
const ackData = { seedUrl, tokens }
|
||||
const toSend = JSON.stringify(ackData)
|
||||
const encrypted = await SEA.encrypt(toSend, secret)
|
||||
const serviceResponse = {
|
||||
type: 'orderAck',
|
||||
response: encrypted
|
||||
}
|
||||
console.log('RES SEED SENT')
|
||||
|
||||
const uuid = gunUUID()
|
||||
orderResponse.ackNode = uuid
|
||||
|
||||
await /** @type {Promise<void>} */ (new Promise((res, rej) => {
|
||||
getUser()
|
||||
.get(Key.ORDER_TO_RESPONSE)
|
||||
.get(uuid)
|
||||
.put(serviceResponse, ack => {
|
||||
if (ack.err && typeof ack.err !== 'number') {
|
||||
rej(
|
||||
new Error(
|
||||
`Error saving encrypted orderAck to order to response usergraph: ${ack}`
|
||||
)
|
||||
)
|
||||
} else {
|
||||
res()
|
||||
}
|
||||
})
|
||||
}))
|
||||
logger.debug('RES SENT SEED')
|
||||
} else if (order.targetType === 'other') {
|
||||
// TODO
|
||||
} else {
|
||||
assertNever(order.targetType)
|
||||
}
|
||||
|
||||
writeCoordinate(invoice.r_hash.toString(), coord)
|
||||
stream.off()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1192,12 +1192,35 @@ module.exports = async (
|
|||
|
||||
app.post('/api/lnd/unifiedTrx', async (req, res) => {
|
||||
try {
|
||||
const { type, amt, to, memo, feeLimit, postID } = req.body
|
||||
const { type, amt, to, memo, feeLimit, postID, ackInfo } = req.body
|
||||
|
||||
if (type !== 'spont' && type !== 'post') {
|
||||
if (
|
||||
type !== 'spont' &&
|
||||
type !== 'post' &&
|
||||
type !== 'spontaneousPayment' &&
|
||||
type !== 'tip' &&
|
||||
type !== 'torrentSeed' &&
|
||||
type !== 'contentReveal' &&
|
||||
type !== 'other'
|
||||
) {
|
||||
return res.status(415).json({
|
||||
field: 'type',
|
||||
errorMessage: `Only 'spont' and 'post' payments supported via this endpoint for now.`
|
||||
errorMessage: `Only 'spontaneousPayment'| 'tip' | 'torrentSeed' | 'contentReveal' | 'other' payments supported via this endpoint for now.`
|
||||
})
|
||||
}
|
||||
|
||||
const typesThatShouldContainAckInfo = [
|
||||
'tip',
|
||||
'torrentSeed',
|
||||
'contentReveal'
|
||||
]
|
||||
|
||||
const shouldContainAckInfo = typesThatShouldContainAckInfo.includes(type)
|
||||
|
||||
if (shouldContainAckInfo && !Common.isPopulatedString(ackInfo)) {
|
||||
return res.status(400).json({
|
||||
field: 'ackInfo',
|
||||
errorMessage: `Transactions of type ${typesThatShouldContainAckInfo} should contain an ackInfo field.`
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1241,7 +1264,8 @@ module.exports = async (
|
|||
return res.status(200).json(
|
||||
await GunActions.sendSpontaneousPayment(to, amt, memo, feeLimit, {
|
||||
type,
|
||||
postID
|
||||
postID,
|
||||
ackInfo
|
||||
})
|
||||
)
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -66,66 +66,6 @@ module.exports = (
|
|||
}
|
||||
}
|
||||
|
||||
const parseJSON = data => {
|
||||
try {
|
||||
if (typeof data === 'string') {
|
||||
return JSON.parse(data)
|
||||
}
|
||||
|
||||
return data
|
||||
} catch (err) {
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
const decryptEvent = ({ eventName, data, socket }) => {
|
||||
try {
|
||||
const deviceId = socket.handshake.query['x-shockwallet-device-id']
|
||||
if (Encryption.isNonEncrypted(eventName)) {
|
||||
return data
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return data
|
||||
}
|
||||
|
||||
const parsedData = parseJSON(data)
|
||||
|
||||
if (!deviceId) {
|
||||
throw {
|
||||
field: 'deviceId',
|
||||
message: 'Please specify a device ID'
|
||||
}
|
||||
}
|
||||
|
||||
if (!Encryption.isAuthorizedDevice({ deviceId })) {
|
||||
throw {
|
||||
field: 'deviceId',
|
||||
message: 'Please exchange keys with the API before using the socket'
|
||||
}
|
||||
}
|
||||
|
||||
const decryptedKey = Encryption.decryptKey({
|
||||
deviceId,
|
||||
message: parsedData.encryptedKey
|
||||
})
|
||||
const decryptedMessage = Encryption.decryptMessage({
|
||||
message: parsedData.encryptedData,
|
||||
key: decryptedKey,
|
||||
iv: parsedData.iv
|
||||
})
|
||||
const decryptedData = JSON.parse(decryptedMessage)
|
||||
return decryptedData
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
`[SOCKET] An error has occurred while decrypting an event (${eventName}):`,
|
||||
err
|
||||
)
|
||||
|
||||
return socket.emit('encryption:error', err)
|
||||
}
|
||||
}
|
||||
|
||||
const onNewInvoice = (socket, subID) => {
|
||||
const { lightning } = LightningServices.services
|
||||
logger.warn('Subscribing to invoices socket...' + subID)
|
||||
|
|
@ -677,7 +617,7 @@ module.exports = (
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {Common.Schema.SimpleReceivedRequest[]} receivedReqs
|
||||
* @param {ReadonlyArray<Common.SimpleReceivedRequest>} receivedReqs
|
||||
*/
|
||||
const onReceivedReqs = receivedReqs => {
|
||||
const processed = receivedReqs.map(({ id, requestorPK, timestamp }) => {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,21 @@
|
|||
/**
|
||||
* @format
|
||||
*/
|
||||
const Gun = require('gun')
|
||||
|
||||
const { asyncFilter } = require('./helpers')
|
||||
|
||||
module.exports = {
|
||||
asyncFilter
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
const gunUUID = () => {
|
||||
// @ts-expect-error Not typed
|
||||
const uuid = Gun.Text.random()
|
||||
|
||||
return uuid
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
asyncFilter,
|
||||
gunUUID
|
||||
}
|
||||
|
|
|
|||
21
yarn.lock
21
yarn.lock
|
|
@ -722,6 +722,14 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d"
|
||||
integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==
|
||||
|
||||
"@types/node-fetch@^2.5.8":
|
||||
version "2.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb"
|
||||
integrity sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
form-data "^3.0.0"
|
||||
|
||||
"@types/node@*":
|
||||
version "12.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.4.tgz#64db61e0359eb5a8d99b55e05c729f130a678b04"
|
||||
|
|
@ -1838,7 +1846,7 @@ colour@~0.7.1:
|
|||
resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778"
|
||||
integrity sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=
|
||||
|
||||
combined-stream@^1.0.6, combined-stream@~1.0.6:
|
||||
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||
|
|
@ -2946,6 +2954,15 @@ forever-agent@~0.6.1:
|
|||
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
||||
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
|
||||
|
||||
form-data@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
|
||||
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
form-data@~2.3.2:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
|
||||
|
|
@ -4991,7 +5008,7 @@ nice-try@^1.0.4:
|
|||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
node-fetch@^2.3.0:
|
||||
node-fetch@^2.3.0, node-fetch@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue