working order ack
This commit is contained in:
parent
4681f22797
commit
9f4a0b05d2
4 changed files with 242 additions and 171 deletions
|
|
@ -923,7 +923,7 @@ const sendHRWithInitialMsg = async (
|
|||
/**
|
||||
* @typedef {object} SpontPaymentOptions
|
||||
* @prop {Common.Schema.OrderTargetType} type
|
||||
* @prop {string=} postID
|
||||
* @prop {string=} ackInfo
|
||||
*/
|
||||
/**
|
||||
* @typedef {object} OrderRes
|
||||
|
|
@ -968,11 +968,8 @@ const sendSpontaneousPayment = async (
|
|||
from: getUser()._.sea.pub,
|
||||
memo: memo || 'no memo',
|
||||
timestamp: Date.now(),
|
||||
targetType: opts.type
|
||||
}
|
||||
|
||||
if (opts.type === 'tip') {
|
||||
order.ackInfo = opts.postID
|
||||
targetType: opts.type,
|
||||
ackInfo: opts.ackInfo
|
||||
}
|
||||
|
||||
logger.info(JSON.stringify(order))
|
||||
|
|
@ -1009,7 +1006,7 @@ const sendSpontaneousPayment = async (
|
|||
)
|
||||
)
|
||||
} else {
|
||||
res(ord._.get)
|
||||
setTimeout(() => res(ord._.get), 0)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
@ -1020,7 +1017,8 @@ const sendSpontaneousPayment = async (
|
|||
)}`
|
||||
throw new Error(msg)
|
||||
}
|
||||
|
||||
console.log('ORDER ID')
|
||||
console.log(orderID)
|
||||
/** @type {import('shock-common').Schema.OrderResponse} */
|
||||
const encryptedOrderRes = await Utils.tryAndWait(
|
||||
gun =>
|
||||
|
|
@ -1030,12 +1028,13 @@ const sendSpontaneousPayment = async (
|
|||
.get(Key.ORDER_TO_RESPONSE)
|
||||
.get(orderID)
|
||||
.on(orderResponse => {
|
||||
console.log(orderResponse)
|
||||
if (Schema.isOrderResponse(orderResponse)) {
|
||||
res(orderResponse)
|
||||
}
|
||||
})
|
||||
}),
|
||||
v => !Schema.isOrderResponse(v)
|
||||
v => Schema.isOrderResponse(v)
|
||||
)
|
||||
|
||||
if (!Schema.isOrderResponse(encryptedOrderRes)) {
|
||||
|
|
@ -1046,10 +1045,12 @@ const sendSpontaneousPayment = async (
|
|||
throw e
|
||||
}
|
||||
|
||||
/** @type {import('shock-common').Schema.OrderResponse} */
|
||||
/** @type {import('shock-common').Schema.OrderResponse &{ackNode:string}} */
|
||||
const orderResponse = {
|
||||
response: await SEA.decrypt(encryptedOrderRes.response, ourSecret),
|
||||
type: encryptedOrderRes.type
|
||||
type: encryptedOrderRes.type,
|
||||
//@ts-expect-error
|
||||
ackNode: encryptedOrderRes.ackNode
|
||||
}
|
||||
|
||||
logger.info('decoded orderResponse: ' + JSON.stringify(orderResponse))
|
||||
|
|
@ -1080,7 +1081,10 @@ const sendSpontaneousPayment = async (
|
|||
payment_request: orderResponse.response
|
||||
})
|
||||
const myLndPub = LNDHealthMananger.lndPub
|
||||
if (opts.type !== 'contentReveal' && opts.type !== 'torrentSeed') {
|
||||
if (
|
||||
(opts.type !== 'contentReveal' && opts.type !== 'torrentSeed') ||
|
||||
!orderResponse.ackNode
|
||||
) {
|
||||
SchemaManager.AddOrder({
|
||||
type: opts.type,
|
||||
amount: parseInt(payment.value_sat, 10),
|
||||
|
|
@ -1094,6 +1098,8 @@ const sendSpontaneousPayment = async (
|
|||
})
|
||||
return { payment }
|
||||
}
|
||||
console.log('ACK NODE')
|
||||
console.log(orderResponse.ackNode)
|
||||
/** @type {import('shock-common').Schema.OrderResponse} */
|
||||
const encryptedOrderAckRes = await Utils.tryAndWait(
|
||||
gun =>
|
||||
|
|
@ -1101,8 +1107,11 @@ const sendSpontaneousPayment = async (
|
|||
gun
|
||||
.user(to)
|
||||
.get(Key.ORDER_TO_RESPONSE)
|
||||
.get(orderID)
|
||||
.get(orderResponse.ackNode)
|
||||
.on(orderResponse => {
|
||||
console.log(orderResponse)
|
||||
console.log(Schema.isOrderResponse(orderResponse))
|
||||
|
||||
if (Schema.isOrderResponse(orderResponse)) {
|
||||
res(orderResponse)
|
||||
}
|
||||
|
|
@ -1113,7 +1122,7 @@ const sendSpontaneousPayment = async (
|
|||
|
||||
if (!Schema.isOrderResponse(encryptedOrderAckRes)) {
|
||||
const e = TypeError(
|
||||
`Expected OrderResponse got: ${typeof encryptedOrderAckRes}`
|
||||
`Expected encryptedOrderAckRes got: ${typeof encryptedOrderAckRes}`
|
||||
)
|
||||
logger.error(e)
|
||||
throw e
|
||||
|
|
@ -1148,6 +1157,7 @@ const sendSpontaneousPayment = async (
|
|||
})
|
||||
return { payment, orderAck }
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
logger.error('Error inside sendPayment()')
|
||||
logger.error(e)
|
||||
throw e
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
* @format
|
||||
*/
|
||||
// @ts-check
|
||||
const Gun = require('gun')
|
||||
const { performance } = require('perf_hooks')
|
||||
const logger = require('winston')
|
||||
const isFinite = require('lodash/isFinite')
|
||||
|
|
@ -201,11 +202,15 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
|
|||
logger.info(
|
||||
`onOrders() -> Will now place the encrypted invoice in order to response usergraph: ${addr}`
|
||||
)
|
||||
// @ts-expect-error
|
||||
const ackNode = Gun.text.random()
|
||||
|
||||
/** @type {import('shock-common').Schema.OrderResponse} */
|
||||
const orderResponse = {
|
||||
response: encInvoice,
|
||||
type: 'invoice'
|
||||
type: 'invoice',
|
||||
//@ts-expect-error
|
||||
ackNode
|
||||
}
|
||||
|
||||
const invoicePutStartTime = performance.now()
|
||||
|
|
@ -231,164 +236,212 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
|
|||
const invoicePutEndTime = performance.now() - invoicePutStartTime
|
||||
|
||||
logger.info(`[PERF] Added invoice to GunDB in ${invoicePutEndTime}ms`)
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Common.Schema.InvoiceWhenListed & {r_hash:Buffer,payment_addr:string}}
|
||||
* @param {Common.Schema.InvoiceWhenListed & {r_hash:Buffer,payment_addr:string}} paidInvoice
|
||||
*/
|
||||
const paidInvoice = await new Promise(res => {
|
||||
SchemaManager.addListenInvoice(invoice.r_hash, res)
|
||||
})
|
||||
const hashString = paidInvoice.r_hash.toString('hex')
|
||||
const {
|
||||
amt_paid_sat: amt,
|
||||
add_index: addIndex,
|
||||
payment_addr: paymentAddr
|
||||
} = paidInvoice
|
||||
const orderType = order.targetType
|
||||
const { ackInfo } = order //a string representing what has been requested
|
||||
switch (orderType) {
|
||||
case 'tip': {
|
||||
const postID = ackInfo
|
||||
if (!Common.isPopulatedString(postID)) {
|
||||
break //create the coordinate, but stop because of the invalid id
|
||||
}
|
||||
getUser()
|
||||
.get('postToTipCount')
|
||||
.get(postID)
|
||||
.set(null) // each item in the set is a tip
|
||||
break
|
||||
}
|
||||
case 'spontaneousPayment': {
|
||||
//no action required
|
||||
break
|
||||
}
|
||||
case 'contentReveal': {
|
||||
//assuming digital product that only requires to be unlocked
|
||||
const postID = ackInfo
|
||||
if (!Common.isPopulatedString(postID)) {
|
||||
break //create the coordinate, but stop because of the invalid id
|
||||
}
|
||||
const selectedPost = await new Promise(res => {
|
||||
getUser()
|
||||
.get(Key.POSTS_NEW)
|
||||
.get(postID)
|
||||
.load(res)
|
||||
})
|
||||
if (!Common.Schema.isPost(selectedPost)) {
|
||||
break //create the coordinate, but stop because of the invalid post
|
||||
}
|
||||
/**
|
||||
* @type {Record<string,string>} <contentID,decryptedRef>
|
||||
*/
|
||||
const contentsToSend = {}
|
||||
const mySecret = require('../../Mediator').getMySecret()
|
||||
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
|
||||
}
|
||||
const decrypted = await SEA.decrypt(item.magnetURI, mySecret)
|
||||
contentsToSend[contentID] = decrypted
|
||||
const invoicePaidCb = async paidInvoice => {
|
||||
console.log('INVOICE PAID')
|
||||
const hashString = paidInvoice.r_hash.toString('hex')
|
||||
const {
|
||||
amt_paid_sat: amt,
|
||||
add_index: addIndex,
|
||||
payment_addr: paymentAddr
|
||||
} = paidInvoice
|
||||
const orderType = order.targetType
|
||||
const { ackInfo } = order //a string representing what has been requested
|
||||
switch (orderType) {
|
||||
case 'tip': {
|
||||
const postID = ackInfo
|
||||
if (!Common.isPopulatedString(postID)) {
|
||||
break //create the coordinate, but stop because of the invalid id
|
||||
}
|
||||
)
|
||||
const ackData = { unlockedContents: contentsToSend }
|
||||
const toSend = JSON.stringify(ackData)
|
||||
const encrypted = await SEA.encrypt(toSend, secret)
|
||||
const ordResponse = {
|
||||
type: 'orderAck',
|
||||
content: encrypted
|
||||
}
|
||||
await new Promise((res, rej) => {
|
||||
getUser()
|
||||
.get(Key.ORDER_TO_RESPONSE)
|
||||
.get(orderID)
|
||||
.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()
|
||||
.get('postToTipCount')
|
||||
.get(postID)
|
||||
.set(null) // each item in the set is a tip
|
||||
break
|
||||
}
|
||||
case 'spontaneousPayment': {
|
||||
//no action required
|
||||
break
|
||||
}
|
||||
case 'contentReveal': {
|
||||
console.log('cONTENT REVEAL')
|
||||
//assuming digital product that only requires to be unlocked
|
||||
const postID = ackInfo
|
||||
console.log('ACK INFO')
|
||||
console.log(ackInfo)
|
||||
if (!Common.isPopulatedString(postID)) {
|
||||
break //create the coordinate, but stop because of the invalid id
|
||||
}
|
||||
console.log('IS STRING')
|
||||
const selectedPost = await new Promise(res => {
|
||||
getUser()
|
||||
.get(Key.POSTS_NEW)
|
||||
.get(postID)
|
||||
.load(res)
|
||||
})
|
||||
console.log('LOAD ok')
|
||||
console.log(selectedPost)
|
||||
if (
|
||||
!selectedPost ||
|
||||
!selectedPost.status ||
|
||||
selectedPost.status !== 'publish'
|
||||
) {
|
||||
break //create the coordinate, but stop because of the invalid post
|
||||
}
|
||||
console.log('IS POST')
|
||||
/**
|
||||
* @type {Record<string,string>} <contentID,decryptedRef>
|
||||
*/
|
||||
const contentsToSend = {}
|
||||
const mySecret = require('../../Mediator').getMySecret()
|
||||
console.log('SECRET OK')
|
||||
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
|
||||
}
|
||||
})
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'torrentSeed': {
|
||||
const seedUrl = process.env.TORRENT_SEED_URL
|
||||
const seedToken = process.env.TORRENT_SEED_TOKEN
|
||||
if (!seedUrl || !seedToken) {
|
||||
break //service not available
|
||||
}
|
||||
const token = crypto.randomBytes(32).toString('hex')
|
||||
const reqData = {
|
||||
seed_token: seedToken,
|
||||
wallet_token: token
|
||||
}
|
||||
const res = await fetch(`${seedUrl}/api/enroll_token`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(reqData)
|
||||
})
|
||||
if (res.ok) {
|
||||
break //request didnt work, save coordinate anyway
|
||||
}
|
||||
if (!item.isPrivate) {
|
||||
return
|
||||
}
|
||||
const decrypted = await SEA.decrypt(item.magnetURI, mySecret)
|
||||
contentsToSend[contentID] = decrypted
|
||||
}
|
||||
)
|
||||
const ackData = { unlockedContents: contentsToSend }
|
||||
const toSend = JSON.stringify(ackData)
|
||||
const encrypted = await SEA.encrypt(toSend, secret)
|
||||
const ordResponse = {
|
||||
type: 'orderAck',
|
||||
response: encrypted
|
||||
}
|
||||
console.log('RES READY')
|
||||
|
||||
const ackData = { seedUrl, token }
|
||||
const toSend = JSON.stringify(ackData)
|
||||
const encrypted = await SEA.encrypt(toSend, secret)
|
||||
const serviceResponse = {
|
||||
type: 'orderAck',
|
||||
content: encrypted
|
||||
}
|
||||
await new Promise((res, rej) => {
|
||||
getUser()
|
||||
.get(Key.ORDER_TO_RESPONSE)
|
||||
.get(orderID)
|
||||
.put(serviceResponse, ack => {
|
||||
if (ack.err && typeof ack.err !== 'number') {
|
||||
rej(
|
||||
new Error(
|
||||
`Error saving encrypted orderAck to order to response usergraph: ${ack}`
|
||||
await new Promise((res, rej) => {
|
||||
getUser()
|
||||
.get(Key.ORDER_TO_RESPONSE)
|
||||
.get(ackNode)
|
||||
.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()
|
||||
}
|
||||
})
|
||||
})
|
||||
break
|
||||
} else {
|
||||
res()
|
||||
}
|
||||
})
|
||||
})
|
||||
console.log('RES SENT')
|
||||
break
|
||||
}
|
||||
case 'torrentSeed': {
|
||||
console.log('TORRENT')
|
||||
const seedUrl = process.env.TORRENT_SEED_URL
|
||||
const seedToken = process.env.TORRENT_SEED_TOKEN
|
||||
if (!seedUrl || !seedToken) {
|
||||
break //service not available
|
||||
}
|
||||
console.log('SEED URL OK')
|
||||
const token = crypto.randomBytes(32).toString('hex')
|
||||
const reqData = {
|
||||
seed_token: seedToken,
|
||||
wallet_token: token
|
||||
}
|
||||
console.log(seedUrl)
|
||||
console.log(seedToken)
|
||||
const res = await fetch(`${seedUrl}/api/enroll_token`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(reqData)
|
||||
})
|
||||
if (res.status !== 200) {
|
||||
break //request didnt work, save coordinate anyway
|
||||
}
|
||||
console.log('RES SEED OK')
|
||||
const ackData = { seedUrl, token }
|
||||
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()
|
||||
}
|
||||
})
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'other': //not implemented yet but save them as a coordinate anyways
|
||||
break
|
||||
default:
|
||||
return //exit because not implemented
|
||||
}
|
||||
case 'other': //not implemented yet but save them as a coordinate anyways
|
||||
break
|
||||
default:
|
||||
return //exit because not implemented
|
||||
const myGunPub = getUser()._.sea.pub
|
||||
SchemaManager.AddOrder({
|
||||
type: orderType,
|
||||
coordinateHash: hashString,
|
||||
coordinateIndex: parseInt(addIndex, 10),
|
||||
inbound: true,
|
||||
amount: parseInt(amt, 10),
|
||||
|
||||
toLndPub: paymentAddr,
|
||||
fromGunPub: order.from,
|
||||
toGunPub: myGunPub,
|
||||
invoiceMemo: memo
|
||||
})
|
||||
}
|
||||
const myGunPub = getUser()._.sea.pub
|
||||
SchemaManager.AddOrder({
|
||||
type: orderType,
|
||||
coordinateHash: hashString,
|
||||
coordinateIndex: parseInt(addIndex, 10),
|
||||
inbound: true,
|
||||
amount: parseInt(amt, 10),
|
||||
console.log('WAITING INVOICE TO BE PAID')
|
||||
new Promise(res => SchemaManager.addListenInvoice(invoice.r_hash, res))
|
||||
.then(invoicePaidCb)
|
||||
.catch(err => {
|
||||
logger.error(
|
||||
`error inside onOrders, orderAddr: ${addr}, orderID: ${orderID}, order: ${JSON.stringify(
|
||||
order
|
||||
)}`
|
||||
)
|
||||
logger.error(err)
|
||||
|
||||
toLndPub: paymentAddr,
|
||||
fromGunPub: order.from,
|
||||
toGunPub: myGunPub,
|
||||
invoiceMemo: memo
|
||||
})
|
||||
/** @type {import('shock-common').Schema.OrderResponse} */
|
||||
const orderResponse = {
|
||||
response: err.message,
|
||||
type: 'err'
|
||||
}
|
||||
|
||||
getUser()
|
||||
.get(Key.ORDER_TO_RESPONSE)
|
||||
.get(orderID)
|
||||
// @ts-expect-error
|
||||
.put(orderResponse, ack => {
|
||||
if (ack.err && typeof ack.err !== 'number') {
|
||||
logger.error(
|
||||
`Error saving encrypted invoice to order to response usergraph: ${ack}`
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
`error inside onOrders, orderAddr: ${addr}, orderID: ${orderID}, order: ${JSON.stringify(
|
||||
|
|
|
|||
|
|
@ -387,6 +387,7 @@ class SchemaManager {
|
|||
.get(coordinateSHA256)
|
||||
.put(encryptedOrderString, ack => {
|
||||
if (ack.err && typeof ack.err !== 'number') {
|
||||
console.log(ack)
|
||||
rej(
|
||||
new Error(
|
||||
`Error saving coordinate order to user-graph: ${ack}`
|
||||
|
|
|
|||
|
|
@ -1192,12 +1192,17 @@ module.exports = async (
|
|||
|
||||
app.post('/api/lnd/unifiedTrx', async (req, res) => {
|
||||
try {
|
||||
const { type, amt, to, memo, feeLimit, postID } = req.body
|
||||
|
||||
if (type !== 'spont' && type !== 'post') {
|
||||
const { type, amt, to, memo, feeLimit, ackInfo } = req.body
|
||||
if (
|
||||
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.`
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1231,17 +1236,17 @@ module.exports = async (
|
|||
})
|
||||
}
|
||||
|
||||
if (type === 'post' && typeof postID !== 'string') {
|
||||
if (type === 'tip' && typeof ackInfo !== 'string') {
|
||||
return res.status(400).json({
|
||||
field: 'postID',
|
||||
errorMessage: `Send postID`
|
||||
field: 'ackInfo',
|
||||
errorMessage: `Send ackInfo`
|
||||
})
|
||||
}
|
||||
|
||||
return res.status(200).json(
|
||||
await GunActions.sendSpontaneousPayment(to, amt, memo, feeLimit, {
|
||||
type,
|
||||
postID
|
||||
ackInfo
|
||||
})
|
||||
)
|
||||
} catch (e) {
|
||||
|
|
@ -2164,10 +2169,12 @@ module.exports = async (
|
|||
app.post(`/api/gun/wall/`, async (req, res) => {
|
||||
try {
|
||||
const { tags, title, contentItems } = req.body
|
||||
const SEA = require('../services/gunDB/Mediator').mySEA
|
||||
return res
|
||||
.status(200)
|
||||
.json(await GunActions.createPostNew(tags, title, contentItems))
|
||||
.json(await GunActions.createPostNew(tags, title, contentItems, SEA))
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return res.status(500).json({
|
||||
errorMessage:
|
||||
(typeof e === 'string' ? e : e.message) || 'Unknown error.'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue