Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
819518eab0 | ||
|
|
0311e56aa2 | ||
|
|
0cf7387c30 | ||
|
|
edb2ab0949 | ||
|
|
625a6e9526 | ||
|
|
525e309105 | ||
|
|
0964ebfca5 | ||
|
|
c87f1b322a | ||
|
|
3a7b452c65 | ||
|
|
fb023b02be | ||
|
|
60cda5eb14 | ||
|
|
c6dc983964 | ||
|
|
481758f03a | ||
|
|
348ee7ce81 | ||
|
|
05eace759f | ||
|
|
aca47a23e9 | ||
|
|
53361d2e3a | ||
|
|
ac4461c7f3 | ||
|
|
b7d106b499 | ||
|
|
14478becae | ||
|
|
292107342e | ||
|
|
661248702d | ||
|
|
d5fb16b074 | ||
|
|
a184872b02 | ||
|
|
6047383747 | ||
|
|
2f2c105177 |
15
.dockerignore
Normal file
15
.dockerignore
Normal file
@ -0,0 +1,15 @@
|
||||
*
|
||||
!build/
|
||||
!class/
|
||||
!controllers/
|
||||
!scripts/
|
||||
!static/
|
||||
!templates/
|
||||
!utils/
|
||||
!*.js
|
||||
!.babelrc
|
||||
!.eslint*
|
||||
!admin.macaroon
|
||||
!package*.json
|
||||
!rpc.proto
|
||||
!tls.cert
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
|
||||
admin.macaroon
|
||||
tls.cert
|
||||
build/
|
||||
logs/
|
||||
|
||||
# dependencies
|
||||
|
||||
13
Dockerfile
13
Dockerfile
@ -13,19 +13,17 @@ RUN apt-get update && apt-get -y install python3
|
||||
|
||||
WORKDIR /lndhub
|
||||
|
||||
# Copy 'package-lock.json' and 'package.json'
|
||||
COPY package.json package-lock.json ./
|
||||
# Copy project files and folders to the current working directory
|
||||
COPY . .
|
||||
|
||||
# Install dependencies
|
||||
RUN npm i
|
||||
|
||||
# Copy project files and folders to the current working directory
|
||||
COPY . .
|
||||
RUN npm run dockerbuild
|
||||
|
||||
# Delete git data as it's not needed inside the container
|
||||
RUN rm -rf .git
|
||||
|
||||
FROM node:16-bullseye-slim
|
||||
FROM node:16-alpine
|
||||
|
||||
# Create a specific user so LNDHub doesn't run as root
|
||||
COPY --from=perms /etc/group /etc/passwd /etc/shadow /etc/
|
||||
@ -34,10 +32,11 @@ COPY --from=perms /etc/group /etc/passwd /etc/shadow /etc/
|
||||
COPY --from=builder /lndhub /lndhub
|
||||
|
||||
# Create logs folder and ensure permissions are set correctly
|
||||
RUN mkdir /lndhub/logs && chown -R lndhub:lndhub /lndhub
|
||||
RUN mkdir -p /lndhub/logs && chown -R lndhub:lndhub /lndhub
|
||||
USER lndhub
|
||||
|
||||
ENV PORT=3000
|
||||
EXPOSE 3000
|
||||
WORKDIR /lndhub
|
||||
|
||||
CMD cp $LND_CERT_FILE /lndhub/ && cp $LND_ADMIN_MACAROON_FILE /lndhub/ && cd /lndhub && npm start
|
||||
|
||||
@ -119,6 +119,12 @@ export class User {
|
||||
return new Promise(function (resolve, reject) {
|
||||
self._lightning.newAddress({ type: 0 }, async function (err, response) {
|
||||
if (err) return reject('LND failure when trying to generate new address');
|
||||
const addressAlreadyExists = await self.getAddress();
|
||||
if (addressAlreadyExists) {
|
||||
// one last final check, for a case of really long race condition
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
await self.addAddress(response.address);
|
||||
if (config.bitcoind) self._bitcoindrpc.request('importaddress', [response.address, response.address, false]);
|
||||
resolve();
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import { User, Lock, Paym, Invo } from '../class/';
|
||||
import Frisbee from 'frisbee';
|
||||
import fetch from 'node-fetch';
|
||||
const config = require('../config');
|
||||
let express = require('express');
|
||||
let router = express.Router();
|
||||
let logger = require('../utils/logger');
|
||||
const MIN_BTC_BLOCK = 670000;
|
||||
console.log('using config', JSON.stringify(config));
|
||||
if (process.env.NODE_ENV !== 'prod') {
|
||||
console.log('using config', JSON.stringify(config));
|
||||
}
|
||||
|
||||
var Redis = require('ioredis');
|
||||
var redis = new Redis(config.redis);
|
||||
@ -90,21 +92,16 @@ const subscribeInvoicesCallCallback = async function (response) {
|
||||
console.log('payment', LightningInvoiceSettledNotification.hash, 'was paid, posting to GroundControl...');
|
||||
const baseURI = process.env.GROUNDCONTROL;
|
||||
if (!baseURI) return;
|
||||
const _api = new Frisbee({ baseURI: baseURI });
|
||||
const apiResponse = await _api.post(
|
||||
'/lightningInvoiceGotSettled',
|
||||
Object.assign(
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: LightningInvoiceSettledNotification,
|
||||
},
|
||||
),
|
||||
);
|
||||
console.log('GroundControl:', apiResponse.originalResponse.status);
|
||||
|
||||
const apiResponse = await fetch(`${baseURI}/lightningInvoiceGotSettled`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(LightningInvoiceSettledNotification),
|
||||
});
|
||||
console.log('Groundcontrol apiResponse=', apiResponse);
|
||||
}
|
||||
};
|
||||
let subscribeInvoicesCall = lightning.subscribeInvoices({});
|
||||
@ -144,7 +141,7 @@ router.post('/create', postLimiter, async function (req, res) {
|
||||
(!req.body.partnerid || (typeof req.body.partnerid === 'string' || req.body.partnerid instanceof String))
|
||||
&& (!req.body.accounttype || (typeof req.body.accounttype === 'string' || req.body.accounttype instanceof String))
|
||||
) ) return errorBadArguments(res);
|
||||
|
||||
|
||||
if (config.sunset) return errorSunset(res);
|
||||
|
||||
let u = new User(redis, bitcoinclient, lightning);
|
||||
@ -202,7 +199,7 @@ router.post('/addinvoice', postLimiter, async function (req, res) {
|
||||
);
|
||||
});
|
||||
|
||||
router.post('/payinvoice', async function (req, res) {
|
||||
router.post('/payinvoice', postLimiter, async function (req, res) {
|
||||
let u = new User(redis, bitcoinclient, lightning);
|
||||
if (!(await u.loadByAuthorization(req.headers.authorization))) {
|
||||
return errorBadAuth(res);
|
||||
@ -245,7 +242,7 @@ router.post('/payinvoice', async function (req, res) {
|
||||
|
||||
logger.log('/payinvoice', [req.id, 'userBalance: ' + userBalance, 'num_satoshis: ' + info.num_satoshis]);
|
||||
|
||||
if (userBalance >= +info.num_satoshis + Math.floor(info.num_satoshis * forwardFee)) {
|
||||
if (userBalance >= +info.num_satoshis + Math.floor(info.num_satoshis * forwardFee) + 1) {
|
||||
// got enough balance, including 1% of payment amount - reserve for fees
|
||||
|
||||
if (identity_pubkey === info.destination) {
|
||||
@ -413,7 +410,7 @@ router.get('/getinfo', postLimiter, async function (req, res) {
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/gettxs', async function (req, res) {
|
||||
router.get('/gettxs', postLimiter, async function (req, res) {
|
||||
logger.log('/gettxs', [req.id]);
|
||||
let u = new User(redis, bitcoinclient, lightning);
|
||||
if (!(await u.loadByAuthorization(req.headers.authorization))) {
|
||||
@ -459,7 +456,7 @@ router.get('/getuserinvoices', postLimiter, async function (req, res) {
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/getpending', async function (req, res) {
|
||||
router.get('/getpending', postLimiter, async function (req, res) {
|
||||
logger.log('/getpending', [req.id]);
|
||||
let u = new User(redis, bitcoinclient, lightning);
|
||||
if (!(await u.loadByAuthorization(req.headers.authorization))) {
|
||||
@ -473,7 +470,7 @@ router.get('/getpending', async function (req, res) {
|
||||
res.send(txs);
|
||||
});
|
||||
|
||||
router.get('/decodeinvoice', async function (req, res) {
|
||||
router.get('/decodeinvoice', postLimiter, async function (req, res) {
|
||||
logger.log('/decodeinvoice', [req.id]);
|
||||
let u = new User(redis, bitcoinclient, lightning);
|
||||
if (!(await u.loadByAuthorization(req.headers.authorization))) {
|
||||
|
||||
@ -70,9 +70,9 @@ Response is always JSON.
|
||||
`error:true` should be always present.
|
||||
|
||||
{
|
||||
"error" : true, // boolean
|
||||
"code" : 1, // int
|
||||
"message": "..." // string
|
||||
"error" : true, // boolean
|
||||
"code" : 1, // int
|
||||
"message": "..." // string
|
||||
}
|
||||
|
||||
Error code | Error message
|
||||
@ -95,15 +95,15 @@ Create new user account and get credentials. Not whitelisted partners should ret
|
||||
Request:
|
||||
|
||||
{
|
||||
"partnerid" : "bluewallet" // string, not mandatory parameter
|
||||
"accounttype" : "..." // string, not mandatory, default is common, also can be test or core
|
||||
"partnerid" : "bluewallet" // string, not mandatory parameter
|
||||
"accounttype" : "..." // string, not mandatory, default is common, also can be test or core
|
||||
}
|
||||
|
||||
Response:
|
||||
|
||||
{
|
||||
"login":"...", // srting
|
||||
"password":"...", // srting
|
||||
"login":"...", // string
|
||||
"password":"...", // string
|
||||
}
|
||||
|
||||
## POST /auth?type=auth
|
||||
@ -113,16 +113,16 @@ Authorize user with Oauth user and login
|
||||
Request:
|
||||
|
||||
{
|
||||
"login": "...", //string
|
||||
"password": "..." //string
|
||||
"login": "...", // string
|
||||
"password": "..." // string
|
||||
}
|
||||
|
||||
Response:
|
||||
|
||||
{
|
||||
"access_token": "...", //string
|
||||
"token_type": "...", //string
|
||||
"refresh_token": "...", //string
|
||||
"access_token": "...", // string
|
||||
"token_type": "...", // string
|
||||
"refresh_token": "...", // string
|
||||
"expiry": "0001-01-01T00:00:00Z" // datetime
|
||||
}
|
||||
|
||||
@ -135,16 +135,16 @@ Authorize user with Oauth user and login
|
||||
Request:
|
||||
|
||||
{
|
||||
"refresh_token": "...", //string
|
||||
"refresh_token": "...", // string
|
||||
}
|
||||
|
||||
Response:
|
||||
|
||||
{
|
||||
"access_token": "...", //string
|
||||
"token_type": "...", //string
|
||||
"refresh_token": "...", //string
|
||||
"expiry": "0001-01-01T00:00:00Z" // datetime
|
||||
"access_token": "...", // string
|
||||
"token_type": "...", // string
|
||||
"refresh_token": "...", // string
|
||||
"expiry": "0001-01-01T00:00:00Z" // datetime
|
||||
}
|
||||
|
||||
## POST /oauth2/token
|
||||
@ -154,17 +154,17 @@ Authorize user with Oauth user and login
|
||||
Request:
|
||||
|
||||
{
|
||||
"grant_type": "client_credentials", //string
|
||||
"client_id": "...", //string
|
||||
"grant_type": "client_credentials", // string
|
||||
"client_id": "...", // string
|
||||
"client_secret": "..." // string
|
||||
}
|
||||
|
||||
Response:
|
||||
|
||||
{
|
||||
"access_token": "...", //string
|
||||
"token_type": "...", //string
|
||||
"refresh_token": "...", //string
|
||||
"access_token": "...", // string
|
||||
"token_type": "...", // string
|
||||
"refresh_token": "...", // string
|
||||
"expiry": "0001-01-01T00:00:00Z" // datetime
|
||||
}
|
||||
|
||||
@ -229,34 +229,34 @@ Request:
|
||||
Response:
|
||||
|
||||
{
|
||||
"destination": "...", //string, lnd node address
|
||||
"payment_hash": "...", //string
|
||||
"num_satoshis": "78497", //string, satoshis
|
||||
"timestamp": "1534430501", //string, unixtime
|
||||
"expiry": "3600", //string, seconds
|
||||
"description": "...", //string
|
||||
"description_hash": "", //string
|
||||
"fallback_addr": "...", //string, fallback on-chain address
|
||||
"cltv_expiry": "...", //string, delta to use for the time-lock of the CLTV extended to the final hop
|
||||
"destination": "...", // string, lnd node address
|
||||
"payment_hash": "...", // string
|
||||
"num_satoshis": "78497", // string, satoshis
|
||||
"timestamp": "1534430501", // string, unixtime
|
||||
"expiry": "3600", // string, seconds
|
||||
"description": "...", // string
|
||||
"description_hash": "", // string
|
||||
"fallback_addr": "...", // string, fallback on-chain address
|
||||
"cltv_expiry": "...", // string, delta to use for the time-lock of the CLTV extended to the final hop
|
||||
"route_hints": [
|
||||
{
|
||||
"hop_hints" : [
|
||||
{
|
||||
"node_id": "..", //string, the public key of the node at the start of the
|
||||
"node_id": "..", // string, the public key of the node at the start of the
|
||||
// channel.
|
||||
|
||||
"chan_id": ..., //int, the unique identifier of the channel.
|
||||
"chan_id": ..., // int, the unique identifier of the channel.
|
||||
|
||||
"fee_base_msat": ..., //int, The base fee of the channel denominated in
|
||||
"fee_base_msat": ..., // int, The base fee of the channel denominated in
|
||||
// millisatoshis.
|
||||
|
||||
"fee_proportional_millionths": ...,
|
||||
//int, the fee rate of the channel
|
||||
// int, the fee rate of the channel
|
||||
// for sending one satoshi across it denominated
|
||||
// in millionths of a satoshi
|
||||
|
||||
"cltv_expiry_delta": ...
|
||||
//int, the fee rate of the channel for sending one satoshi
|
||||
// int, the fee rate of the channel for sending one satoshi
|
||||
// across it denominated in millionths of a satoshi
|
||||
}, ...
|
||||
]
|
||||
@ -288,7 +288,7 @@ Request:
|
||||
|
||||
{
|
||||
"destination" : "..." // string, destination lnd node address
|
||||
"amt": "..." // string,
|
||||
"amt": "..." // string,
|
||||
}
|
||||
|
||||
Response:
|
||||
@ -311,26 +311,26 @@ Request:
|
||||
Response:
|
||||
|
||||
{
|
||||
"payment_error": "..." //string
|
||||
"payment_preimage": "..." //string
|
||||
"payment_route": {
|
||||
"total_time_lock": ... , //int
|
||||
"total_fees": ... , //int
|
||||
"total_amt": ... , //int
|
||||
"total_fees_msat": ... , //int
|
||||
"total_amt_msat": ... , //int
|
||||
"hops": [
|
||||
{
|
||||
"chan_id": ... , //int
|
||||
"chan_capacity": ... , //int
|
||||
"amt_to_forward": ... , //int
|
||||
"fee": ... , //int
|
||||
"expiry": ... , //int
|
||||
"amt_to_forward_msat": ... , //int
|
||||
"fee_msat": ... , //int
|
||||
},
|
||||
]
|
||||
}
|
||||
"payment_error": "..." // string
|
||||
"payment_preimage": "..." // string
|
||||
"payment_route": {
|
||||
"total_time_lock": ... , // int
|
||||
"total_fees": ... , // int
|
||||
"total_amt": ... , // int
|
||||
"total_fees_msat": ... , // int
|
||||
"total_amt_msat": ... , // int
|
||||
"hops": [
|
||||
{
|
||||
"chan_id": ... , // int
|
||||
"chan_capacity": ... , // int
|
||||
"amt_to_forward": ... , // int
|
||||
"fee": ... , // int
|
||||
"expiry": ... , // int
|
||||
"amt_to_forward_msat": ... , // int
|
||||
"fee_msat": ... , // int
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
## POST /sendcoins
|
||||
@ -356,14 +356,14 @@ Get successful lightning and btc transactions user made. Order newest to oldest.
|
||||
Request:
|
||||
|
||||
{
|
||||
"limit" : 10, // INT
|
||||
"offset": 0, // INT
|
||||
"limit" : 10, // INT
|
||||
"offset": 0, // INT
|
||||
}
|
||||
|
||||
Response:
|
||||
|
||||
{
|
||||
[ // array of Transaction object (see below)
|
||||
[ // array of Transaction object (see below)
|
||||
{
|
||||
...
|
||||
}
|
||||
@ -398,10 +398,10 @@ Request:
|
||||
Response:
|
||||
|
||||
{
|
||||
"BTC": { //string, currency
|
||||
"TotalBalance": 109388, //int, satoshis
|
||||
"BTC": { // string, currency
|
||||
"TotalBalance": 109388, // int, satoshis
|
||||
"AvailableBalance": 109388, // int, satoshis
|
||||
"UncomfirmedBalance": 0 //int, satoshis
|
||||
"UncomfirmedBalance": 0 // int, satoshis
|
||||
}, ...
|
||||
//now available only btc balance
|
||||
|
||||
@ -422,50 +422,52 @@ Response:
|
||||
"fee": 0, // int, in cents of percent, i.e. 100 for 1%, 50 for 0.5%, 1 for 0.01%
|
||||
|
||||
|
||||
"identity_pubkey": "...", //string, lnd node identity pubkey
|
||||
"alias": "...", //string, lnd node alias
|
||||
"num_pending_channels": 0, //int
|
||||
"num_active_channels": 3, //int
|
||||
"num_peers": 6, //int
|
||||
"block_height": 542389, //int
|
||||
"block_hash": "...", //string
|
||||
"synced_to_chain": true, //bool
|
||||
"testnet": false,
|
||||
"chains": [
|
||||
"bitcoin" //string, available chans to operate by lnd
|
||||
],
|
||||
"uris": [
|
||||
"...", //string, uris of lnd node
|
||||
],
|
||||
"best_header_timestamp": "...", //string, unixtime
|
||||
"version": "..." // string, lnd version
|
||||
"identity_pubkey": "...", // string, lnd node identity pubkey
|
||||
"alias": "...", // string, lnd node alias
|
||||
"num_pending_channels": 0, // int
|
||||
"num_active_channels": 3, // int
|
||||
"num_peers": 6, // int
|
||||
"block_height": 542389, // int
|
||||
"block_hash": "...", // string
|
||||
"synced_to_chain": true, // bool
|
||||
"testnet": false,
|
||||
"chains": [
|
||||
"bitcoin" // string, available chans to operate by lnd
|
||||
],
|
||||
"uris": [
|
||||
"...", // string, uris of lnd node
|
||||
],
|
||||
"best_header_timestamp": "...", // string, unixtime
|
||||
"version": "..." // string, lnd version
|
||||
}
|
||||
|
||||
## GET /getaddinvoice
|
||||
## GET /getinvoice
|
||||
|
||||
Returns fees user pays for payments, status of the system, etc.
|
||||
|
||||
Request:
|
||||
|
||||
{
|
||||
"amt": "...", //string
|
||||
"memo":"...", //string
|
||||
"receipt":"...", //string, not mandatory parameter
|
||||
"preimage": "...", //string, not mandatory parameter
|
||||
"fallbackAddr": "...", //string, not mandatory parameter
|
||||
"expiry": "...", //string, not mandatory parameter
|
||||
"private": "..." //string, not mandatory parameter
|
||||
"amt": "...", // string
|
||||
"memo":"...", // string
|
||||
"receipt":"...", // string, not mandatory parameter
|
||||
"preimage": "...", // string, not mandatory parameter
|
||||
"fallbackAddr": "...", // string, not mandatory parameter
|
||||
"expiry": "...", // string, not mandatory parameter
|
||||
"private": "..." // string, not mandatory parameter
|
||||
}
|
||||
|
||||
Response:
|
||||
|
||||
{
|
||||
"r_hash": "...", //string,
|
||||
"pay_req": "...", //string, a bare-bones invoice for a payment within the Lightning Network
|
||||
"add_index": ... //int, The “add” index of this invoice. Each newly created invoice will
|
||||
"r_hash": "...", // string,
|
||||
"pay_req": "...", // string, a bare-bones invoice for a payment within the Lightning Network
|
||||
"add_index": ... // int, The “add” index of this invoice. Each newly created invoice will
|
||||
// increment this index making it monotonically increasing.
|
||||
// Callers to the SubscribeInvoices call can use this to instantly
|
||||
// get notified of all added invoices with an add_index greater than this one.
|
||||
}
|
||||
|
||||
## GET /getuserinvoices
|
||||
|
||||
Returns fees user pays for payments, status of the system, etc.
|
||||
@ -475,33 +477,35 @@ Request:
|
||||
none
|
||||
|
||||
Response:
|
||||
{
|
||||
"r_hash": "...", //string
|
||||
"payment_request": "...", //string
|
||||
"add_index": "...", //string
|
||||
"description": "...", //string
|
||||
"amt": ... , //int
|
||||
"ispaid": ... //bool
|
||||
}
|
||||
|
||||
{
|
||||
"r_hash": "...", // string
|
||||
"payment_request": "...", // string
|
||||
"add_index": "...", // string
|
||||
"description": "...", // string
|
||||
"amt": ... , // int
|
||||
"ispaid": ... // bool
|
||||
}
|
||||
|
||||
# Data structures
|
||||
|
||||
## Transaction object
|
||||
|
||||
{
|
||||
"type": "...", // string, type of txs. Types:
|
||||
// bitcoind_internal_tx - moves to user btc address or account
|
||||
// bitcoind_tx - received by address or account
|
||||
// paid_invoice - user paid someone's invoice
|
||||
// sent_coins - user sent coins by lnd to someone's btc account
|
||||
// received_invoice_payments - user received payments by invoice
|
||||
"txid": "...", // string, internal tx id. not related to onchain transaction id
|
||||
"amt": 666, // satoshi, int
|
||||
"fee": 11, // satoshi, int
|
||||
"timestamp": 1234567, // int, unixtime
|
||||
"from": "...", // string
|
||||
"to": "...", // string
|
||||
"description": "...", // string, user-defined text
|
||||
"invoice": "...", // string, original bolt11-format invoice
|
||||
// bitcoind_internal_tx - moves to user btc address or account
|
||||
// bitcoind_tx - received by address or account
|
||||
// paid_invoice - user paid someone's invoice
|
||||
// sent_coins - user sent coins by lnd to someone's btc account
|
||||
// received_invoice_payments - user received payments by invoice
|
||||
"txid": "...", // string, internal tx id. not related to onchain transaction id
|
||||
"amt": 666, // satoshi, int
|
||||
"fee": 11, // satoshi, int
|
||||
"timestamp": 1234567, // int, unixtime
|
||||
"from": "...", // string
|
||||
"to": "...", // string
|
||||
"description": "...", // string, user-defined text
|
||||
"invoice": "...", // string, original bolt11-format invoice
|
||||
}
|
||||
|
||||
# Explaining oauth2 mechanism
|
||||
|
||||
1
index.js
1
index.js
@ -52,5 +52,6 @@ const bindPort = process.env.PORT || 3000;
|
||||
|
||||
let server = app.listen(bindPort, bindHost, function () {
|
||||
logger.log('BOOTING UP', 'Listening on ' + bindHost + ':' + bindPort);
|
||||
logger.log('using GroundControl', process.env.GROUNDCONTROL);
|
||||
});
|
||||
module.exports = server;
|
||||
|
||||
7392
package-lock.json
generated
7392
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "lndhub",
|
||||
"version": "1.4.1",
|
||||
"version": "1.4.3",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dockerbuild": "./node_modules/.bin/babel ./ --ignore node_modules/ --ignore *.spec.js --out-dir ./build",
|
||||
"dev": "nodemon node_modules/.bin/babel-node index.js",
|
||||
"start": "node_modules/.bin/babel-node index.js",
|
||||
"lint": "./node_modules/.bin/eslint ./ controllers/ class/ --fix"
|
||||
@ -16,10 +17,10 @@
|
||||
"@babel/core": "^7.15.0",
|
||||
"@babel/eslint-parser": "^7.14.2",
|
||||
"@babel/node": "^7.14.9",
|
||||
"@babel/preset-env": "^7.15.0",
|
||||
"@babel/preset-env": "^7.22.0",
|
||||
"@babel/register": "^7.14.5",
|
||||
"@grpc/grpc-js": "^1.3.7",
|
||||
"@grpc/proto-loader": "^0.6.4",
|
||||
"@grpc/proto-loader": "^0.6.5",
|
||||
"bignumber.js": "^9.0.1",
|
||||
"bitcoinjs-lib": "^5.2.0",
|
||||
"bolt11": "^1.3.2",
|
||||
@ -27,13 +28,13 @@
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"express": "^4.17.1",
|
||||
"express-rate-limit": "^5.3.0",
|
||||
"frisbee": "^3.1.4",
|
||||
"express-rate-limit": "^5.4.1",
|
||||
"helmet": "^4.6.0",
|
||||
"ioredis": "^4.27.8",
|
||||
"ioredis": "^4.27.10",
|
||||
"jayson": "^3.6.4",
|
||||
"morgan": "^1.10.0",
|
||||
"mustache": "^4.1.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"prettier": "^2.3.0",
|
||||
"qr-image": "3.2.0",
|
||||
"request": "^2.88.2",
|
||||
|
||||
@ -3,10 +3,15 @@
|
||||
* sentout payments from LND. If locked payment is in there we moe locked payment to array of real payments for the user
|
||||
* (it is effectively spent coins by user), if not - we attempt to pay it again (if it is not too old).
|
||||
*/
|
||||
import { User, Lock, Paym } from '../class/';
|
||||
import { User, Paym } from '../class/';
|
||||
const config = require('../config');
|
||||
|
||||
const fs = require('fs');
|
||||
/****** START SET FEES FROM CONFIG AT STARTUP ******/
|
||||
/** GLOBALS */
|
||||
global.forwardFee = config.forwardReserveFee || 0.01;
|
||||
global.internalFee = config.intraHubFee || 0.003;
|
||||
/****** END SET FEES FROM CONFIG AT STARTUP ******/
|
||||
|
||||
var Redis = require('ioredis');
|
||||
var redis = new Redis(config.redis);
|
||||
|
||||
@ -20,8 +25,8 @@ let lightning = require('../lightning');
|
||||
console.log('fetching listPayments...');
|
||||
let tempPaym = new Paym(redis, bitcoinclient, lightning);
|
||||
let listPayments = await tempPaym.listPayments();
|
||||
// DEBUG let listPayments = JSON.parse(fs.readFileSync('listpayments.txt').toString('ascii'));
|
||||
console.log('done', 'got', listPayments['payments'].length, 'payments');
|
||||
fs.writeFileSync('listPayments.json', JSON.stringify(listPayments['payments'], null, 2));
|
||||
|
||||
for (let key of keys) {
|
||||
const userid = key.replace('locked_payments_for_', '');
|
||||
@ -30,7 +35,7 @@ let lightning = require('../lightning');
|
||||
let user = new User(redis, bitcoinclient, lightning);
|
||||
user._userid = userid;
|
||||
let lockedPayments = await user.getLockedPayments();
|
||||
// lockedPayments = [{pay_req : 'lnbc2m1pwgd4tdpp5vjz80mm8murdkskrnre6w4kphzy3d6gap5jyffr93u02ruaj0wtsdq2xgcrqvpsxqcqzysk34zva4h9ce9jdf08nfdm2sh2ek4y4hjse8ww9jputneltjl24krkv50sene4jh0wpull6ujgrg632u2qt3lkva74vpkqr5e5tuuljspasqfhx'}];
|
||||
// DEBUG let lockedPayments = [{ pay_req : 'lnbc108130n1pshdaeupp58kw9djt9vcdx26wkdxl07tgncdmxz2w7s9hzul45tf8gfplme94sdqqcqzzgxqrrssrzjqw8c7yfutqqy3kz8662fxutjvef7q2ujsxtt45csu0k688lkzu3ld93gutl3k6wauyqqqqryqqqqthqqpysp5jcmk82hypuud0lhpf66dg3w5ta6aumc4w9g9sxljazglq9wkwstq9qypqsqnw8hwwauvzrala3g4yrkgazk2l2fh582j9ytz7le46gmsgglvmrknx842ej9z4c63en5866l8tpevm8cwul8g94kf2nepppn256unucp43jnsw', amount: 10813, timestamp: 1635186606 }];
|
||||
|
||||
for (let lockedPayment of lockedPayments) {
|
||||
let daysPassed = (+new Date() / 1000 - lockedPayment.timestamp) / 3600 / 24;
|
||||
@ -38,52 +43,33 @@ let lightning = require('../lightning');
|
||||
|
||||
let payment = new Paym(redis, bitcoinclient, lightning);
|
||||
payment.setInvoice(lockedPayment.pay_req);
|
||||
if (daysPassed > 1 / 24 && daysPassed <= 1) {
|
||||
// if (!await payment.isExpired()) {
|
||||
let sendResult;
|
||||
console.log('attempting to pay to route');
|
||||
try {
|
||||
sendResult = await payment.attemptPayToRoute();
|
||||
} catch (_) {
|
||||
console.log(_);
|
||||
console.log('evict lock');
|
||||
await user.unlockFunds(lockedPayment.pay_req);
|
||||
continue;
|
||||
}
|
||||
console.log('sendResult=', sendResult);
|
||||
console.log('payment.getIsPaid() = ', payment.getIsPaid());
|
||||
if (payment.getIsPaid() === true) {
|
||||
console.log('paid successfully');
|
||||
sendResult = payment.processSendPaymentResponse(sendResult); // adds fees
|
||||
|
||||
// first things first:
|
||||
// trying to lookup this stuck payment in an array of delivered payments
|
||||
let isPaid = false;
|
||||
for (let sentPayment of listPayments['payments']) {
|
||||
if ((await payment.getPaymentHash()) == sentPayment.payment_hash) {
|
||||
console.log('found this payment in listPayments array, so it is paid successfully');
|
||||
let sendResult = payment.processSendPaymentResponse({ payment_error: 'already paid' } /* hacky */); // adds fees
|
||||
if (sendResult.decoded.num_satoshis == 0) {
|
||||
// zero sat invoice, get corect sat number from the lockedPayment info
|
||||
sendResult.decoded.num_satoshis = lockedPayment.amount + Math.ceil(lockedPayment.amount * forwardFee);
|
||||
sendResult.decoded.num_msat = sendResult.decoded.num_satoshis * 1000;
|
||||
sendResult.payment_route.total_fees = 0;
|
||||
sendResult.payment_route.total_amt = sendResult.decoded.num_satoshis;
|
||||
}
|
||||
console.log('saving paid invoice:', sendResult);
|
||||
await user.savePaidLndInvoice(sendResult);
|
||||
await user.unlockFunds(lockedPayment.pay_req);
|
||||
} else if (payment.getIsPaid() === false) {
|
||||
console.log('not paid, just evict the lock');
|
||||
await user.unlockFunds(lockedPayment.pay_req);
|
||||
} else {
|
||||
console.log('payment is in unknown state');
|
||||
}
|
||||
console.log('sleeping 5 sec...');
|
||||
console.log('-----------------------------------------------------------------------------------');
|
||||
await User._sleep(0);
|
||||
} else if (daysPassed > 1) {
|
||||
// trying to lookup this stuck payment in an array of delivered payments
|
||||
let isPaid = false;
|
||||
for (let sentPayment of listPayments['payments']) {
|
||||
if ((await payment.getPaymentHash()) == sentPayment.payment_hash) {
|
||||
console.log('found this payment in listPayments array, so it is paid successfully');
|
||||
let sendResult = payment.processSendPaymentResponse({ payment_error: 'already paid' } /* hacky */); // adds fees
|
||||
console.log('saving paid invoice:', sendResult);
|
||||
await user.savePaidLndInvoice(sendResult);
|
||||
await user.unlockFunds(lockedPayment.pay_req);
|
||||
isPaid = true;
|
||||
console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!', await payment.getPaymentHash(), sentPayment.payment_hash);
|
||||
process.exit();
|
||||
break;
|
||||
}
|
||||
isPaid = true;
|
||||
console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!', await payment.getPaymentHash(), sentPayment.payment_hash);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// could not find...
|
||||
|
||||
if (daysPassed > 1) {
|
||||
// could not find in listpayments array; too late to retry
|
||||
if (!isPaid) {
|
||||
console.log('very old payment, evict the lock');
|
||||
await user.unlockFunds(lockedPayment.pay_req);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user