commit
2c744fb92a
9 changed files with 187 additions and 244 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"eslint.enable": true,
|
"eslint.enable": true,
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"debug.node.autoAttach": "on"
|
"debug.node.autoAttach": "on",
|
||||||
|
"editor.formatOnSave": true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ module.exports = (mainnet = false) => {
|
||||||
maxNumRoutesToQuery: 20,
|
maxNumRoutesToQuery: 20,
|
||||||
lndProto: parsePath(`${__dirname}/rpc.proto`),
|
lndProto: parsePath(`${__dirname}/rpc.proto`),
|
||||||
routerProto: parsePath(`${__dirname}/router.proto`),
|
routerProto: parsePath(`${__dirname}/router.proto`),
|
||||||
|
invoicesProto: parsePath(`${__dirname}/invoices.proto`),
|
||||||
walletUnlockerProto: parsePath(`${__dirname}/walletunlocker.proto`),
|
walletUnlockerProto: parsePath(`${__dirname}/walletunlocker.proto`),
|
||||||
lndHost: "localhost:10009",
|
lndHost: "localhost:10009",
|
||||||
lndCertPath: parsePath(`${lndDirectory}/tls.cert`),
|
lndCertPath: parsePath(`${lndDirectory}/tls.cert`),
|
||||||
|
|
|
||||||
122
config/invoices.proto
Normal file
122
config/invoices.proto
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "rpc.proto";
|
||||||
|
|
||||||
|
package invoicesrpc;
|
||||||
|
|
||||||
|
option go_package = "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc";
|
||||||
|
|
||||||
|
// Invoices is a service that can be used to create, accept, settle and cancel
|
||||||
|
// invoices.
|
||||||
|
service Invoices {
|
||||||
|
/*
|
||||||
|
SubscribeSingleInvoice returns a uni-directional stream (server -> client)
|
||||||
|
to notify the client of state transitions of the specified invoice.
|
||||||
|
Initially the current invoice state is always sent out.
|
||||||
|
*/
|
||||||
|
rpc SubscribeSingleInvoice (SubscribeSingleInvoiceRequest)
|
||||||
|
returns (stream lnrpc.Invoice);
|
||||||
|
|
||||||
|
/*
|
||||||
|
CancelInvoice cancels a currently open invoice. If the invoice is already
|
||||||
|
canceled, this call will succeed. If the invoice is already settled, it will
|
||||||
|
fail.
|
||||||
|
*/
|
||||||
|
rpc CancelInvoice (CancelInvoiceMsg) returns (CancelInvoiceResp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
AddHoldInvoice creates a hold invoice. It ties the invoice to the hash
|
||||||
|
supplied in the request.
|
||||||
|
*/
|
||||||
|
rpc AddHoldInvoice (AddHoldInvoiceRequest) returns (AddHoldInvoiceResp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
SettleInvoice settles an accepted invoice. If the invoice is already
|
||||||
|
settled, this call will succeed.
|
||||||
|
*/
|
||||||
|
rpc SettleInvoice (SettleInvoiceMsg) returns (SettleInvoiceResp);
|
||||||
|
}
|
||||||
|
|
||||||
|
message CancelInvoiceMsg {
|
||||||
|
// Hash corresponding to the (hold) invoice to cancel.
|
||||||
|
bytes payment_hash = 1;
|
||||||
|
}
|
||||||
|
message CancelInvoiceResp {
|
||||||
|
}
|
||||||
|
|
||||||
|
message AddHoldInvoiceRequest {
|
||||||
|
/*
|
||||||
|
An optional memo to attach along with the invoice. Used for record keeping
|
||||||
|
purposes for the invoice's creator, and will also be set in the description
|
||||||
|
field of the encoded payment request if the description_hash field is not
|
||||||
|
being used.
|
||||||
|
*/
|
||||||
|
string memo = 1;
|
||||||
|
|
||||||
|
// The hash of the preimage
|
||||||
|
bytes hash = 2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The value of this invoice in satoshis
|
||||||
|
|
||||||
|
The fields value and value_msat are mutually exclusive.
|
||||||
|
*/
|
||||||
|
int64 value = 3;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The value of this invoice in millisatoshis
|
||||||
|
|
||||||
|
The fields value and value_msat are mutually exclusive.
|
||||||
|
*/
|
||||||
|
int64 value_msat = 10;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hash (SHA-256) of a description of the payment. Used if the description of
|
||||||
|
payment (memo) is too long to naturally fit within the description field
|
||||||
|
of an encoded payment request.
|
||||||
|
*/
|
||||||
|
bytes description_hash = 4;
|
||||||
|
|
||||||
|
// Payment request expiry time in seconds. Default is 3600 (1 hour).
|
||||||
|
int64 expiry = 5;
|
||||||
|
|
||||||
|
// Fallback on-chain address.
|
||||||
|
string fallback_addr = 6;
|
||||||
|
|
||||||
|
// Delta to use for the time-lock of the CLTV extended to the final hop.
|
||||||
|
uint64 cltv_expiry = 7;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Route hints that can each be individually used to assist in reaching the
|
||||||
|
invoice's destination.
|
||||||
|
*/
|
||||||
|
repeated lnrpc.RouteHint route_hints = 8;
|
||||||
|
|
||||||
|
// Whether this invoice should include routing hints for private channels.
|
||||||
|
bool private = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AddHoldInvoiceResp {
|
||||||
|
/*
|
||||||
|
A bare-bones invoice for a payment within the Lightning Network. With the
|
||||||
|
details of the invoice, the sender has all the data necessary to send a
|
||||||
|
payment to the recipient.
|
||||||
|
*/
|
||||||
|
string payment_request = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SettleInvoiceMsg {
|
||||||
|
// Externally discovered pre-image that should be used to settle the hold
|
||||||
|
// invoice.
|
||||||
|
bytes preimage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SettleInvoiceResp {
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubscribeSingleInvoiceRequest {
|
||||||
|
reserved 1;
|
||||||
|
|
||||||
|
// Hash corresponding to the (hold) invoice to subscribe to.
|
||||||
|
bytes r_hash = 2;
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
/**
|
/**
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
// @ts-check
|
||||||
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')
|
||||||
const isNumber = require('lodash/isNumber')
|
const isNumber = require('lodash/isNumber')
|
||||||
const isNaN = require('lodash/isNaN')
|
const isNaN = require('lodash/isNaN')
|
||||||
|
const Common = require('shock-common')
|
||||||
const {
|
const {
|
||||||
Constants: { ErrorCode },
|
Constants: { ErrorCode },
|
||||||
Schema
|
Schema
|
||||||
} = require('shock-common')
|
} = Common
|
||||||
|
|
||||||
const LightningServices = require('../../../../utils/lightningServices')
|
const LightningServices = require('../../../../utils/lightningServices')
|
||||||
|
|
||||||
|
|
@ -226,30 +227,49 @@ const listenerForAddr = (addr, SEA) => async (order, orderID) => {
|
||||||
|
|
||||||
const invoicePutEndTime = performance.now() - invoicePutStartTime
|
const invoicePutEndTime = performance.now() - invoicePutStartTime
|
||||||
|
|
||||||
|
const hash = invoice.r_hash.toString('base64')
|
||||||
|
|
||||||
|
// invoices should be settled right away so we can rely on this single
|
||||||
|
// subscription instead of life-long all invoices subscription
|
||||||
|
if (order.targetType === 'post') {
|
||||||
|
const { subscribeSingleInvoice } = LightningServices.invoices
|
||||||
|
const { postID } = order
|
||||||
|
|
||||||
|
if (!Common.isPopulatedString(postID)) {
|
||||||
|
throw new TypeError(`postID not a a populated string`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const stream = subscribeSingleInvoice({ r_hash: hash })
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Common.Invoice} invoice
|
||||||
|
*/
|
||||||
|
const onData = invoice => {
|
||||||
|
if (invoice.settled) {
|
||||||
|
getUser()
|
||||||
|
.get('postToTipCount')
|
||||||
|
.get(postID)
|
||||||
|
.set(null) // each item in the set is a tip
|
||||||
|
|
||||||
|
stream.off()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.on('data', onData)
|
||||||
|
|
||||||
|
stream.on('status', (/** @type {any} */ status) => {
|
||||||
|
logger.info(`Post tip, post: ${postID}, invoice status: ${status}`)
|
||||||
|
})
|
||||||
|
stream.on('end', () => {
|
||||||
|
logger.warn(`Post tip, post: ${postID}, invoice stream ended`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
logger.info(`[PERF] Added invoice to GunDB in ${invoicePutEndTime}ms`)
|
logger.info(`[PERF] Added invoice to GunDB in ${invoicePutEndTime}ms`)
|
||||||
|
|
||||||
const listenerEndTime = performance.now() - listenerStartTime
|
const listenerEndTime = performance.now() - listenerStartTime
|
||||||
|
|
||||||
logger.info(`[PERF] Invoice generation completed in ${listenerEndTime}ms`)
|
logger.info(`[PERF] Invoice generation completed in ${listenerEndTime}ms`)
|
||||||
|
|
||||||
const hash = invoice.r_hash.toString('base64')
|
|
||||||
|
|
||||||
if (order.targetType === 'post') {
|
|
||||||
/** @type {TipPaymentStatus} */
|
|
||||||
const paymentStatus = {
|
|
||||||
hash,
|
|
||||||
state: 'OPEN',
|
|
||||||
targetType: order.targetType,
|
|
||||||
postID: order.postID
|
|
||||||
}
|
|
||||||
getUser()
|
|
||||||
.get(Key.TIPS_PAYMENT_STATUS)
|
|
||||||
.get(hash)
|
|
||||||
// @ts-ignore
|
|
||||||
.put(paymentStatus, response => {
|
|
||||||
console.log(response)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} 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(
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,6 @@ exports.POSTS = 'posts'
|
||||||
// Tips counter for posts
|
// Tips counter for posts
|
||||||
exports.TOTAL_TIPS = 'totalTips'
|
exports.TOTAL_TIPS = 'totalTips'
|
||||||
|
|
||||||
exports.TIPS_PAYMENT_STATUS = 'tipsPaymentStatus'
|
|
||||||
|
|
||||||
exports.PROFILE_BINARY = 'profileBinary'
|
exports.PROFILE_BINARY = 'profileBinary'
|
||||||
|
|
||||||
exports.POSTS_NEW = 'posts'
|
exports.POSTS_NEW = 'posts'
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ const errorConstants = require("../../constants/errors");
|
||||||
* @typedef LightningConfig
|
* @typedef LightningConfig
|
||||||
* @prop {string} lnrpcProtoPath
|
* @prop {string} lnrpcProtoPath
|
||||||
* @prop {string} routerProtoPath
|
* @prop {string} routerProtoPath
|
||||||
|
* @prop {string} invoicesProtoPath
|
||||||
* @prop {string} walletUnlockerProtoPath
|
* @prop {string} walletUnlockerProtoPath
|
||||||
* @prop {string} lndHost
|
* @prop {string} lndHost
|
||||||
* @prop {string} lndCertPath
|
* @prop {string} lndCertPath
|
||||||
|
|
@ -21,6 +22,7 @@ const errorConstants = require("../../constants/errors");
|
||||||
* @prop {any} lightning
|
* @prop {any} lightning
|
||||||
* @prop {any} walletUnlocker
|
* @prop {any} walletUnlocker
|
||||||
* @prop {any} router
|
* @prop {any} router
|
||||||
|
* @prop {any} invoices
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -30,6 +32,7 @@ const errorConstants = require("../../constants/errors");
|
||||||
module.exports = async ({
|
module.exports = async ({
|
||||||
lnrpcProtoPath,
|
lnrpcProtoPath,
|
||||||
routerProtoPath,
|
routerProtoPath,
|
||||||
|
invoicesProtoPath,
|
||||||
walletUnlockerProtoPath,
|
walletUnlockerProtoPath,
|
||||||
lndHost,
|
lndHost,
|
||||||
lndCertPath,
|
lndCertPath,
|
||||||
|
|
@ -46,9 +49,15 @@ module.exports = async ({
|
||||||
includeDirs: ["node_modules/google-proto-files", "proto", Path.resolve(__dirname, "../../config")]
|
includeDirs: ["node_modules/google-proto-files", "proto", Path.resolve(__dirname, "../../config")]
|
||||||
}
|
}
|
||||||
|
|
||||||
const [lnrpcProto, routerProto, walletUnlockerProto] = await Promise.all([protoLoader.load(lnrpcProtoPath, protoLoaderConfig), protoLoader.load(routerProtoPath, protoLoaderConfig), protoLoader.load(walletUnlockerProtoPath, protoLoaderConfig)]);
|
const [lnrpcProto, routerProto, walletUnlockerProto, invoicesProto] = await Promise.all([
|
||||||
|
protoLoader.load(lnrpcProtoPath, protoLoaderConfig),
|
||||||
|
protoLoader.load(routerProtoPath, protoLoaderConfig),
|
||||||
|
protoLoader.load(walletUnlockerProtoPath, protoLoaderConfig),
|
||||||
|
protoLoader.load(invoicesProtoPath, protoLoaderConfig)
|
||||||
|
]);
|
||||||
const { lnrpc } = grpc.loadPackageDefinition(lnrpcProto);
|
const { lnrpc } = grpc.loadPackageDefinition(lnrpcProto);
|
||||||
const { routerrpc } = grpc.loadPackageDefinition(routerProto);
|
const { routerrpc } = grpc.loadPackageDefinition(routerProto);
|
||||||
|
const { invoicesrpc } = grpc.loadPackageDefinition(invoicesProto);
|
||||||
const { lnrpc: walletunlockerrpc } = grpc.loadPackageDefinition(walletUnlockerProto);
|
const { lnrpc: walletunlockerrpc } = grpc.loadPackageDefinition(walletUnlockerProto);
|
||||||
|
|
||||||
const getCredentials = async () => {
|
const getCredentials = async () => {
|
||||||
|
|
@ -93,11 +102,13 @@ module.exports = async ({
|
||||||
const walletUnlocker = new walletunlockerrpc.WalletUnlocker(lndHost, credentials);
|
const walletUnlocker = new walletunlockerrpc.WalletUnlocker(lndHost, credentials);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const router = new routerrpc.Router(lndHost, credentials);
|
const router = new routerrpc.Router(lndHost, credentials);
|
||||||
|
// @ts-ignore
|
||||||
|
const invoices = new invoicesrpc.Invoices(lndHost, credentials);
|
||||||
return {
|
return {
|
||||||
lightning,
|
lightning,
|
||||||
walletUnlocker,
|
walletUnlocker,
|
||||||
router
|
router,
|
||||||
|
invoices
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ const {
|
||||||
sendPaymentV2Invoice,
|
sendPaymentV2Invoice,
|
||||||
listPayments
|
listPayments
|
||||||
} = require('../utils/lightningServices/v2')
|
} = require('../utils/lightningServices/v2')
|
||||||
const { startTipStatusJob } = require('../utils/lndJobs')
|
|
||||||
const GunWriteRPC = require('../services/gunDB/rpc')
|
const GunWriteRPC = require('../services/gunDB/rpc')
|
||||||
|
|
||||||
const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10
|
const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10
|
||||||
|
|
@ -690,7 +689,6 @@ module.exports = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
onNewChannelBackup()
|
onNewChannelBackup()
|
||||||
startTipStatusJob()
|
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
authorization: token,
|
authorization: token,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ const lnrpc = require('../../services/lnd/lightning')
|
||||||
* @prop {string} macaroonPath
|
* @prop {string} macaroonPath
|
||||||
* @prop {string} lndProto
|
* @prop {string} lndProto
|
||||||
* @prop {string} routerProto
|
* @prop {string} routerProto
|
||||||
|
* @prop {string} invoicesProto
|
||||||
* @prop {string} walletUnlockerProto
|
* @prop {string} walletUnlockerProto
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -119,6 +120,7 @@ class LightningServices {
|
||||||
const lnServices = await lnrpc({
|
const lnServices = await lnrpc({
|
||||||
lnrpcProtoPath: this.defaults.lndProto,
|
lnrpcProtoPath: this.defaults.lndProto,
|
||||||
routerProtoPath: this.defaults.routerProto,
|
routerProtoPath: this.defaults.routerProto,
|
||||||
|
invoicesProtoPath: this.defaults.invoicesProto,
|
||||||
walletUnlockerProtoPath: this.defaults.walletUnlockerProto,
|
walletUnlockerProtoPath: this.defaults.walletUnlockerProto,
|
||||||
lndHost,
|
lndHost,
|
||||||
lndCertPath,
|
lndCertPath,
|
||||||
|
|
@ -127,10 +129,11 @@ class LightningServices {
|
||||||
if (!lnServices) {
|
if (!lnServices) {
|
||||||
throw new Error(`Could not init lnServices`)
|
throw new Error(`Could not init lnServices`)
|
||||||
}
|
}
|
||||||
const { lightning, walletUnlocker, router } = lnServices
|
const { lightning, walletUnlocker, router, invoices } = lnServices
|
||||||
this.lightning = lightning
|
this.lightning = lightning
|
||||||
this.walletUnlocker = walletUnlocker
|
this.walletUnlocker = walletUnlocker
|
||||||
this.router = router
|
this.router = router
|
||||||
|
this.invoices = invoices
|
||||||
this.lnServicesData = {
|
this.lnServicesData = {
|
||||||
lndProto: this.defaults.lndProto,
|
lndProto: this.defaults.lndProto,
|
||||||
lndHost,
|
lndHost,
|
||||||
|
|
|
||||||
211
utils/lndJobs.js
211
utils/lndJobs.js
|
|
@ -1,211 +0,0 @@
|
||||||
/**
|
|
||||||
* @prettier
|
|
||||||
*/
|
|
||||||
const Logger = require('winston')
|
|
||||||
const { wait } = require('./helpers')
|
|
||||||
const Key = require('../services/gunDB/contact-api/key')
|
|
||||||
const { getUser } = require('../services/gunDB/Mediator')
|
|
||||||
const LightningServices = require('./lightningServices')
|
|
||||||
|
|
||||||
const ERROR_TRIES_THRESHOLD = 3
|
|
||||||
const ERROR_TRIES_DELAY = 500
|
|
||||||
const INVOICE_STATE = {
|
|
||||||
OPEN: 'OPEN',
|
|
||||||
SETTLED: 'SETTLED',
|
|
||||||
CANCELLED: 'CANCELLED',
|
|
||||||
ACCEPTED: 'ACCEPTED'
|
|
||||||
}
|
|
||||||
|
|
||||||
const _lookupInvoice = hash =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
const { lightning } = LightningServices.services
|
|
||||||
lightning.lookupInvoice({ r_hash: hash }, (err, response) => {
|
|
||||||
if (err) {
|
|
||||||
Logger.error(
|
|
||||||
'[TIP] An error has occurred while trying to lookup invoice:',
|
|
||||||
err,
|
|
||||||
'\nInvoice Hash:',
|
|
||||||
hash
|
|
||||||
)
|
|
||||||
reject(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.info('[TIP] Invoice lookup result:', response)
|
|
||||||
resolve(response)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const _getPostTipInfo = ({ postID }) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
getUser()
|
|
||||||
.get(Key.POSTS_NEW)
|
|
||||||
.get(postID)
|
|
||||||
.once(post => {
|
|
||||||
if (post && post.date) {
|
|
||||||
const { tipCounter, tipValue } = post
|
|
||||||
resolve({
|
|
||||||
tipCounter: typeof tipCounter === 'number' ? tipCounter : 0,
|
|
||||||
tipValue: typeof tipValue === 'number' ? tipValue : 0
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(post)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const _incrementPost = ({ postID, orderAmount }) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
const parsedAmount = parseFloat(orderAmount)
|
|
||||||
|
|
||||||
if (typeof parsedAmount !== 'number') {
|
|
||||||
reject(new Error('Invalid order amount specified'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.info('[POST TIP] Getting Post Tip Values...')
|
|
||||||
|
|
||||||
return _getPostTipInfo({ postID })
|
|
||||||
.then(({ tipValue, tipCounter }) => {
|
|
||||||
const updatedTip = {
|
|
||||||
tipCounter: tipCounter + 1,
|
|
||||||
tipValue: tipValue + parsedAmount
|
|
||||||
}
|
|
||||||
|
|
||||||
getUser()
|
|
||||||
.get(Key.POSTS_NEW)
|
|
||||||
.get(postID)
|
|
||||||
.put(updatedTip, () => {
|
|
||||||
Logger.info('[POST TIP] Successfully updated Post tip info')
|
|
||||||
resolve(updatedTip)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
Logger.error(err)
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const _updateTipData = (invoiceHash, data) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
try {
|
|
||||||
getUser()
|
|
||||||
.get(Key.TIPS_PAYMENT_STATUS)
|
|
||||||
.get(invoiceHash)
|
|
||||||
.put(data, tip => {
|
|
||||||
if (tip === undefined) {
|
|
||||||
reject(new Error('Tip update failed'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(tip)
|
|
||||||
|
|
||||||
resolve(tip)
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
Logger.error('An error has occurred while updating tip^data')
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const _getTipData = (invoiceHash, tries = 0) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
if (tries >= ERROR_TRIES_THRESHOLD) {
|
|
||||||
reject(new Error('Malformed data'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
getUser()
|
|
||||||
.get(Key.TIPS_PAYMENT_STATUS)
|
|
||||||
.get(invoiceHash)
|
|
||||||
.once(async tip => {
|
|
||||||
try {
|
|
||||||
if (tip === undefined) {
|
|
||||||
await wait(ERROR_TRIES_DELAY)
|
|
||||||
const tip = await _getTipData(invoiceHash, tries + 1)
|
|
||||||
|
|
||||||
if (tip) {
|
|
||||||
resolve(tip)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
reject(new Error('Malformed data'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(tip)
|
|
||||||
} catch (err) {
|
|
||||||
reject(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const executeTipAction = (tip, invoice) => {
|
|
||||||
if (invoice.state !== INVOICE_STATE.SETTLED) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute actions once invoice is settled
|
|
||||||
Logger.info('Invoice settled!', invoice)
|
|
||||||
|
|
||||||
if (tip.targetType === 'post') {
|
|
||||||
_incrementPost({
|
|
||||||
postID: tip.postID,
|
|
||||||
orderAmount: invoice.amt_paid_sat
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateUnverifiedTips = () => {
|
|
||||||
getUser()
|
|
||||||
.get(Key.TIPS_PAYMENT_STATUS)
|
|
||||||
.map()
|
|
||||||
.once(async (tip, id) => {
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
!tip ||
|
|
||||||
tip.state !== INVOICE_STATE.OPEN ||
|
|
||||||
(tip._errorCount && tip._errorCount >= ERROR_TRIES_THRESHOLD)
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Logger.info('Unverified invoice found!', tip)
|
|
||||||
const invoice = await _lookupInvoice(tip.hash)
|
|
||||||
Logger.info('Invoice located:', invoice)
|
|
||||||
if (invoice.state !== tip.state) {
|
|
||||||
await _updateTipData(id, { state: invoice.state })
|
|
||||||
|
|
||||||
// Actions to be executed when the tip's state is updated
|
|
||||||
executeTipAction(tip, invoice)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
Logger.error('[TIP] An error has occurred while updating invoice', err)
|
|
||||||
const errorCount = tip._errorCount ? tip._errorCount : 0
|
|
||||||
_updateTipData(id, {
|
|
||||||
_errorCount: errorCount + 1
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const startTipStatusJob = () => {
|
|
||||||
const { lightning } = LightningServices.services
|
|
||||||
const stream = lightning.subscribeInvoices({})
|
|
||||||
updateUnverifiedTips()
|
|
||||||
stream.on('data', async invoice => {
|
|
||||||
const hash = invoice.r_hash.toString('base64')
|
|
||||||
const tip = await _getTipData(hash)
|
|
||||||
if (tip.state !== invoice.state) {
|
|
||||||
await _updateTipData(hash, { state: invoice.state })
|
|
||||||
executeTipAction(tip, invoice)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
stream.on('error', err => {
|
|
||||||
Logger.error('Tip Job error' + err.details)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
startTipStatusJob
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue