Merge pull request #148 from shocknet/fix/sendpayment-fix
clean send payment
This commit is contained in:
commit
135d52da08
3 changed files with 147 additions and 75 deletions
|
|
@ -509,6 +509,7 @@ class Mediator {
|
||||||
Action.GENERATE_NEW_HANDSHAKE_NODE,
|
Action.GENERATE_NEW_HANDSHAKE_NODE,
|
||||||
this.generateHandshakeNode
|
this.generateHandshakeNode
|
||||||
)
|
)
|
||||||
|
this.socket.on('GENERATE_ORDER_ADDRESS', this.generateOrderAddress)
|
||||||
this.socket.on(Action.SEND_HANDSHAKE_REQUEST, this.sendHandshakeRequest)
|
this.socket.on(Action.SEND_HANDSHAKE_REQUEST, this.sendHandshakeRequest)
|
||||||
this.socket.on(
|
this.socket.on(
|
||||||
Action.SEND_HANDSHAKE_REQUEST_WITH_INITIAL_MSG,
|
Action.SEND_HANDSHAKE_REQUEST_WITH_INITIAL_MSG,
|
||||||
|
|
@ -745,6 +746,32 @@ class Mediator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Readonly<{ token: string }>} body
|
||||||
|
*/
|
||||||
|
generateOrderAddress = async body => {
|
||||||
|
try {
|
||||||
|
const { token } = body
|
||||||
|
|
||||||
|
await throwOnInvalidToken(token)
|
||||||
|
|
||||||
|
await API.Actions.generateOrderAddress(user)
|
||||||
|
|
||||||
|
this.socket.emit('GENERATE_ORDER_ADDRESS', {
|
||||||
|
ok: true,
|
||||||
|
msg: null,
|
||||||
|
origBody: body
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
logger.info(err)
|
||||||
|
this.socket.emit('GENERATE_ORDER_ADDRESS', {
|
||||||
|
ok: false,
|
||||||
|
msg: err.message,
|
||||||
|
origBody: body
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Readonly<{ recipientPublicKey: string , token: string }>} body
|
* @param {Readonly<{ recipientPublicKey: string , token: string }>} body
|
||||||
*/
|
*/
|
||||||
|
|
@ -861,15 +888,20 @@ class Mediator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Readonly<{ uuid: string, recipientPub: string, amount: number, memo: string, token: string }>} reqBody
|
* @param {Readonly<{ uuid: string, recipientPub: string, amount: number, memo: string, token: string, feeLimit:number }>} reqBody
|
||||||
*/
|
*/
|
||||||
sendPayment = async reqBody => {
|
sendPayment = async reqBody => {
|
||||||
try {
|
try {
|
||||||
const { recipientPub, amount, memo, token } = reqBody
|
const { recipientPub, amount, memo, feeLimit, token } = reqBody
|
||||||
|
|
||||||
await throwOnInvalidToken(token)
|
await throwOnInvalidToken(token)
|
||||||
|
|
||||||
const preimage = await API.Actions.sendPayment(recipientPub, amount, memo)
|
const preimage = await API.Actions.sendPayment(
|
||||||
|
recipientPub,
|
||||||
|
amount,
|
||||||
|
memo,
|
||||||
|
feeLimit
|
||||||
|
)
|
||||||
|
|
||||||
this.socket.emit(Action.SEND_PAYMENT, {
|
this.socket.emit(Action.SEND_PAYMENT, {
|
||||||
ok: true,
|
ok: true,
|
||||||
|
|
|
||||||
|
|
@ -902,11 +902,21 @@ const sendHRWithInitialMsg = async (
|
||||||
* @param {string} to
|
* @param {string} to
|
||||||
* @param {number} amount
|
* @param {number} amount
|
||||||
* @param {string} memo
|
* @param {string} memo
|
||||||
|
* @param {number} feeLimit
|
||||||
|
* @param {number=} maxParts
|
||||||
|
* @param {number=} timeoutSeconds
|
||||||
* @throws {Error} If no response in less than 20 seconds from the recipient, or
|
* @throws {Error} If no response in less than 20 seconds from the recipient, or
|
||||||
* lightning cannot find a route for the payment.
|
* lightning cannot find a route for the payment.
|
||||||
* @returns {Promise<string>} The payment's preimage.
|
* @returns {Promise<string>} The payment's preimage.
|
||||||
*/
|
*/
|
||||||
const sendPayment = async (to, amount, memo) => {
|
const sendPayment = async (
|
||||||
|
to,
|
||||||
|
amount,
|
||||||
|
memo,
|
||||||
|
feeLimit,
|
||||||
|
maxParts = 3,
|
||||||
|
timeoutSeconds = 5
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const SEA = require('../Mediator').mySEA
|
const SEA = require('../Mediator').mySEA
|
||||||
const getUser = () => require('../Mediator').getUser()
|
const getUser = () => require('../Mediator').getUser()
|
||||||
|
|
@ -1037,7 +1047,7 @@ const sendPayment = async (to, amount, memo) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
services: { lightning }
|
services: { router }
|
||||||
} = LightningServices
|
} = LightningServices
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1047,50 +1057,46 @@ const sendPayment = async (to, amount, memo) => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partial
|
* Partial
|
||||||
* https://api.lightning.community/#grpc-response-sendresponse-2
|
* https://api.lightning.community/#sendpaymentv2
|
||||||
* @typedef {object} SendResponse
|
* @typedef {object} SendResponse
|
||||||
* @prop {string|null} payment_error
|
* @prop {string} failure_reason
|
||||||
* @prop {any[]|null} payment_route
|
|
||||||
* @prop {string} payment_preimage
|
* @prop {string} payment_preimage
|
||||||
*/
|
*/
|
||||||
|
|
||||||
logger.info('Will now send payment through lightning')
|
logger.info('Will now send payment through lightning')
|
||||||
|
|
||||||
const sendPaymentSyncArgs = {
|
const sendPaymentV2Args = {
|
||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
payment_request: orderResponse.response
|
payment_request: orderResponse.response,
|
||||||
|
max_parts: maxParts,
|
||||||
|
timeout_seconds: timeoutSeconds,
|
||||||
|
no_inflight_updates: true,
|
||||||
|
fee_limit_sat: feeLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {string} */
|
const preimage = await new Promise((res, rej) => {
|
||||||
const preimage = await new Promise((resolve, rej) => {
|
const sentPaymentStream = router.sendPaymentV2(sendPaymentV2Args)
|
||||||
lightning.sendPaymentSync(sendPaymentSyncArgs, (
|
/**
|
||||||
/** @type {SendErr=} */ err,
|
* @param {SendResponse} response
|
||||||
/** @type {SendResponse} */ res
|
*/
|
||||||
) => {
|
const dataCB = response => {
|
||||||
if (err) {
|
logger.info('SendPayment Data:', response)
|
||||||
rej(new Error(err.details))
|
if (response.failure_reason !== 'FAILURE_REASON_NONE') {
|
||||||
} else if (res) {
|
rej(new Error(response.failure_reason))
|
||||||
if (res.payment_error) {
|
|
||||||
rej(
|
|
||||||
new Error(
|
|
||||||
`sendPaymentSync error response: ${JSON.stringify(res)}`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else if (!res.payment_route || !res.payment_preimage) {
|
|
||||||
rej(
|
|
||||||
new Error(
|
|
||||||
`sendPaymentSync no payment route response or preimage: ${JSON.stringify(
|
|
||||||
res
|
|
||||||
)}`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
resolve(res.payment_preimage)
|
res(response.payment_preimage)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
rej(new Error('no error or response received from sendPaymentSync'))
|
|
||||||
}
|
}
|
||||||
})
|
sentPaymentStream.on('data', dataCB)
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {SendErr} err
|
||||||
|
*/
|
||||||
|
const errCB = err => {
|
||||||
|
logger.error('SendPayment Error:', err)
|
||||||
|
rej(err.details)
|
||||||
|
}
|
||||||
|
sentPaymentStream.on('error', errCB)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (Utils.successfulHandshakeAlreadyExists(to)) {
|
if (Utils.successfulHandshakeAlreadyExists(to)) {
|
||||||
|
|
|
||||||
|
|
@ -1446,7 +1446,12 @@ module.exports = async (
|
||||||
const { router } = LightningServices.services
|
const { router } = LightningServices.services
|
||||||
// this is the recommended value from lightning labs
|
// this is the recommended value from lightning labs
|
||||||
let paymentRequest = {}
|
let paymentRequest = {}
|
||||||
const { keysend, maxParts = 3, timeoutSeconds = 5 } = req.body
|
const { keysend, maxParts = 3, timeoutSeconds = 5, feeLimit } = req.body
|
||||||
|
if (!feeLimit) {
|
||||||
|
return res.status(500).json({
|
||||||
|
errorMessage: 'please provide a "feeLimit" to the send payment request'
|
||||||
|
})
|
||||||
|
}
|
||||||
if (keysend) {
|
if (keysend) {
|
||||||
const { dest, amt, finalCltvDelta = 40 } = req.body
|
const { dest, amt, finalCltvDelta = 40 } = req.body
|
||||||
if (!dest || !amt) {
|
if (!dest || !amt) {
|
||||||
|
|
@ -1474,7 +1479,8 @@ module.exports = async (
|
||||||
payment_hash: r_hash,
|
payment_hash: r_hash,
|
||||||
max_parts: maxParts,
|
max_parts: maxParts,
|
||||||
timeout_seconds: timeoutSeconds,
|
timeout_seconds: timeoutSeconds,
|
||||||
no_inflight_updates: true
|
no_inflight_updates: true,
|
||||||
|
fee_limit_sat: feeLimit
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const { payreq } = req.body
|
const { payreq } = req.body
|
||||||
|
|
@ -1483,7 +1489,8 @@ module.exports = async (
|
||||||
payment_request: payreq,
|
payment_request: payreq,
|
||||||
max_parts: maxParts,
|
max_parts: maxParts,
|
||||||
timeout_seconds: timeoutSeconds,
|
timeout_seconds: timeoutSeconds,
|
||||||
no_inflight_updates: true
|
no_inflight_updates: true,
|
||||||
|
fee_limit_sat: feeLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.body.amt) {
|
if (req.body.amt) {
|
||||||
|
|
@ -1493,25 +1500,14 @@ module.exports = async (
|
||||||
|
|
||||||
logger.info('Sending payment', paymentRequest)
|
logger.info('Sending payment', paymentRequest)
|
||||||
const sentPayment = router.sendPaymentV2(paymentRequest)
|
const sentPayment = router.sendPaymentV2(paymentRequest)
|
||||||
let finalEvent = null //Object to send to the socket, depends on final event from the stream
|
|
||||||
sentPayment.on('data', response => {
|
sentPayment.on('data', response => {
|
||||||
if (res.headersSent) {
|
|
||||||
//if res was already sent
|
|
||||||
if (response.status !== 'SUCCEEDED') {
|
|
||||||
//if the operation failed
|
|
||||||
logger.error('Sen payment failure', response.details)
|
|
||||||
} else {
|
|
||||||
finalEvent = { status: response.status }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (response.status !== 'SUCCEEDED') {
|
|
||||||
logger.error('Sen payment failure', response.details)
|
|
||||||
return res.status(500).json({
|
|
||||||
errorMessage: sanitizeLNDError(response.details)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
logger.info('SendPayment Data:', response)
|
logger.info('SendPayment Data:', response)
|
||||||
return res.json(response)
|
if (response.failure_reason !== 'FAILURE_REASON_NONE') {
|
||||||
|
res.status(500).json({
|
||||||
|
errorMessage: response.failure_reason
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
res.json(response)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -1521,26 +1517,17 @@ module.exports = async (
|
||||||
|
|
||||||
sentPayment.on('error', async err => {
|
sentPayment.on('error', async err => {
|
||||||
logger.error('SendPayment Error:', err)
|
logger.error('SendPayment Error:', err)
|
||||||
if (res.headersSent) {
|
|
||||||
logger.error('Sen payment failure', err)
|
|
||||||
} else {
|
|
||||||
const health = await checkHealth()
|
const health = await checkHealth()
|
||||||
if (health.LNDStatus.success) {
|
if (health.LNDStatus.success) {
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
errorMessage: sanitizeLNDError(err)
|
errorMessage: sanitizeLNDError(err.details)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
res.status(500)
|
res.status(500)
|
||||||
res.json({ errorMessage: 'LND is down' })
|
res.json({ errorMessage: 'LND is down' })
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
sentPayment.on('end', () => {
|
|
||||||
if (finalEvent !== null) {
|
|
||||||
//send the last event got from the stream
|
|
||||||
//TO DO send finalEvent on socket
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
//sentPayment.on('end', () => {})
|
||||||
})
|
})
|
||||||
|
|
||||||
app.post('/api/lnd/trackpayment', (req, res) => {
|
app.post('/api/lnd/trackpayment', (req, res) => {
|
||||||
|
|
@ -2026,6 +2013,53 @@ module.exports = async (
|
||||||
})
|
})
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
app.post(`/api/gun/sendpayment`, async (req, res) => {
|
||||||
|
const {
|
||||||
|
recipientPub,
|
||||||
|
amount,
|
||||||
|
memo,
|
||||||
|
maxParts,
|
||||||
|
timeoutSeconds,
|
||||||
|
feeLimit,
|
||||||
|
sessionUuid
|
||||||
|
} = req.body
|
||||||
|
logger.info('handling spont pay')
|
||||||
|
if (!feeLimit) {
|
||||||
|
logger.error(
|
||||||
|
'please provide a "feeLimit" to the send spont payment request'
|
||||||
|
)
|
||||||
|
return res.status(500).json({
|
||||||
|
errorMessage:
|
||||||
|
'please provide a "feeLimit" to the send spont payment request'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!recipientPub || !amount) {
|
||||||
|
logger.info(
|
||||||
|
'please provide a "recipientPub" and "amount" to the send spont payment request'
|
||||||
|
)
|
||||||
|
return res.status(500).json({
|
||||||
|
errorMessage:
|
||||||
|
'please provide a "recipientPub" and "amount" to the send spont payment request'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const preimage = await GunActions.sendPayment(
|
||||||
|
recipientPub,
|
||||||
|
amount,
|
||||||
|
memo,
|
||||||
|
feeLimit,
|
||||||
|
maxParts,
|
||||||
|
timeoutSeconds
|
||||||
|
)
|
||||||
|
res.json({ preimage, sessionUuid })
|
||||||
|
} catch (err) {
|
||||||
|
logger.info('spont pay err:', err)
|
||||||
|
return res.status(500).json({
|
||||||
|
errorMessage: err.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
app.get(`/api/gun/wall/:publicKey?`, async (req, res) => {
|
app.get(`/api/gun/wall/:publicKey?`, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { page } = req.query
|
const { page } = req.query
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue