Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
9b81cc6287
|
|||
|
947c7f7056
|
|||
| 9563cb9714 | |||
|
|
aca47a23e9 | ||
|
|
53361d2e3a | ||
|
|
ac4461c7f3 | ||
|
|
b7d106b499 | ||
| ac2061c6ed | |||
|
|
14478becae | ||
|
|
292107342e | ||
|
|
661248702d | ||
|
|
d5fb16b074 | ||
|
|
a184872b02 | ||
|
|
6047383747 | ||
|
|
2f2c105177 | ||
|
|
64adcfc279 | ||
|
|
3a9cefa64b | ||
|
|
e149f695cc | ||
|
|
c4a41fff48 | ||
|
|
688798024c | ||
|
|
66a54570c5 | ||
|
|
e42ef3a85e | ||
|
|
600556f84c | ||
|
|
a01a8c6e14 | ||
|
|
e9c6f3abde | ||
|
|
7f9463bbbc | ||
|
|
1b1fe8f08b | ||
|
|
28dd3a847a | ||
|
|
a28eead0ab | ||
|
|
87afa20019 | ||
|
|
067f6d6667 | ||
|
|
957d6c5951 | ||
|
|
d3df6aab05 | ||
|
|
4e8ce5e46f | ||
|
|
1fd24b640e | ||
|
|
c47211f984 | ||
|
|
5223edc2b7 | ||
|
|
92e626508c | ||
|
|
3e2c31f429 | ||
|
|
376d61f81d | ||
|
|
1bb781270c | ||
|
|
c0716bc6e9 | ||
|
|
358f858ad8 | ||
|
|
7a6e8a2940 | ||
|
|
96e82ad20d | ||
|
|
406afef7f7 | ||
|
|
3ebe1938ab | ||
|
|
ee2398f247 | ||
|
|
bc84429b61 | ||
|
|
ec3039d91d | ||
|
|
1483449529 | ||
|
|
e0f037ea95 | ||
|
|
b4f880b35d | ||
|
|
35fe816d0a | ||
|
|
4a4daccecd | ||
|
|
66b9d3ab92 | ||
|
|
87fc5d6ff7 | ||
|
|
e6b1f950f3 | ||
|
|
eab49fde2f | ||
|
|
aa11daf608 | ||
|
|
4e7731ff54 | ||
|
|
1c8232d1ec | ||
|
|
fd6caf859c | ||
|
|
df6412a525 | ||
|
|
c46c1385c6 | ||
|
|
dd5d96aa56 | ||
|
|
d6d563039e | ||
|
|
f5eb5d0c78 | ||
|
|
9d17ceb23f | ||
|
|
d0406afd31 | ||
|
|
6411e9bd28 | ||
|
|
db218ef440 | ||
|
|
735b65dc0f | ||
|
|
c73d762028 | ||
|
|
4f3dff6022 | ||
|
|
a792432700 | ||
|
|
590bccd4c0 | ||
|
|
d90881771c | ||
|
|
d9d75a0b29 | ||
|
|
34ffd59c1b | ||
|
|
7dce6b848e | ||
|
|
0d337bdc7b | ||
|
|
f0bf066cfd | ||
|
|
f0ad86f1b9 | ||
|
|
12d1e9560f | ||
|
|
1eaf6c5a47 | ||
|
|
dfcaac2cfa | ||
|
|
f11c02d2f2 | ||
|
|
a4eedde3fb | ||
|
|
597615209b | ||
|
|
5fc7b074f9 |
@@ -6,7 +6,7 @@ RUN adduser --disabled-password \
|
||||
--gecos "" \
|
||||
"lndhub"
|
||||
|
||||
FROM node:12-buster-slim AS builder
|
||||
FROM node:16-bullseye-slim AS builder
|
||||
|
||||
# These packages are required for building LNDHub
|
||||
RUN apt-get update && apt-get -y install python3
|
||||
@@ -25,7 +25,7 @@ COPY . .
|
||||
# Delete git data as it's not needed inside the container
|
||||
RUN rm -rf .git
|
||||
|
||||
FROM node:12-buster-slim
|
||||
FROM node:16-bullseye-slim
|
||||
|
||||
# Create a specific user so LNDHub doesn't run as root
|
||||
COPY --from=perms /etc/group /etc/passwd /etc/shadow /etc/
|
||||
|
||||
21
README.md
21
README.md
@@ -1,7 +1,7 @@
|
||||
LndHub
|
||||
======
|
||||
|
||||
Wrapper for Lightning Network Daemon. It provides separate accounts with minimum trust for end users
|
||||
Wrapper for Lightning Network Daemon (lnd). It provides separate accounts with minimum trust for end users.
|
||||
|
||||
INSTALLATION
|
||||
------------
|
||||
@@ -17,15 +17,17 @@ cd LndHub
|
||||
npm i
|
||||
```
|
||||
|
||||
Install `bitcoind`, `lnd` and `redis`. Edit `config.js` and set it up correctly.
|
||||
Copy `admin.macaroon` and `tls.cert` in root folder of LndHub.
|
||||
Install `bitcoind`, `lnd`, and `redis`. Edit LndHub's `config.js` to set it up correctly.
|
||||
Copy the files `admin.macaroon` (for Bitcoin mainnet, usually stored in `~/.lnd/data/chain/bitcoin/mainnet/admin.macaroon`)
|
||||
and `tls.cert` (usually stored in `~/.lnd/tls.cert`) into the root folder of LndHub.
|
||||
|
||||
`bitcoind` should run with `-deprecatedrpc=accounts`, for now. Lndhub expects Lnd's wallet to be unlocked, if not - it will attempt to unlock it with password stored in `config.lnd.password`.
|
||||
Don't forget to enable disk-persistance for `redis`.
|
||||
LndHub expects LND's wallet to be unlocked, if not — it will attempt to unlock it with the password stored in `config.lnd.password`.
|
||||
Don't forget to configure disk-persistence for `redis` (e.g., you may want to set `appendonly` to `yes` in `redis.conf` (see
|
||||
http://redis.io/topics/persistence for more information).
|
||||
|
||||
If you have no `bitcoind` instance, for example if you use neutrino, or you have no bitcoind wallet,
|
||||
for example if you use LND for wallet managment, you can remove the bitcoind settings from `config.js`.
|
||||
Please not that this feature is limited to Bitcoin, so you can't use this feature if you use any other cryptocurrency with LND.
|
||||
Please note that this feature is limited to Bitcoin, so you can't use it if you use any other cryptocurrency with LND (e.g., Litecoin).
|
||||
|
||||
### Deploy to Heroku
|
||||
|
||||
@@ -44,12 +46,13 @@ You can also view Umbrel's implementation using docker-compose [here](https://gi
|
||||
|
||||
Can be used in ReactNative or Nodejs environment
|
||||
|
||||
* https://github.com/BlueWallet/BlueWallet/blob/master/class/lightning-custodian-wallet.js
|
||||
* https://github.com/BlueWallet/BlueWallet/blob/master/class/wallets/lightning-custodian-wallet.js
|
||||
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
Acceptance tests are in https://github.com/BlueWallet/BlueWallet/blob/master/LightningCustodianWallet.test.js
|
||||
Acceptance tests are in https://github.com/BlueWallet/BlueWallet/blob/master/tests/integration/lightning-custodian-wallet.test.js
|
||||
|
||||

|
||||
|
||||
@@ -57,5 +60,5 @@ Acceptance tests are in https://github.com/BlueWallet/BlueWallet/blob/master/Lig
|
||||
|
||||
## Responsible disclosure
|
||||
|
||||
Found critical bugs/vulnerabilities? Please email them bluewallet@bluewallet.io
|
||||
Found critical bugs/vulnerabilities? Please email them to bluewallet@bluewallet.io
|
||||
Thanks!
|
||||
|
||||
@@ -12,10 +12,6 @@ export class Paym {
|
||||
this._isPaid = null;
|
||||
}
|
||||
|
||||
static get fee() {
|
||||
return 0.003;
|
||||
}
|
||||
|
||||
setInvoice(bolt11) {
|
||||
this._bolt11 = bolt11;
|
||||
}
|
||||
@@ -39,7 +35,7 @@ export class Paym {
|
||||
pub_key: this._decoded.destination,
|
||||
amt: this._decoded.num_satoshis,
|
||||
final_cltv_delta: 144,
|
||||
fee_limit: { fixed: Math.floor(this._decoded.num_satoshis * 0.01) + 1 },
|
||||
fee_limit: { fixed: Math.floor(this._decoded.num_satoshis * forwardFee) + 1 },
|
||||
};
|
||||
let that = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
@@ -74,7 +70,7 @@ export class Paym {
|
||||
if (payment && payment.payment_route && payment.payment_route.total_amt_msat) {
|
||||
// paid just now
|
||||
this._isPaid = true;
|
||||
payment.payment_route.total_fees = +payment.payment_route.total_fees + Math.floor(+payment.payment_route.total_amt * Paym.fee);
|
||||
payment.payment_route.total_fees = +payment.payment_route.total_fees + Math.floor(+payment.payment_route.total_amt * internalFee);
|
||||
if (this._bolt11) payment.pay_req = this._bolt11;
|
||||
if (this._decoded) payment.decoded = this._decoded;
|
||||
}
|
||||
@@ -87,7 +83,7 @@ export class Paym {
|
||||
if (this._bolt11) payment.pay_req = this._bolt11;
|
||||
// trying to guess the fee
|
||||
payment.payment_route = payment.payment_route || {};
|
||||
payment.payment_route.total_fees = Math.floor(this._decoded.num_satoshis * 0.01); // we dont know the exact fee, so we use max (same as fee_limit)
|
||||
payment.payment_route.total_fees = Math.floor(this._decoded.num_satoshis * forwardFee); // we dont know the exact fee, so we use max (same as fee_limit)
|
||||
payment.payment_route.total_amt = this._decoded.num_satoshis;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ export class User {
|
||||
let lockedPayments = await this.getLockedPayments();
|
||||
for (let paym of lockedPayments) {
|
||||
// locked payments are processed in scripts/process-locked-payments.js
|
||||
calculatedBalance -= +paym.amount + /* feelimit */ Math.floor(paym.amount * 0.01);
|
||||
calculatedBalance -= +paym.amount + /* feelimit */ Math.floor(paym.amount * forwardFee);
|
||||
}
|
||||
|
||||
return calculatedBalance;
|
||||
@@ -363,7 +363,7 @@ export class User {
|
||||
}
|
||||
if (invoice.decoded) {
|
||||
invoice.timestamp = invoice.decoded.timestamp;
|
||||
invoice.memo = invoice.decoded.description;
|
||||
invoice.memo = invoice.memo || invoice.decoded.description;
|
||||
}
|
||||
if (invoice.payment_preimage) {
|
||||
invoice.payment_preimage = Buffer.from(invoice.payment_preimage, 'hex').toString('hex');
|
||||
@@ -504,7 +504,7 @@ export class User {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async accountForPosibleTxids() {
|
||||
return; // TODO: remove
|
||||
// return; // TODO: remove
|
||||
let onchain_txs = await this.getTxs();
|
||||
let imported_txids = await this._redis.lrange('imported_txids_for_' + this._userid, 0, -1);
|
||||
for (let tx of onchain_txs) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
let config = {
|
||||
enableUpdateDescribeGraph: false,
|
||||
postRateLimit: 100,
|
||||
rateLimit: 200,
|
||||
forwardReserveFee: 0.01, // default 0.01
|
||||
intraHubFee: 0.003, // default 0.003
|
||||
bitcoind: {
|
||||
rpc: 'http://login:password@1.1.1.1:8332/wallet/wallet.dat',
|
||||
},
|
||||
|
||||
@@ -15,6 +15,12 @@ redis.monitor(function (err, monitor) {
|
||||
});
|
||||
});
|
||||
|
||||
/****** 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 ******/
|
||||
|
||||
let bitcoinclient = require('../bitcoin');
|
||||
let lightning = require('../lightning');
|
||||
let identity_pubkey = false;
|
||||
@@ -118,8 +124,10 @@ function updateDescribeGraph() {
|
||||
console.log('updated graph');
|
||||
});
|
||||
}
|
||||
updateDescribeGraph();
|
||||
setInterval(updateDescribeGraph, 120000);
|
||||
if (config.enableUpdateDescribeGraph) {
|
||||
updateDescribeGraph();
|
||||
setInterval(updateDescribeGraph, 120000);
|
||||
}
|
||||
|
||||
// ######################## ROUTES ########################
|
||||
|
||||
@@ -131,7 +139,13 @@ const postLimiter = rateLimit({
|
||||
|
||||
router.post('/create', postLimiter, async function (req, res) {
|
||||
logger.log('/create', [req.id]);
|
||||
if (!(req.body.partnerid && req.body.partnerid === 'bluewallet' && req.body.accounttype)) return errorBadArguments(res);
|
||||
// Valid if the partnerid isn't there or is a string (same with accounttype)
|
||||
if (! (
|
||||
(!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);
|
||||
await u.create();
|
||||
@@ -170,13 +184,20 @@ router.post('/addinvoice', postLimiter, async function (req, res) {
|
||||
|
||||
if (!req.body.amt || /*stupid NaN*/ !(req.body.amt > 0)) return errorBadArguments(res);
|
||||
|
||||
if (config.sunset) return errorSunsetAddInvoice(res);
|
||||
|
||||
const invoice = new Invo(redis, bitcoinclient, lightning);
|
||||
const r_preimage = invoice.makePreimageHex();
|
||||
const invoice_args = { memo: req.body.memo, value: req.body.amt, expiry: 3600 * 24, r_preimage: Buffer.from(r_preimage, 'hex').toString('base64') };
|
||||
if (req.body.description_hash) {
|
||||
invoice_args.description_hash = Buffer.from(req.body.description_hash, 'hex').toString('base64')
|
||||
}
|
||||
lightning.addInvoice(
|
||||
{ memo: req.body.memo, value: req.body.amt, expiry: 3600 * 24, r_preimage: Buffer.from(r_preimage, 'hex').toString('base64') },
|
||||
invoice_args,
|
||||
async function (err, info) {
|
||||
if (err) return errorLnd(res);
|
||||
|
||||
info.memo = req.body.memo;
|
||||
info.pay_req = info.payment_request; // client backwards compatibility
|
||||
await u.saveUserInvoice(info);
|
||||
await invoice.savePreimage(r_preimage);
|
||||
@@ -229,7 +250,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 * 0.01)) {
|
||||
if (userBalance >= +info.num_satoshis + Math.floor(info.num_satoshis * forwardFee)) {
|
||||
// got enough balance, including 1% of payment amount - reserve for fees
|
||||
|
||||
if (identity_pubkey === info.destination) {
|
||||
@@ -256,8 +277,8 @@ router.post('/payinvoice', async function (req, res) {
|
||||
await u.savePaidLndInvoice({
|
||||
timestamp: parseInt(+new Date() / 1000),
|
||||
type: 'paid_invoice',
|
||||
value: +info.num_satoshis + Math.floor(info.num_satoshis * Paym.fee),
|
||||
fee: Math.floor(info.num_satoshis * Paym.fee),
|
||||
value: +info.num_satoshis + Math.floor(info.num_satoshis * internalFee),
|
||||
fee: Math.floor(info.num_satoshis * internalFee),
|
||||
memo: decodeURIComponent(info.description),
|
||||
pay_req: req.body.invoice,
|
||||
});
|
||||
@@ -310,7 +331,7 @@ router.post('/payinvoice', async function (req, res) {
|
||||
let inv = {
|
||||
payment_request: req.body.invoice,
|
||||
amt: info.num_satoshis, // amt is used only for 'tip' invoices
|
||||
fee_limit: { fixed: Math.floor(info.num_satoshis * 0.005) + 1 },
|
||||
fee_limit: { fixed: Math.floor(info.num_satoshis * forwardFee) + 1 },
|
||||
};
|
||||
try {
|
||||
await u.lockFunds(req.body.invoice, info);
|
||||
@@ -335,6 +356,8 @@ router.get('/getbtc', async function (req, res) {
|
||||
return errorBadAuth(res);
|
||||
}
|
||||
|
||||
if (config.sunset) return errorSunsetAddInvoice(res);
|
||||
|
||||
let address = await u.getAddress();
|
||||
if (!address) {
|
||||
await u.generateAddress();
|
||||
@@ -411,8 +434,8 @@ router.get('/gettxs', async function (req, res) {
|
||||
for (let locked of lockedPayments) {
|
||||
txs.push({
|
||||
type: 'paid_invoice',
|
||||
fee: Math.floor(locked.amount * 0.01) /* feelimit */,
|
||||
value: locked.amount + Math.floor(locked.amount * 0.01) /* feelimit */,
|
||||
fee: Math.floor(locked.amount * forwardFee) /* feelimit */,
|
||||
value: locked.amount + Math.floor(locked.amount * forwardFee) /* feelimit */,
|
||||
timestamp: locked.timestamp,
|
||||
memo: 'Payment in transition',
|
||||
});
|
||||
@@ -492,6 +515,7 @@ router.get('/queryroutes/:source/:dest/:amt', async function (req, res) {
|
||||
|
||||
let request = {
|
||||
pub_key: req.params.dest,
|
||||
use_mission_control: true,
|
||||
amt: req.params.amt,
|
||||
source_pub_key: req.params.source,
|
||||
};
|
||||
@@ -581,3 +605,19 @@ function errorPaymentFailed(res) {
|
||||
message: 'Payment failed. Does the receiver have enough inbound capacity?',
|
||||
});
|
||||
}
|
||||
|
||||
function errorSunset(res) {
|
||||
return res.send({
|
||||
error: true,
|
||||
code: 11,
|
||||
message: 'This LNDHub instance is not accepting any more users',
|
||||
});
|
||||
}
|
||||
|
||||
function errorSunsetAddInvoice(res) {
|
||||
return res.send({
|
||||
error: true,
|
||||
code: 11,
|
||||
message: 'This LNDHub instance is scheduled to shut down. Withdraw any remaining funds',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ function updateLightning() {
|
||||
lightning.getInfo({}, function (err, info) {
|
||||
if (err) {
|
||||
console.error('lnd failure:', err);
|
||||
process.exit(4);
|
||||
return;
|
||||
}
|
||||
lightningGetInfo = info;
|
||||
@@ -22,6 +23,7 @@ function updateLightning() {
|
||||
lightning.listChannels({}, function (err, response) {
|
||||
if (err) {
|
||||
console.error('lnd failure:', err);
|
||||
process.exit(4);
|
||||
return;
|
||||
}
|
||||
console.log('updated');
|
||||
@@ -60,6 +62,7 @@ const pubkey2name = {
|
||||
'030c3f19d742ca294a55c00376b3b355c3c90d61c6b6b39554dbc7ac19b141c14f': 'bitrefill.com',
|
||||
'03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f': 'ACINQ',
|
||||
'03abf6f44c355dec0d5aa155bdbdd6e0c8fefe318eff402de65c6eb2e1be55dc3e': 'OpenNode',
|
||||
'028d98b9969fbed53784a36617eb489a59ab6dc9b9d77fcdca9ff55307cd98e3c4': 'OpenNode 2',
|
||||
'0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3': 'coingate.com',
|
||||
'0279c22ed7a068d10dc1a38ae66d2d6461e269226c60258c021b1ddcdfe4b00bc4': 'ln1.satoshilabs.com',
|
||||
'02c91d6aa51aa940608b497b6beebcb1aec05be3c47704b682b3889424679ca490': 'lnd-21.LNBIG.com',
|
||||
@@ -80,6 +83,10 @@ const pubkey2name = {
|
||||
'033d8656219478701227199cbd6f670335c8d408a92ae88b962c49d4dc0e83e025': 'bfx-lnd0',
|
||||
'03021c5f5f57322740e4ee6936452add19dc7ea7ccf90635f95119ab82a62ae268': 'lnd1.bluewallet.io',
|
||||
'037cc5f9f1da20ac0d60e83989729a204a33cc2d8e80438969fadf35c1c5f1233b': 'lnd2.bluewallet.io',
|
||||
'036b53093df5a932deac828cca6d663472dbc88322b05eec1d42b26ab9b16caa1c': 'okcoin',
|
||||
'038f8f113c580048d847d6949371726653e02b928196bad310e3eda39ff61723f6': 'magnetron',
|
||||
'03829249ef39746fd534a196510232df08b83db0967804ec71bf4120930864ff97': 'blokada.org',
|
||||
'02ce691b2e321954644514db708ba2a72769a6f9142ac63e65dd87964e9cf2add9': 'Satoshis.Games',
|
||||
};
|
||||
|
||||
router.get('/', function (req, res) {
|
||||
@@ -98,7 +105,8 @@ router.get('/qr', function (req, res) {
|
||||
if (process.env.TOR_URL) {
|
||||
host = process.env.TOR_URL;
|
||||
}
|
||||
const url = 'bluewallet:setlndhuburl?url=' + encodeURIComponent(req.protocol + '://' + host);
|
||||
const customPath = req.url.replace('/qr', '');
|
||||
const url = 'bluewallet:setlndhuburl?url=' + encodeURIComponent(req.protocol + '://' + host + customPath);
|
||||
var code = qr.image(url, { type: 'png' });
|
||||
res.setHeader('Content-type', 'image/png');
|
||||
code.pipe(res);
|
||||
|
||||
@@ -33,7 +33,7 @@ associated with corresponding user id.
|
||||
| Call | Method | Handler | Params | Return | Description |
|
||||
| ------------- | ------------- | ------------- | ------------- | ------------- | ------------- |
|
||||
| Create Account | POST | /create | {none} | JSON Auth Data | Create new user account and get credentials |
|
||||
| Authorize | POST | /auth | auth params (login/password of refresh_token) | JSON token data | Authorize user with Oauth. When user use refresh_token to auth, then this refresh_token not available for access once again. Use new refresh_token |
|
||||
| Authorize | POST | /auth | auth params (login/password or refresh_token) | JSON token data | Authorize user with Oauth. When user use refresh_token to auth, then this refresh_token not available for access once again. Use new refresh_token |
|
||||
| Get token | POST | /oauth2/token | user id, secret, grant_type and scope | token data | Get token data from user id, secret, grant_type and scope |
|
||||
| Get BTC Addr | GET | /getbtc | {none} | Text address | Get user's BTC address to top-up his account |
|
||||
| New BTC Addr | POST | /newbtc | {none} | Text address | Create new BTC address for user. Old addresses should remain valid, so if user accidentaly sends money to old address transaction will be assigned to his account |
|
||||
@@ -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
|
||||
|
||||
10
index.js
10
index.js
@@ -5,6 +5,7 @@ process.on('uncaughtException', function (err) {
|
||||
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
||||
let express = require('express');
|
||||
const helmet = require('helmet');
|
||||
let morgan = require('morgan');
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
let logger = require('./utils/logger');
|
||||
@@ -16,6 +17,8 @@ morgan.token('id', function getId(req) {
|
||||
|
||||
let app = express();
|
||||
app.enable('trust proxy');
|
||||
app.use(helmet.hsts());
|
||||
app.use(helmet.hidePoweredBy());
|
||||
|
||||
const rateLimit = require('express-rate-limit');
|
||||
const limiter = rateLimit({
|
||||
@@ -44,7 +47,10 @@ app.use('/static', express.static('static'));
|
||||
app.use(require('./controllers/api'));
|
||||
app.use(require('./controllers/website'));
|
||||
|
||||
let server = app.listen(process.env.PORT || 3000, function () {
|
||||
logger.log('BOOTING UP', 'Listening on port ' + (process.env.PORT || 3000));
|
||||
const bindHost = process.env.HOST || '0.0.0.0';
|
||||
const bindPort = process.env.PORT || 3000;
|
||||
|
||||
let server = app.listen(bindPort, bindHost, function () {
|
||||
logger.log('BOOTING UP', 'Listening on ' + bindHost + ':' + bindPort);
|
||||
});
|
||||
module.exports = server;
|
||||
|
||||
2092
package-lock.json
generated
2092
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
36
package.json
36
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lndhub",
|
||||
"version": "1.3.3",
|
||||
"version": "1.4.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@@ -12,29 +12,29 @@
|
||||
"author": "Igor Korsakov <overtorment@gmail.com>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/cli": "^7.13.0",
|
||||
"@babel/core": "^7.13.8",
|
||||
"@babel/eslint-parser": "^7.13.10",
|
||||
"@babel/node": "^7.13.0",
|
||||
"@babel/preset-env": "^7.13.9",
|
||||
"@babel/register": "^7.13.8",
|
||||
"@grpc/grpc-js": "^1.2.10",
|
||||
"@grpc/proto-loader": "^0.6.0",
|
||||
"@babel/cli": "^7.14.8",
|
||||
"@babel/core": "^7.15.0",
|
||||
"@babel/eslint-parser": "^7.14.2",
|
||||
"@babel/node": "^7.14.9",
|
||||
"@babel/preset-env": "^7.15.6",
|
||||
"@babel/register": "^7.14.5",
|
||||
"@grpc/grpc-js": "^1.3.7",
|
||||
"@grpc/proto-loader": "^0.6.5",
|
||||
"bignumber.js": "^9.0.1",
|
||||
"bitcoinjs-lib": "^5.2.0",
|
||||
"bolt11": "^1.3.1",
|
||||
"core-js": "^3.9.1",
|
||||
"eslint": "^7.20.0",
|
||||
"eslint-config-prettier": "^8.0.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"bolt11": "^1.3.2",
|
||||
"eslint": "^7.24.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"express": "^4.17.1",
|
||||
"express-rate-limit": "^5.2.6",
|
||||
"express-rate-limit": "^5.4.1",
|
||||
"frisbee": "^3.1.4",
|
||||
"ioredis": "^4.22.0",
|
||||
"jayson": "^3.4.4",
|
||||
"helmet": "^4.6.0",
|
||||
"ioredis": "^4.27.10",
|
||||
"jayson": "^3.6.4",
|
||||
"morgan": "^1.10.0",
|
||||
"mustache": "^4.1.0",
|
||||
"prettier": "^2.2.1",
|
||||
"prettier": "^2.3.0",
|
||||
"qr-image": "3.2.0",
|
||||
"request": "^2.88.2",
|
||||
"request-promise": "^4.2.6",
|
||||
|
||||
@@ -9,6 +9,11 @@ const important_channels = {
|
||||
uri: '03abf6f44c355dec0d5aa155bdbdd6e0c8fefe318eff402de65c6eb2e1be55dc3e@18.221.23.28:9735',
|
||||
wumbo: 1,
|
||||
},
|
||||
'028d98b9969fbed53784a36617eb489a59ab6dc9b9d77fcdca9ff55307cd98e3c4': {
|
||||
name: 'OpenNode 2',
|
||||
uri: '028d98b9969fbed53784a36617eb489a59ab6dc9b9d77fcdca9ff55307cd98e3c4@18.222.70.85:9735',
|
||||
wumbo: 1,
|
||||
},
|
||||
// '0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3': {
|
||||
// name: 'coingate.com',
|
||||
// uri: '0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3@3.124.63.44:9735',
|
||||
@@ -45,9 +50,9 @@ const important_channels = {
|
||||
uri: '02816caed43171d3c9854e3b0ab2cf0c42be086ff1bd4005acc2a5f7db70d83774@35.238.153.25:9735',
|
||||
wumbo: 1,
|
||||
},
|
||||
'02570432c30df86ff7dbe3b49e24e8280411674f96470154cadc76d447e339292b': {
|
||||
'036b53093df5a932deac828cca6d663472dbc88322b05eec1d42b26ab9b16caa1c': {
|
||||
name: 'okcoin',
|
||||
uri: '02570432c30df86ff7dbe3b49e24e8280411674f96470154cadc76d447e339292b@8.210.75.128:26657',
|
||||
uri: '036b53093df5a932deac828cca6d663472dbc88322b05eec1d42b26ab9b16caa1c@47.243.25.4:26658',
|
||||
wumbo: 1,
|
||||
},
|
||||
// '0331f80652fb840239df8dc99205792bba2e559a05469915804c08420230e23c7c': {
|
||||
@@ -78,7 +83,7 @@ lightning.listChannels({}, function (err, response) {
|
||||
}
|
||||
let lightningListChannels = response;
|
||||
for (let channel of lightningListChannels.channels) {
|
||||
if (channel.capacity < 0.05 / 100000000) {
|
||||
if (channel.capacity <= 1000000) {
|
||||
console.log(
|
||||
'lncli closechannel',
|
||||
channel.channel_point.replace(':', ' '),
|
||||
|
||||
@@ -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,8 +43,25 @@ let lightning = require('../lightning');
|
||||
|
||||
let payment = new Paym(redis, bitcoinclient, lightning);
|
||||
payment.setInvoice(lockedPayment.pay_req);
|
||||
|
||||
// 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
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// could not find...
|
||||
|
||||
if (daysPassed > 1 / 24 && daysPassed <= 1) {
|
||||
// if (!await payment.isExpired()) {
|
||||
let sendResult;
|
||||
console.log('attempting to pay to route');
|
||||
try {
|
||||
@@ -68,22 +90,7 @@ let lightning = require('../lightning');
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -50,9 +50,10 @@ let lightning = require('../lightning');
|
||||
|
||||
let locked = await U.getLockedPayments();
|
||||
for (let loc of locked) {
|
||||
console.log('-', loc.amount + /* fee limit */ Math.floor(loc.amount * 0.01), new Date(loc.timestamp * 1000).toString(), '[locked]');
|
||||
console.log('-', loc.amount + /* fee limit */ Math.floor(loc.amount * config.forwardReserveFee), new Date(loc.timestamp * 1000).toString(), '[locked]');
|
||||
}
|
||||
|
||||
console.log('\ncalculatedBalance\n================\n', calculatedBalance, await U.getCalculatedBalance());
|
||||
console.log('txs:', txs.length, 'userinvoices:', userinvoices.length);
|
||||
process.exit();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user