services wired with orderAck
This commit is contained in:
parent
bdf8c7206b
commit
6514018cf3
8 changed files with 190 additions and 77 deletions
|
|
@ -54,7 +54,7 @@
|
|||
"request-promise": "^4.2.6",
|
||||
"response-time": "^2.3.2",
|
||||
"shelljs": "^0.8.2",
|
||||
"shock-common": "32.0.0",
|
||||
"shock-common": "34.0.0",
|
||||
"socket.io": "2.1.1",
|
||||
"text-encoding": "^0.7.0",
|
||||
"tingodb": "^0.6.1",
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ const logger = require('winston')
|
|||
const Common = require('shock-common')
|
||||
const { Constants, Schema } = Common
|
||||
const Gun = require('gun')
|
||||
const crypto = require('crypto')
|
||||
const fetch = require('node-fetch')
|
||||
|
||||
const { ErrorCode } = Constants
|
||||
|
||||
|
|
@ -25,6 +23,7 @@ const Key = require('./key')
|
|||
const Utils = require('./utils')
|
||||
const SchemaManager = require('../../schema')
|
||||
const LNDHealthMananger = require('../../../utils/lightningServices/errors')
|
||||
const { enrollContentTokens, selfContentToken } = require('../../seed')
|
||||
|
||||
/**
|
||||
* @typedef {import('./SimpleGUN').GUNNode} GUNNode
|
||||
|
|
@ -961,39 +960,14 @@ const sendSpontaneousPayment = async (
|
|||
!isNaN(parseInt(opts.ackInfo, 10))
|
||||
) {
|
||||
//user requested a seed to themselves
|
||||
const numberOfTokens = Number(opts.ackInfo)
|
||||
if (isNaN(numberOfTokens)) {
|
||||
throw new Error('ackInfo provided is not a valid number')
|
||||
}
|
||||
const seedUrl = process.env.TORRENT_SEED_URL
|
||||
const seedToken = process.env.TORRENT_SEED_TOKEN
|
||||
if (!seedUrl || !seedToken) {
|
||||
const numberOfTokens = Number(opts.ackInfo) || 1
|
||||
const seedInfo = selfContentToken()
|
||||
if (!seedInfo) {
|
||||
throw new Error('torrentSeed service not available')
|
||||
}
|
||||
const { seedUrl } = seedInfo
|
||||
console.log('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
|
||||
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))
|
||||
const tokens = await enrollContentTokens(numberOfTokens, seedInfo)
|
||||
console.log('RES SEED OK')
|
||||
const ackData = JSON.stringify({ seedUrl, tokens })
|
||||
return {
|
||||
|
|
@ -1132,7 +1106,11 @@ const sendSpontaneousPayment = async (
|
|||
})
|
||||
const myLndPub = LNDHealthMananger.lndPub
|
||||
if (
|
||||
(opts.type !== 'contentReveal' && opts.type !== 'torrentSeed') ||
|
||||
(opts.type !== 'contentReveal' &&
|
||||
opts.type !== 'torrentSeed' &&
|
||||
opts.type !== 'service' &&
|
||||
opts.type !== 'streamSeed' &&
|
||||
opts.type !== 'product') ||
|
||||
!orderResponse.ackNode
|
||||
) {
|
||||
SchemaManager.AddOrder({
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ const isFinite = require('lodash/isFinite')
|
|||
const isNumber = require('lodash/isNumber')
|
||||
const isNaN = require('lodash/isNaN')
|
||||
const Common = require('shock-common')
|
||||
const crypto = require('crypto')
|
||||
const fetch = require('node-fetch')
|
||||
const {
|
||||
Constants: { ErrorCode },
|
||||
Schema
|
||||
|
|
@ -18,6 +16,7 @@ const LightningServices = require('../../../../utils/lightningServices')
|
|||
const Key = require('../key')
|
||||
const Utils = require('../utils')
|
||||
const Gun = require('gun')
|
||||
const { selfContentToken, enrollContentTokens } = require('../../../seed')
|
||||
|
||||
const getUser = () => require('../../Mediator').getUser()
|
||||
|
||||
|
|
@ -152,6 +151,54 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
|
|||
`Amount was correctly decrypted, but got a non finite number, decryptedAmount: ${decryptedAmount}`
|
||||
)
|
||||
}
|
||||
const mySecret = require('../../Mediator').getMySecret()
|
||||
/**
|
||||
* @type {string|null}
|
||||
*/
|
||||
let serviceOrderType = null //if the order refers to a service, we take the info from the service before sending the invoice
|
||||
/**
|
||||
* @type {{ seedUrl: string, seedToken: string }|null}
|
||||
*/
|
||||
let serviceOrderContentSeedInfo = null //in case the service is of type 'torrentSeed' or 'streamSeed' this is {seedUrl,seedToken}, can be omitted, in that case, it will be taken from env
|
||||
if (order.targetType === 'service') {
|
||||
console.log('General Service')
|
||||
const { ackInfo: serviceID } = order
|
||||
console.log('ACK INFO')
|
||||
console.log(serviceID)
|
||||
if (!Common.isPopulatedString(serviceID)) {
|
||||
throw new TypeError(`no serviceID provided to orderAck`)
|
||||
}
|
||||
const selectedService = await new Promise(res => {
|
||||
getUser()
|
||||
.get(Key.OFFERED_SERVICES)
|
||||
.get(serviceID)
|
||||
.load(res)
|
||||
})
|
||||
console.log(selectedService)
|
||||
if (!selectedService) {
|
||||
throw new TypeError(`invalid serviceID provided to orderAck`)
|
||||
}
|
||||
const {
|
||||
serviceType,
|
||||
servicePrice,
|
||||
serviceSeedUrl: encSeedUrl, //=
|
||||
serviceSeedToken: encSeedToken //=
|
||||
} = selectedService
|
||||
if (Number(amount) !== Number(servicePrice)) {
|
||||
throw new TypeError(
|
||||
`service price mismatch ${amount} : ${servicePrice}`
|
||||
)
|
||||
}
|
||||
if (serviceType === 'torrentSeed' || serviceType === 'streamSeed') {
|
||||
if (encSeedUrl && encSeedToken) {
|
||||
const seedUrl = await SEA.decrypt(encSeedUrl, mySecret)
|
||||
const seedToken = await SEA.decrypt(encSeedToken, mySecret)
|
||||
serviceOrderContentSeedInfo = { seedUrl, seedToken }
|
||||
}
|
||||
}
|
||||
|
||||
serviceOrderType = serviceType
|
||||
}
|
||||
|
||||
const invoiceReq = {
|
||||
expiry: 36000,
|
||||
|
|
@ -218,7 +265,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
|
|||
add_index: addIndex,
|
||||
payment_addr: paymentAddr
|
||||
} = paidInvoice
|
||||
const orderType = order.targetType
|
||||
const orderType = serviceOrderType || order.targetType
|
||||
const { ackInfo } = order //a string representing what has been requested
|
||||
switch (orderType) {
|
||||
case 'tip': {
|
||||
|
|
@ -269,7 +316,6 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
|
|||
* @type {Record<string,string>} <contentID,decryptedRef>
|
||||
*/
|
||||
const contentsToSend = {}
|
||||
const mySecret = require('../../Mediator').getMySecret()
|
||||
console.log('SECRET OK')
|
||||
let privateFound = false
|
||||
await Common.Utils.asyncForEach(
|
||||
|
|
@ -294,7 +340,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
|
|||
'post provided from ackInfo does not contain private content'
|
||||
break //no private content in this post
|
||||
}
|
||||
const ackData = { unlockedContents: contentsToSend }
|
||||
const ackData = { unlockedContents: contentsToSend, ackInfo }
|
||||
const toSend = JSON.stringify(ackData)
|
||||
const encrypted = await SEA.encrypt(toSend, secret)
|
||||
const ordResponse = {
|
||||
|
|
@ -325,43 +371,75 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
|
|||
}
|
||||
case 'torrentSeed': {
|
||||
console.log('TORRENT')
|
||||
const numberOfTokens = Number(ackInfo)
|
||||
if (isNaN(numberOfTokens)) {
|
||||
breakError = 'ackInfo provided is not a valid number'
|
||||
break
|
||||
}
|
||||
const seedUrl = process.env.TORRENT_SEED_URL
|
||||
const seedToken = process.env.TORRENT_SEED_TOKEN
|
||||
if (!seedUrl || !seedToken) {
|
||||
const numberOfTokens = Number(ackInfo) || 1
|
||||
const seedInfo = selfContentToken()
|
||||
if (!seedInfo && !serviceOrderContentSeedInfo) {
|
||||
breakError = 'torrentSeed service not available'
|
||||
break //service not available
|
||||
}
|
||||
console.log('SEED URL OK')
|
||||
const tokens = Array(numberOfTokens)
|
||||
for (let i = 0; i < numberOfTokens; i++) {
|
||||
tokens[i] = crypto.randomBytes(32).toString('hex')
|
||||
const seedInfoReady = serviceOrderContentSeedInfo || seedInfo
|
||||
if (!seedInfoReady) {
|
||||
breakError = 'torrentSeed service not available'
|
||||
break //service not available
|
||||
}
|
||||
/**@param {string} token */
|
||||
const enrollToken = async token => {
|
||||
const reqData = {
|
||||
seed_token: seedToken,
|
||||
wallet_token: token
|
||||
}
|
||||
//@ts-expect-error
|
||||
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))
|
||||
const { seedUrl } = seedInfoReady
|
||||
const tokens = await enrollContentTokens(
|
||||
numberOfTokens,
|
||||
seedInfoReady
|
||||
)
|
||||
console.log('RES SEED OK')
|
||||
const ackData = { seedUrl, tokens }
|
||||
const ackData = { seedUrl, tokens, ackInfo }
|
||||
const toSend = JSON.stringify(ackData)
|
||||
const encrypted = await SEA.encrypt(toSend, secret)
|
||||
const serviceResponse = {
|
||||
type: 'orderAck',
|
||||
response: encrypted
|
||||
}
|
||||
console.log('RES SEED SENT')
|
||||
await new Promise((res, rej) => {
|
||||
getUser()
|
||||
.get(Key.ORDER_TO_RESPONSE)
|
||||
.get(ackNode)
|
||||
.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(null)
|
||||
}
|
||||
})
|
||||
})
|
||||
console.log('RES SENT SEED')
|
||||
orderMetadata = JSON.stringify(serviceResponse)
|
||||
break
|
||||
}
|
||||
case 'streamSeed': {
|
||||
console.log('STREAM')
|
||||
const numberOfTokens = 1
|
||||
const seedInfo = selfContentToken() //TODO this must change for streams
|
||||
if (!seedInfo && !serviceOrderContentSeedInfo) {
|
||||
breakError = 'torrentSeed service not available'
|
||||
break //service not available
|
||||
}
|
||||
const seedInfoReady = serviceOrderContentSeedInfo || seedInfo
|
||||
if (!seedInfoReady) {
|
||||
breakError = 'torrentSeed service not available'
|
||||
break //service not available
|
||||
}
|
||||
const { seedUrl } = seedInfoReady
|
||||
const tokens = await enrollContentTokens(
|
||||
numberOfTokens,
|
||||
seedInfoReady
|
||||
)
|
||||
console.log('RES SEED OK')
|
||||
const ackData = {
|
||||
seedUrl,
|
||||
tokens,
|
||||
ackInfo
|
||||
}
|
||||
const toSend = JSON.stringify(ackData)
|
||||
const encrypted = await SEA.encrypt(toSend, secret)
|
||||
const serviceResponse = {
|
||||
|
|
@ -392,6 +470,7 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
|
|||
case 'other': //not implemented yet but save them as a coordinate anyways
|
||||
break
|
||||
default:
|
||||
breakError = 'invalid service type provided'
|
||||
return //exit because not implemented
|
||||
}
|
||||
const metadata = breakError ? JSON.stringify(breakError) : orderMetadata
|
||||
|
|
|
|||
|
|
@ -71,3 +71,5 @@ exports.COORDINATE_INDEX = 'coordinateIndex'
|
|||
exports.TMP_CHAIN_COORDINATE = 'tmpChainCoordinate'
|
||||
|
||||
exports.DATE_COORDINATE_INDEX = 'dateCoordinateIndex'
|
||||
|
||||
exports.OFFERED_SERVICES = 'offeredServices'
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const Key = require('../gunDB/contact-api/key')
|
|||
const lndV2 = require('../../utils/lightningServices/v2')
|
||||
/**
|
||||
* @typedef {import('../gunDB/contact-api/SimpleGUN').ISEA} ISEA
|
||||
* @typedef { 'spontaneousPayment' | 'tip' | 'torrentSeed' | 'contentReveal' | 'other'|'invoice'|'payment'|'chainTx' } OrderType
|
||||
* @typedef { 'spontaneousPayment' | 'tip' | 'torrentSeed' | 'contentReveal' | 'other'|'invoice'|'payment'|'chainTx' | 'streamSeed' |'service'|'product' } OrderType
|
||||
*
|
||||
* This represents a settled order only, unsettled orders have no coordinate
|
||||
* @typedef {object} CoordinateOrder //everything is optional for different types
|
||||
|
|
|
|||
51
services/seed.js
Normal file
51
services/seed.js
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
const crypto = require('crypto')
|
||||
const fetch = require('node-fetch')
|
||||
|
||||
const selfContentToken = () => {
|
||||
const seedUrl = process.env.TORRENT_SEED_URL
|
||||
const seedToken = process.env.TORRENT_SEED_TOKEN
|
||||
if (!seedUrl || !seedToken) {
|
||||
return false
|
||||
}
|
||||
return {seedUrl,seedToken}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} nOfTokens
|
||||
* @param {{seedUrl:string,seedToken:string}} param1
|
||||
* @returns
|
||||
*/
|
||||
const enrollContentTokens = async (nOfTokens,{seedUrl,seedToken}) => {
|
||||
const tokens = Array(nOfTokens)
|
||||
for (let i = 0; i < nOfTokens; 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
|
||||
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))
|
||||
return tokens
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
selfContentToken,
|
||||
enrollContentTokens
|
||||
}
|
||||
|
|
@ -1197,12 +1197,15 @@ module.exports = async (
|
|||
type !== 'spontaneousPayment' &&
|
||||
type !== 'tip' &&
|
||||
type !== 'torrentSeed' &&
|
||||
type !== 'streamSeed' &&
|
||||
type !== 'contentReveal' &&
|
||||
type !== 'service' &&
|
||||
type !== 'product' &&
|
||||
type !== 'other'
|
||||
) {
|
||||
return res.status(415).json({
|
||||
field: 'type',
|
||||
errorMessage: `Only 'spontaneousPayment'| 'tip' | 'torrentSeed' | 'contentReveal' | 'other' payments supported via this endpoint for now.`
|
||||
errorMessage: `Only 'spontaneousPayment'| 'tip' | 'torrentSeed' | 'contentReveal' | 'service' | 'streamSeed' | 'product' |'other' payments supported via this endpoint for now.`
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6212,10 +6212,10 @@ shellwords@^0.1.1:
|
|||
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
||||
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
|
||||
|
||||
shock-common@32.0.0:
|
||||
version "32.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-32.0.0.tgz#bb7f70d7a572783c46aeae7420179935eb5096d4"
|
||||
integrity sha512-1GorUFRpkRGXdKT9PImwnj2orpoJaESU/iD+rvL8sqFMLKazkW9LfLAcRwEWCvytWdKFJ35UO2gN49N8dPiRmA==
|
||||
shock-common@34.0.0:
|
||||
version "34.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-34.0.0.tgz#30ffbcb136af9bc04b936999a7eebee4e18c67f0"
|
||||
integrity sha512-i+io2YBh/GLXBz4YURdxg0t//gm2H3dmpkdU8gnEVe7i/ZdabYGhyBgEnUOMy1ZTMunvv/20U8wan9W4VrOaVQ==
|
||||
dependencies:
|
||||
immer "^6.0.6"
|
||||
lodash "^4.17.19"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue