Merge pull request #53 from shocknet/chat-spont-payment

Chat spont payment
This commit is contained in:
Daniel Lugo 2020-04-03 14:06:33 -04:00 committed by GitHub
commit e6b1ca2186
3 changed files with 142 additions and 8 deletions

View file

@ -3,9 +3,6 @@
*/
const uuidv1 = require('uuid/v1')
const logger = require('winston')
const isFinite = require('lodash/isFinite')
const isNumber = require('lodash/isNumber')
const isNaN = require('lodash/isNaN')
const LightningServices = require('../../../utils/lightningServices')
@ -14,7 +11,11 @@ const Getters = require('./getters')
const Key = require('./key')
const Utils = require('./utils')
// const { promisifyGunNode: p } = Utils
const { isHandshakeRequest, isOrderResponse } = require('./schema')
const {
isHandshakeRequest,
isOrderResponse,
encodeSpontaneousPayment
} = require('./schema')
/**
* @typedef {import('./SimpleGUN').GUNNode} GUNNode
* @typedef {import('./SimpleGUN').ISEA} ISEA
@ -1027,6 +1028,7 @@ const sendPayment = async (to, amount, memo) => {
* @typedef {object} SendResponse
* @prop {string|null} payment_error
* @prop {any[]|null} payment_route
* @prop {string} payment_preimage
*/
logger.info('Will now send payment through lightning')
@ -1036,7 +1038,8 @@ const sendPayment = async (to, amount, memo) => {
payment_request: orderResponse.response
}
await new Promise((resolve, rej) => {
/** @type {string} */
const preimage = await new Promise((resolve, rej) => {
lightning.sendPaymentSync(sendPaymentSyncArgs, (
/** @type {SendErr=} */ err,
/** @type {SendResponse} */ res
@ -1050,22 +1053,31 @@ const sendPayment = async (to, amount, memo) => {
`sendPaymentSync error response: ${JSON.stringify(res)}`
)
)
} else if (!res.payment_route) {
} else if (!res.payment_route || !res.payment_preimage) {
rej(
new Error(
`sendPaymentSync no payment route response: ${JSON.stringify(
`sendPaymentSync no payment route response or preimage: ${JSON.stringify(
res
)}`
)
)
} else {
resolve()
resolve(res.payment_preimage)
}
} else {
rej(new Error('no error or response received from sendPaymentSync'))
}
})
})
if (Utils.successfulHandshakeAlreadyExists(to)) {
await sendMessage(
to,
encodeSpontaneousPayment(to, memo || 'no memo', preimage),
require('../Mediator').getUser(),
require('../Mediator').mySEA
)
}
} catch (e) {
logger.error('Error inside sendPayment()')
logger.error(e)

View file

@ -0,0 +1,15 @@
/**
* @format
* Contains types that are used throughout the application. Written in
* typescript for easier implementation.
*
* Nominal types are archieved through the enum method as outlined here:
* https://basarat.gitbook.io/typescript/main-1/nominaltyping
*/
enum _EncSpontPayment {
_ = ''
}
/**
* Spontaneous payment as found inside a chat message body.
*/
export type EncSpontPayment = _EncSpontPayment & string

View file

@ -1,6 +1,11 @@
/**
* @format
*/
const logger = require('winston')
const isFinite = require('lodash/isFinite')
const isNumber = require('lodash/isNumber')
const isNaN = require('lodash/isNaN')
/**
* @typedef {object} HandshakeRequest
* @prop {string} from Public key of the requestor.
@ -410,3 +415,105 @@ exports.isOrderResponse = o => {
return obj.type === 'err' || obj.type === 'invoice'
}
/**
* @typedef {import('./schema-types').EncSpontPayment} EncSpontPayment
*/
const ENC_SPONT_PAYMENT_PREFIX = '$$__SHOCKWALLET__SPONT__PAYMENT__'
/**
* @param {string} s
* @returns {s is EncSpontPayment}
*/
const isEncodedSpontPayment = s => s.indexOf(ENC_SPONT_PAYMENT_PREFIX) === 0
exports.isEncodedSpontPayment = isEncodedSpontPayment
/**
* @typedef {object} SpontaneousPayment
* @prop {number} amt
* @prop {string} memo
* @prop {string} preimage
*/
/**
*
* @param {EncSpontPayment} sp
* @throws {Error} If decoding fails.
* @returns {SpontaneousPayment}
*/
exports.decodeSpontPayment = sp => {
try {
const [preimage, amtStr, memo] = sp
.slice(ENC_SPONT_PAYMENT_PREFIX.length)
.split('__')
if (typeof preimage !== 'string') {
throw new TypeError('Could not parse preimage')
}
if (typeof amtStr !== 'string') {
throw new TypeError('Could not parse amt')
}
if (typeof memo !== 'string') {
throw new TypeError('Could not parse memo')
}
const amt = Number(amtStr)
if (!isNumber(amt)) {
throw new TypeError(`Could parse amount as a number, not a number?`)
}
if (isNaN(amt)) {
throw new TypeError(`Could not amount as a number, got NaN.`)
}
if (!isFinite(amt)) {
throw new TypeError(
`Amount was correctly parsed, but got a non finite number.`
)
}
return {
amt,
memo,
preimage
}
} catch (err) {
logger.debug(`Encoded spontaneous payment: ${sp}`)
logger.error(err)
throw err
}
}
/**
*
* @param {number} amt
* @param {string} memo
* @param {string} preimage
* @returns {EncSpontPayment}
*/
exports.encodeSpontaneousPayment = (amt, memo, preimage) => {
if (amt <= 0) {
throw new RangeError('Amt must be greater than zero')
}
if (memo.length < 1) {
throw new TypeError('Memo must be populated')
}
if (preimage.length < 1) {
throw new TypeError('preimage must be populated')
}
const enc = `${ENC_SPONT_PAYMENT_PREFIX}__${amt}__${memo}__${preimage}`
if (isEncodedSpontPayment(enc)) {
return enc
}
throw new Error('isEncodedSpontPayment(enc) false')
}