working order ack

This commit is contained in:
hatim boufnichel 2021-01-21 17:30:12 +01:00
parent 4681f22797
commit 9f4a0b05d2
4 changed files with 242 additions and 171 deletions

View file

@ -923,7 +923,7 @@ const sendHRWithInitialMsg = async (
/** /**
* @typedef {object} SpontPaymentOptions * @typedef {object} SpontPaymentOptions
* @prop {Common.Schema.OrderTargetType} type * @prop {Common.Schema.OrderTargetType} type
* @prop {string=} postID * @prop {string=} ackInfo
*/ */
/** /**
* @typedef {object} OrderRes * @typedef {object} OrderRes
@ -968,11 +968,8 @@ const sendSpontaneousPayment = async (
from: getUser()._.sea.pub, from: getUser()._.sea.pub,
memo: memo || 'no memo', memo: memo || 'no memo',
timestamp: Date.now(), timestamp: Date.now(),
targetType: opts.type targetType: opts.type,
} ackInfo: opts.ackInfo
if (opts.type === 'tip') {
order.ackInfo = opts.postID
} }
logger.info(JSON.stringify(order)) logger.info(JSON.stringify(order))
@ -1009,7 +1006,7 @@ const sendSpontaneousPayment = async (
) )
) )
} else { } else {
res(ord._.get) setTimeout(() => res(ord._.get), 0)
} }
}) })
}) })
@ -1020,7 +1017,8 @@ const sendSpontaneousPayment = async (
)}` )}`
throw new Error(msg) throw new Error(msg)
} }
console.log('ORDER ID')
console.log(orderID)
/** @type {import('shock-common').Schema.OrderResponse} */ /** @type {import('shock-common').Schema.OrderResponse} */
const encryptedOrderRes = await Utils.tryAndWait( const encryptedOrderRes = await Utils.tryAndWait(
gun => gun =>
@ -1030,12 +1028,13 @@ const sendSpontaneousPayment = async (
.get(Key.ORDER_TO_RESPONSE) .get(Key.ORDER_TO_RESPONSE)
.get(orderID) .get(orderID)
.on(orderResponse => { .on(orderResponse => {
console.log(orderResponse)
if (Schema.isOrderResponse(orderResponse)) { if (Schema.isOrderResponse(orderResponse)) {
res(orderResponse) res(orderResponse)
} }
}) })
}), }),
v => !Schema.isOrderResponse(v) v => Schema.isOrderResponse(v)
) )
if (!Schema.isOrderResponse(encryptedOrderRes)) { if (!Schema.isOrderResponse(encryptedOrderRes)) {
@ -1046,10 +1045,12 @@ const sendSpontaneousPayment = async (
throw e throw e
} }
/** @type {import('shock-common').Schema.OrderResponse} */ /** @type {import('shock-common').Schema.OrderResponse &{ackNode:string}} */
const orderResponse = { const orderResponse = {
response: await SEA.decrypt(encryptedOrderRes.response, ourSecret), 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)) logger.info('decoded orderResponse: ' + JSON.stringify(orderResponse))
@ -1080,7 +1081,10 @@ const sendSpontaneousPayment = async (
payment_request: orderResponse.response payment_request: orderResponse.response
}) })
const myLndPub = LNDHealthMananger.lndPub const myLndPub = LNDHealthMananger.lndPub
if (opts.type !== 'contentReveal' && opts.type !== 'torrentSeed') { if (
(opts.type !== 'contentReveal' && opts.type !== 'torrentSeed') ||
!orderResponse.ackNode
) {
SchemaManager.AddOrder({ SchemaManager.AddOrder({
type: opts.type, type: opts.type,
amount: parseInt(payment.value_sat, 10), amount: parseInt(payment.value_sat, 10),
@ -1094,6 +1098,8 @@ const sendSpontaneousPayment = async (
}) })
return { payment } return { payment }
} }
console.log('ACK NODE')
console.log(orderResponse.ackNode)
/** @type {import('shock-common').Schema.OrderResponse} */ /** @type {import('shock-common').Schema.OrderResponse} */
const encryptedOrderAckRes = await Utils.tryAndWait( const encryptedOrderAckRes = await Utils.tryAndWait(
gun => gun =>
@ -1101,8 +1107,11 @@ const sendSpontaneousPayment = async (
gun gun
.user(to) .user(to)
.get(Key.ORDER_TO_RESPONSE) .get(Key.ORDER_TO_RESPONSE)
.get(orderID) .get(orderResponse.ackNode)
.on(orderResponse => { .on(orderResponse => {
console.log(orderResponse)
console.log(Schema.isOrderResponse(orderResponse))
if (Schema.isOrderResponse(orderResponse)) { if (Schema.isOrderResponse(orderResponse)) {
res(orderResponse) res(orderResponse)
} }
@ -1113,7 +1122,7 @@ const sendSpontaneousPayment = async (
if (!Schema.isOrderResponse(encryptedOrderAckRes)) { if (!Schema.isOrderResponse(encryptedOrderAckRes)) {
const e = TypeError( const e = TypeError(
`Expected OrderResponse got: ${typeof encryptedOrderAckRes}` `Expected encryptedOrderAckRes got: ${typeof encryptedOrderAckRes}`
) )
logger.error(e) logger.error(e)
throw e throw e
@ -1148,6 +1157,7 @@ const sendSpontaneousPayment = async (
}) })
return { payment, orderAck } return { payment, orderAck }
} catch (e) { } catch (e) {
console.log(e)
logger.error('Error inside sendPayment()') logger.error('Error inside sendPayment()')
logger.error(e) logger.error(e)
throw e throw e

View file

@ -2,6 +2,7 @@
* @format * @format
*/ */
// @ts-check // @ts-check
const Gun = require('gun')
const { performance } = require('perf_hooks') const { performance } = require('perf_hooks')
const logger = require('winston') const logger = require('winston')
const isFinite = require('lodash/isFinite') const isFinite = require('lodash/isFinite')
@ -201,11 +202,15 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
logger.info( logger.info(
`onOrders() -> Will now place the encrypted invoice in order to response usergraph: ${addr}` `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} */ /** @type {import('shock-common').Schema.OrderResponse} */
const orderResponse = { const orderResponse = {
response: encInvoice, response: encInvoice,
type: 'invoice' type: 'invoice',
//@ts-expect-error
ackNode
} }
const invoicePutStartTime = performance.now() const invoicePutStartTime = performance.now()
@ -231,14 +236,12 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
const invoicePutEndTime = performance.now() - invoicePutStartTime const invoicePutEndTime = performance.now() - invoicePutStartTime
logger.info(`[PERF] Added invoice to GunDB in ${invoicePutEndTime}ms`) 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 => { const invoicePaidCb = async paidInvoice => {
SchemaManager.addListenInvoice(invoice.r_hash, res) console.log('INVOICE PAID')
})
const hashString = paidInvoice.r_hash.toString('hex') const hashString = paidInvoice.r_hash.toString('hex')
const { const {
amt_paid_sat: amt, amt_paid_sat: amt,
@ -264,25 +267,37 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
break break
} }
case 'contentReveal': { case 'contentReveal': {
console.log('cONTENT REVEAL')
//assuming digital product that only requires to be unlocked //assuming digital product that only requires to be unlocked
const postID = ackInfo const postID = ackInfo
console.log('ACK INFO')
console.log(ackInfo)
if (!Common.isPopulatedString(postID)) { if (!Common.isPopulatedString(postID)) {
break //create the coordinate, but stop because of the invalid id break //create the coordinate, but stop because of the invalid id
} }
console.log('IS STRING')
const selectedPost = await new Promise(res => { const selectedPost = await new Promise(res => {
getUser() getUser()
.get(Key.POSTS_NEW) .get(Key.POSTS_NEW)
.get(postID) .get(postID)
.load(res) .load(res)
}) })
if (!Common.Schema.isPost(selectedPost)) { 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 break //create the coordinate, but stop because of the invalid post
} }
console.log('IS POST')
/** /**
* @type {Record<string,string>} <contentID,decryptedRef> * @type {Record<string,string>} <contentID,decryptedRef>
*/ */
const contentsToSend = {} const contentsToSend = {}
const mySecret = require('../../Mediator').getMySecret() const mySecret = require('../../Mediator').getMySecret()
console.log('SECRET OK')
await Common.Utils.asyncForEach( await Common.Utils.asyncForEach(
Object.entries(selectedPost.contentItems), Object.entries(selectedPost.contentItems),
async ([contentID, item]) => { async ([contentID, item]) => {
@ -304,12 +319,14 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
const encrypted = await SEA.encrypt(toSend, secret) const encrypted = await SEA.encrypt(toSend, secret)
const ordResponse = { const ordResponse = {
type: 'orderAck', type: 'orderAck',
content: encrypted response: encrypted
} }
console.log('RES READY')
await new Promise((res, rej) => { await new Promise((res, rej) => {
getUser() getUser()
.get(Key.ORDER_TO_RESPONSE) .get(Key.ORDER_TO_RESPONSE)
.get(orderID) .get(ackNode)
.put(ordResponse, ack => { .put(ordResponse, ack => {
if (ack.err && typeof ack.err !== 'number') { if (ack.err && typeof ack.err !== 'number') {
rej( rej(
@ -322,19 +339,24 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
} }
}) })
}) })
console.log('RES SENT')
break break
} }
case 'torrentSeed': { case 'torrentSeed': {
console.log('TORRENT')
const seedUrl = process.env.TORRENT_SEED_URL const seedUrl = process.env.TORRENT_SEED_URL
const seedToken = process.env.TORRENT_SEED_TOKEN const seedToken = process.env.TORRENT_SEED_TOKEN
if (!seedUrl || !seedToken) { if (!seedUrl || !seedToken) {
break //service not available break //service not available
} }
console.log('SEED URL OK')
const token = crypto.randomBytes(32).toString('hex') const token = crypto.randomBytes(32).toString('hex')
const reqData = { const reqData = {
seed_token: seedToken, seed_token: seedToken,
wallet_token: token wallet_token: token
} }
console.log(seedUrl)
console.log(seedToken)
const res = await fetch(`${seedUrl}/api/enroll_token`, { const res = await fetch(`${seedUrl}/api/enroll_token`, {
method: 'POST', method: 'POST',
headers: { headers: {
@ -342,21 +364,22 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
}, },
body: JSON.stringify(reqData) body: JSON.stringify(reqData)
}) })
if (res.ok) { if (res.status !== 200) {
break //request didnt work, save coordinate anyway break //request didnt work, save coordinate anyway
} }
console.log('RES SEED OK')
const ackData = { seedUrl, token } const ackData = { seedUrl, token }
const toSend = JSON.stringify(ackData) const toSend = JSON.stringify(ackData)
const encrypted = await SEA.encrypt(toSend, secret) const encrypted = await SEA.encrypt(toSend, secret)
const serviceResponse = { const serviceResponse = {
type: 'orderAck', type: 'orderAck',
content: encrypted response: encrypted
} }
console.log('RES SEED SENT')
await new Promise((res, rej) => { await new Promise((res, rej) => {
getUser() getUser()
.get(Key.ORDER_TO_RESPONSE) .get(Key.ORDER_TO_RESPONSE)
.get(orderID) .get(ackNode)
.put(serviceResponse, ack => { .put(serviceResponse, ack => {
if (ack.err && typeof ack.err !== 'number') { if (ack.err && typeof ack.err !== 'number') {
rej( rej(
@ -389,6 +412,36 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
toGunPub: myGunPub, toGunPub: myGunPub,
invoiceMemo: memo invoiceMemo: memo
}) })
}
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)
/** @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) { } catch (err) {
logger.error( logger.error(
`error inside onOrders, orderAddr: ${addr}, orderID: ${orderID}, order: ${JSON.stringify( `error inside onOrders, orderAddr: ${addr}, orderID: ${orderID}, order: ${JSON.stringify(

View file

@ -387,6 +387,7 @@ class SchemaManager {
.get(coordinateSHA256) .get(coordinateSHA256)
.put(encryptedOrderString, ack => { .put(encryptedOrderString, ack => {
if (ack.err && typeof ack.err !== 'number') { if (ack.err && typeof ack.err !== 'number') {
console.log(ack)
rej( rej(
new Error( new Error(
`Error saving coordinate order to user-graph: ${ack}` `Error saving coordinate order to user-graph: ${ack}`

View file

@ -1192,12 +1192,17 @@ module.exports = async (
app.post('/api/lnd/unifiedTrx', async (req, res) => { app.post('/api/lnd/unifiedTrx', async (req, res) => {
try { try {
const { type, amt, to, memo, feeLimit, postID } = req.body const { type, amt, to, memo, feeLimit, ackInfo } = req.body
if (
if (type !== 'spont' && type !== 'post') { type !== 'spontaneousPayment' &&
type !== 'tip' &&
type !== 'torrentSeed' &&
type !== 'contentReveal' &&
type !== 'other'
) {
return res.status(415).json({ return res.status(415).json({
field: 'type', 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({ return res.status(400).json({
field: 'postID', field: 'ackInfo',
errorMessage: `Send postID` errorMessage: `Send ackInfo`
}) })
} }
return res.status(200).json( return res.status(200).json(
await GunActions.sendSpontaneousPayment(to, amt, memo, feeLimit, { await GunActions.sendSpontaneousPayment(to, amt, memo, feeLimit, {
type, type,
postID ackInfo
}) })
) )
} catch (e) { } catch (e) {
@ -2164,10 +2169,12 @@ module.exports = async (
app.post(`/api/gun/wall/`, async (req, res) => { app.post(`/api/gun/wall/`, async (req, res) => {
try { try {
const { tags, title, contentItems } = req.body const { tags, title, contentItems } = req.body
const SEA = require('../services/gunDB/Mediator').mySEA
return res return res
.status(200) .status(200)
.json(await GunActions.createPostNew(tags, title, contentItems)) .json(await GunActions.createPostNew(tags, title, contentItems, SEA))
} catch (e) { } catch (e) {
console.log(e)
return res.status(500).json({ return res.status(500).json({
errorMessage: errorMessage:
(typeof e === 'string' ? e : e.message) || 'Unknown error.' (typeof e === 'string' ? e : e.message) || 'Unknown error.'