Umbrel support (#141)

* Don't require BTC core connection
* Docker: Don't clone, but use local repo
* Add GitHub workflows
* Allow to overwrite hostname
* chore: update dependencies
* Get tor URL from env
This commit is contained in:
Aaron Dewes 2021-02-24 16:17:22 +01:00 committed by GitHub
parent 76b289b652
commit f0493d595f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 8369 additions and 1001 deletions

View File

@ -3,7 +3,7 @@
"plugins": [ "plugins": [
"prettier" "prettier"
], ],
"extends": ["prettier"], "extends": ["plugin:prettier/recommended"],
"rules": { "rules": {
"prettier/prettier": [ "prettier/prettier": [
"warn", "warn",

54
.github/workflows/push.yml vendored Normal file
View File

@ -0,0 +1,54 @@
name: Build on push
on:
push:
branches:
- master
env:
DOCKER_CLI_EXPERIMENTAL: enabled
jobs:
build:
name: Build image
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
steps:
- name: Checkout project
uses: actions/checkout@v2
- name: Set env variables
run: echo "BRANCH=$(echo ${GITHUB_REF#refs/heads/} | sed 's/\//-/g')" >> $GITHUB_ENV
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
id: qemu
- name: Setup Docker buildx action
uses: docker/setup-buildx-action@v1
id: buildx
- name: Show available Docker buildx platforms
run: echo ${{ steps.buildx.outputs.platforms }}
- name: Run Docker buildx
run: |
docker buildx build \
--cache-from "type=local,src=/tmp/.buildx-cache" \
--cache-to "type=local,dest=/tmp/.buildx-cache" \
--platform ${{matrix.platform}} \
--tag ${{ secrets.DOCKER_CONTAINER_USERNAME }}/lndhub:$BRANCH \
--output "type=registry" ./

62
.github/workflows/tag.yml vendored Normal file
View File

@ -0,0 +1,62 @@
name: Build on push
on:
push:
tags:
- v[0-9]+.[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+-*
env:
DOCKER_CLI_EXPERIMENTAL: enabled
jobs:
build:
name: Build image
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
steps:
- name: Checkout project
uses: actions/checkout@v2
- name: Set env variables
run: echo "TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
id: qemu
- name: Setup Docker buildx action
uses: docker/setup-buildx-action@v1
id: buildx
- name: Show available Docker buildx platforms
run: echo ${{ steps.buildx.outputs.platforms }}
- name: Run Docker buildx
run: |
docker buildx build \
--cache-from "type=local,src=/tmp/.buildx-cache" \
--cache-to "type=local,dest=/tmp/.buildx-cache" \
--platform ${{matrix.platform}} \
--tag ${{ secrets.DOCKER_CONTAINER_USERNAME }}/lndhub:$TAG \
--output "type=registry" ./
- name: Run Docker buildx
run: |
docker buildx build \
--cache-from "type=local,src=/tmp/.buildx-cache" \
--cache-to "type=local,dest=/tmp/.buildx-cache" \
--platform ${{matrix.platform}} \
--tag ${{ secrets.DOCKER_CONTAINER_USERNAME }}/lndhub:latest \
--output "type=registry" ./

View File

@ -6,19 +6,26 @@ RUN adduser --disabled-password \
--gecos "" \ --gecos "" \
"lndhub" "lndhub"
FROM node:buster-slim AS builder FROM node:12-buster-slim AS builder
# These packages are required for building LNDHub # These packages are required for building LNDHub
RUN apt-get update && apt-get -y install git python3 RUN apt-get update && apt-get -y install python3
# TODO: Switch to official images once my PR is merged
RUN git clone https://github.com/AaronDewes/LndHub.git -b update-dependencies /lndhub
WORKDIR /lndhub WORKDIR /lndhub
# Copy 'package-lock.json' and 'package.json'
COPY package.json package-lock.json ./
# Install dependencies
RUN npm i RUN npm i
FROM node:buster-slim # Copy project files and folders to the current working directory
COPY . .
# Delete git data as it's not needed inside the container
RUN rm -rf .git
FROM node:12-buster-slim
# Create a specific user so LNDHub doesn't run as root # Create a specific user so LNDHub doesn't run as root
COPY --from=perms /etc/group /etc/passwd /etc/shadow /etc/ COPY --from=perms /etc/group /etc/passwd /etc/shadow /etc/
@ -26,12 +33,8 @@ COPY --from=perms /etc/group /etc/passwd /etc/shadow /etc/
# Copy LNDHub with installed modules from builder # Copy LNDHub with installed modules from builder
COPY --from=builder /lndhub /lndhub COPY --from=builder /lndhub /lndhub
# Delete git data as it's not needed inside the container
RUN rm -rf .git
# Create logs folder and ensure permissions are set correctly # Create logs folder and ensure permissions are set correctly
RUN mkdir /lndhub/logs && chown -R lndhub:lndhub /lndhub RUN mkdir /lndhub/logs && chown -R lndhub:lndhub /lndhub
USER lndhub USER lndhub
ENV PORT=3000 ENV PORT=3000

View File

@ -2,6 +2,10 @@
const config = require('./config'); const config = require('./config');
let jayson = require('jayson/promise'); let jayson = require('jayson/promise');
let url = require('url'); let url = require('url');
let rpc = url.parse(config.bitcoind.rpc); if (config.bitcoind) {
rpc.timeout = 15000; let rpc = url.parse(config.bitcoind.rpc);
module.exports = jayson.client.http(rpc); rpc.timeout = 15000;
module.exports = jayson.client.http(rpc);
} else {
module.exports = {};
}

79
btc-decoder.js Normal file
View File

@ -0,0 +1,79 @@
const bitcoin = require('bitcoinjs-lib');
const classify = require('bitcoinjs-lib/src/classify');
const decodeFormat = (tx) => ({
txid: tx.getId(),
version: tx.version,
locktime: tx.locktime,
});
const decodeInput = function (tx) {
const result = [];
tx.ins.forEach(function (input, n) {
result.push({
txid: input.hash.reverse().toString('hex'),
n: input.index,
script: bitcoin.script.toASM(input.script),
sequence: input.sequence,
});
});
return result;
};
const decodeOutput = function (tx, network) {
const format = function (out, n, network) {
const vout = {
satoshi: out.value,
value: (1e-8 * out.value).toFixed(8),
n: n,
scriptPubKey: {
asm: bitcoin.script.toASM(out.script),
hex: out.script.toString('hex'),
type: classify.output(out.script),
addresses: [],
},
};
switch (vout.scriptPubKey.type) {
case 'pubkeyhash':
case 'scripthash':
vout.scriptPubKey.addresses.push(bitcoin.address.fromOutputScript(out.script, network));
break;
case 'witnesspubkeyhash':
case 'witnessscripthash':
const data = bitcoin.script.decompile(out.script)[1];
vout.scriptPubKey.addresses.push(bitcoin.address.toBech32(data, 0, network.bech32));
break;
}
return vout;
};
const result = [];
tx.outs.forEach(function (out, n) {
result.push(format(out, n, network));
});
return result;
};
class TxDecoder {
constructor(rawTx, network = bitcoin.networks.bitcoin) {
this.tx = bitcoin.Transaction.fromHex(rawTx);
this.format = decodeFormat(this.tx);
this.inputs = decodeInput(this.tx);
this.outputs = decodeOutput(this.tx, network);
}
decode() {
const result = {};
const self = this;
Object.keys(self.format).forEach(function (key) {
result[key] = self.format[key];
});
result.outputs = self.outputs;
result.inputs = self.inputs;
return result;
}
}
module.exports.decodeRawHex = (rawTx, network = bitcoin.networks.bitcoin) => {
return new TxDecoder(rawTx, network).decode();
};

View File

@ -3,6 +3,8 @@ import { Lock } from './Lock';
var crypto = require('crypto'); var crypto = require('crypto');
var lightningPayReq = require('bolt11'); var lightningPayReq = require('bolt11');
import { BigNumber } from 'bignumber.js'; import { BigNumber } from 'bignumber.js';
import { decodeRawHex } from '../btc-decoder';
const config = require('../config');
// static cache: // static cache:
let _invoice_ispaid_cache = {}; let _invoice_ispaid_cache = {};
@ -114,11 +116,11 @@ export class User {
} }
let self = this; let self = this;
return new Promise(function(resolve, reject) { return new Promise(function (resolve, reject) {
self._lightning.newAddress({ type: 0 }, async function(err, response) { self._lightning.newAddress({ type: 0 }, async function (err, response) {
if (err) return reject('LND failure when trying to generate new address'); if (err) return reject('LND failure when trying to generate new address');
await self.addAddress(response.address); await self.addAddress(response.address);
self._bitcoindrpc.request('importaddress', [response.address, response.address, false]); if (config.bitcoind) self._bitcoindrpc.request('importaddress', [response.address, response.address, false]);
resolve(); resolve();
}); });
}); });
@ -126,7 +128,7 @@ export class User {
async watchAddress(address) { async watchAddress(address) {
if (!address) return; if (!address) return;
return this._bitcoindrpc.request('importaddress', [address, address, false]); if (config.bitcoind) return this._bitcoindrpc.request('importaddress', [address, address, false]);
} }
/** /**
@ -304,7 +306,10 @@ export class User {
_invoice_ispaid_cache[invoice.payment_hash] = paymentHashPaidAmountSat; _invoice_ispaid_cache[invoice.payment_hash] = paymentHashPaidAmountSat;
} }
invoice.amt = (paymentHashPaidAmountSat && parseInt(paymentHashPaidAmountSat) > decoded.satoshis) ? parseInt(paymentHashPaidAmountSat) : decoded.satoshis; invoice.amt =
paymentHashPaidAmountSat && parseInt(paymentHashPaidAmountSat) > decoded.satoshis
? parseInt(paymentHashPaidAmountSat)
: decoded.satoshis;
invoice.expire_time = 3600 * 24; invoice.expire_time = 3600 * 24;
// ^^^default; will keep for now. if we want to un-hardcode it - it should be among tags (`expire_time`) // ^^^default; will keep for now. if we want to un-hardcode it - it should be among tags (`expire_time`)
invoice.timestamp = decoded.timestamp; invoice.timestamp = decoded.timestamp;
@ -326,12 +331,7 @@ export class User {
* @returns {Promise<Array>} * @returns {Promise<Array>}
*/ */
async getTxs() { async getTxs() {
let addr = await this.getAddress(); const addr = await this.getOrGenerateAddress();
if (!addr) {
await this.generateAddress();
addr = await this.getAddress();
}
if (!addr) throw new Error('cannot get transactions: no onchain address assigned to user');
let txs = await this._listtransactions(); let txs = await this._listtransactions();
txs = txs.result; txs = txs.result;
let result = []; let result = [];
@ -402,17 +402,22 @@ export class User {
} }
try { try {
let txs = await this._bitcoindrpc.request('listtransactions', ['*', 100500, 0, true]);
// now, compacting response a bit
let ret = { result: [] }; let ret = { result: [] };
for (const tx of txs.result) { if (config.bitcoind) {
ret.result.push({ let txs = await this._bitcoindrpc.request('listtransactions', ['*', 100500, 0, true]);
category: tx.category, // now, compacting response a bit
amount: tx.amount, for (const tx of txs.result) {
confirmations: tx.confirmations, ret.result.push({
address: tx.address, category: tx.category,
time: tx.time, amount: tx.amount,
}); confirmations: tx.confirmations,
address: tx.address,
time: tx.time,
});
}
} else {
let txs = await this._getChainTransactions();
ret.result.push(...txs);
} }
_listtransactions_cache = JSON.stringify(ret); _listtransactions_cache = JSON.stringify(ret);
_listtransactions_cache_expiry_ts = +new Date() + 5 * 60 * 1000; // 5 min _listtransactions_cache_expiry_ts = +new Date() + 5 * 60 * 1000; // 5 min
@ -426,18 +431,42 @@ export class User {
} }
} }
async _getChainTransactions() {
return new Promise((resolve, reject) => {
this._lightning.getTransactions({}, (err, data) => {
if (err) return reject(err);
const { transactions } = data;
const outTxns = [];
// on lightning incoming transactions have no labels
// for now filter out known labels to reduce transactions
transactions
.filter((tx) => tx.label !== 'external' && !tx.label.includes('openchannel'))
.map((tx) => {
const decodedTx = decodeRawHex(tx.raw_tx_hex);
decodedTx.outputs.forEach((vout) =>
outTxns.push({
// mark all as received, since external is filtered out
category: 'receive',
confirmations: tx.num_confirmations,
amount: Number(vout.value),
address: vout.scriptPubKey.addresses[0],
time: tx.time_stamp,
}),
);
});
resolve(outTxns);
});
});
}
/** /**
* Returning onchain txs for user's address that are less than 3 confs * Returning onchain txs for user's address that are less than 3 confs
* *
* @returns {Promise<Array>} * @returns {Promise<Array>}
*/ */
async getPendingTxs() { async getPendingTxs() {
let addr = await this.getAddress(); const addr = await this.getOrGenerateAddress();
if (!addr) {
await this.generateAddress();
addr = await this.getAddress();
}
if (!addr) throw new Error('cannot get transactions: no onchain address assigned to user');
let txs = await this._listtransactions(); let txs = await this._listtransactions();
txs = txs.result; txs = txs.result;
let result = []; let result = [];
@ -556,6 +585,16 @@ export class User {
return result; return result;
} }
async getOrGenerateAddress() {
let addr = await this.getAddress();
if (!addr) {
await this.generateAddress();
addr = await this.getAddress();
}
if (!addr) throw new Error('cannot get transactions: no onchain address assigned to user');
return addr;
}
_hash(string) { _hash(string) {
return crypto.createHash('sha256').update(string).digest().toString('hex'); return crypto.createHash('sha256').update(string).digest().toString('hex');
} }

View File

@ -4,6 +4,7 @@ const config = require('../config');
let express = require('express'); let express = require('express');
let router = express.Router(); let router = express.Router();
let logger = require('../utils/logger'); let logger = require('../utils/logger');
const MIN_BTC_BLOCK = 670000;
console.log('using config', JSON.stringify(config)); console.log('using config', JSON.stringify(config));
var Redis = require('ioredis'); var Redis = require('ioredis');
@ -19,17 +20,19 @@ let lightning = require('../lightning');
let identity_pubkey = false; let identity_pubkey = false;
// ###################### SMOKE TESTS ######################## // ###################### SMOKE TESTS ########################
bitcoinclient.request('getblockchaininfo', false, function (err, info) { if (config.bitcoind) {
if (info && info.result && info.result.blocks) { bitcoinclient.request('getblockchaininfo', false, function (err, info) {
if (info.result.chain === 'mainnet' && info.result.blocks < 550000) { if (info && info.result && info.result.blocks) {
console.error('bitcoind is not caught up'); if (info.result.chain === 'mainnet' && info.result.blocks < MIN_BTC_BLOCK && !config.forceStart) {
process.exit(1); console.error('bitcoind is not caught up');
process.exit(1);
}
} else {
console.error('bitcoind failure:', err, info);
process.exit(2);
} }
} else { });
console.error('bitcoind failure:', err, info); }
process.exit(2);
}
});
lightning.getInfo({}, function (err, info) { lightning.getInfo({}, function (err, info) {
if (err) { if (err) {
@ -39,7 +42,7 @@ lightning.getInfo({}, function (err, info) {
} }
if (info) { if (info) {
console.info(info); console.info(info);
if (!info.synced_to_chain) { if (!info.synced_to_chain && !config.forceStart) {
console.error('lnd not synced'); console.error('lnd not synced');
// process.exit(4); // process.exit(4);
} }
@ -70,7 +73,10 @@ const subscribeInvoicesCallCallback = async function (response) {
return; return;
} }
let invoice = new Invo(redis, bitcoinclient, lightning); let invoice = new Invo(redis, bitcoinclient, lightning);
await invoice._setIsPaymentHashPaidInDatabase(LightningInvoiceSettledNotification.hash, LightningInvoiceSettledNotification.amt_paid_sat || 1); await invoice._setIsPaymentHashPaidInDatabase(
LightningInvoiceSettledNotification.hash,
LightningInvoiceSettledNotification.amt_paid_sat || 1,
);
const user = new User(redis, bitcoinclient, lightning); const user = new User(redis, bitcoinclient, lightning);
user._userid = await user.getUseridByPaymentHash(LightningInvoiceSettledNotification.hash); user._userid = await user.getUseridByPaymentHash(LightningInvoiceSettledNotification.hash);
await user.clearBalanceCache(); await user.clearBalanceCache();
@ -469,7 +475,7 @@ router.get('/checkrouteinvoice', async function (req, res) {
}); });
}); });
router.get('/queryroutes/:source/:dest/:amt', async function(req, res) { router.get('/queryroutes/:source/:dest/:amt', async function (req, res) {
logger.log('/queryroutes', [req.id]); logger.log('/queryroutes', [req.id]);
let request = { let request = {
@ -477,7 +483,7 @@ router.get('/queryroutes/:source/:dest/:amt', async function(req, res) {
amt: req.params.amt, amt: req.params.amt,
source_pub_key: req.params.source, source_pub_key: req.params.source,
}; };
lightning.queryRoutes(request, function(err, response) { lightning.queryRoutes(request, function (err, response) {
console.log(JSON.stringify(response, null, 2)); console.log(JSON.stringify(response, null, 2));
res.send(response); res.send(response);
}); });

View File

@ -1,10 +1,10 @@
let express = require('express'); const express = require('express');
let router = express.Router(); const router = express.Router();
let fs = require('fs'); const fs = require('fs');
let mustache = require('mustache'); const mustache = require('mustache');
let lightning = require('../lightning'); const lightning = require('../lightning');
let logger = require('../utils/logger'); const logger = require('../utils/logger');
var qr = require('qr-image'); const qr = require('qr-image');
let lightningGetInfo = {}; let lightningGetInfo = {};
let lightningListChannels = {}; let lightningListChannels = {};
@ -92,7 +92,11 @@ router.get('/', function (req, res) {
}); });
router.get('/qr', function (req, res) { router.get('/qr', function (req, res) {
const url = "bluewallet:setlndhuburl?url=" + encodeURIComponent(req.protocol + '://' + req.headers.host); let host = req.headers.host;
if (process.env.TOR_URL) {
host = process.env.TOR_URL;
}
const url = 'bluewallet:setlndhuburl?url=' + encodeURIComponent(req.protocol + '://' + host);
var code = qr.image(url, { type: 'png' }); var code = qr.image(url, { type: 'png' });
res.setHeader('Content-type', 'image/png'); res.setHeader('Content-type', 'image/png');
code.pipe(res); code.pipe(res);

View File

@ -8,7 +8,7 @@ const loaderOptions = {
longs: String, longs: String,
enums: String, enums: String,
defaults: true, defaults: true,
oneofs: true oneofs: true,
}; };
const packageDefinition = protoLoader.loadSync('rpc.proto', loaderOptions); const packageDefinition = protoLoader.loadSync('rpc.proto', loaderOptions);
var lnrpc = grpc.loadPackageDefinition(packageDefinition).lnrpc; var lnrpc = grpc.loadPackageDefinition(packageDefinition).lnrpc;

8904
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
{ {
"name": "LndHub", "name": "lndhub",
"version": "1.2.2", "version": "1.2.2",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
@ -12,24 +12,25 @@
"author": "Igor Korsakov <overtorment@gmail.com>", "author": "Igor Korsakov <overtorment@gmail.com>",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/cli": "^7.12.10", "@babel/cli": "^7.13.0",
"@babel/core": "^7.12.10", "@babel/core": "^7.13.1",
"@babel/eslint-parser": "^7.12.1", "@babel/eslint-parser": "^7.13.4",
"@babel/node": "^7.12.10", "@babel/node": "^7.13.0",
"@babel/preset-env": "^7.12.11", "@babel/preset-env": "^7.13.5",
"@babel/register": "^7.12.10", "@babel/register": "^7.13.0",
"@grpc/grpc-js": "^1.2.2", "@grpc/grpc-js": "^1.2.8",
"@grpc/proto-loader": "^0.5.6", "@grpc/proto-loader": "^0.5.6",
"bignumber.js": "^9.0.1", "bignumber.js": "^9.0.1",
"bitcoinjs-lib": "^5.2.0",
"bolt11": "^1.2.7", "bolt11": "^1.2.7",
"core-js": "^3.8.1", "core-js": "^3.9.0",
"eslint": "^7.19.0", "eslint": "^7.20.0",
"eslint-config-prettier": "^7.1.0", "eslint-config-prettier": "^8.0.0",
"eslint-plugin-prettier": "^3.3.0", "eslint-plugin-prettier": "^3.3.1",
"express": "^4.17.1", "express": "^4.17.1",
"express-rate-limit": "^5.2.3", "express-rate-limit": "^5.2.6",
"frisbee": "^3.1.4", "frisbee": "^3.1.4",
"ioredis": "^4.19.4", "ioredis": "^4.22.0",
"jayson": "^3.4.4", "jayson": "^3.4.4",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"mustache": "^4.1.0", "mustache": "^4.1.0",

View File

@ -13,45 +13,45 @@ const important_channels = {
name: 'coingate.com', name: 'coingate.com',
uri: '0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3@3.124.63.44:9735', uri: '0242a4ae0c5bef18048fbecf995094b74bfb0f7391418d71ed394784373f41e4f3@3.124.63.44:9735',
}, },
// '0254ff808f53b2f8c45e74b70430f336c6c76ba2f4af289f48d6086ae6e60462d3': { // '0254ff808f53b2f8c45e74b70430f336c6c76ba2f4af289f48d6086ae6e60462d3': {
// name: 'bitrefill thor', // name: 'bitrefill thor',
// uri: '0254ff808f53b2f8c45e74b70430f336c6c76ba2f4af289f48d6086ae6e60462d3@52.30.63.2:9735', // uri: '0254ff808f53b2f8c45e74b70430f336c6c76ba2f4af289f48d6086ae6e60462d3@52.30.63.2:9735',
// wumbo: 1, // wumbo: 1,
// }, // },
'030c3f19d742ca294a55c00376b3b355c3c90d61c6b6b39554dbc7ac19b141c14f': { '030c3f19d742ca294a55c00376b3b355c3c90d61c6b6b39554dbc7ac19b141c14f': {
name: 'bitrefill 2', name: 'bitrefill 2',
uri: '030c3f19d742ca294a55c00376b3b355c3c90d61c6b6b39554dbc7ac19b141c14f@52.50.244.44:9735', uri: '030c3f19d742ca294a55c00376b3b355c3c90d61c6b6b39554dbc7ac19b141c14f@52.50.244.44:9735',
wumbo: 1, wumbo: 1,
}, },
// '025f1456582e70c4c06b61d5c8ed3ce229e6d0db538be337a2dc6d163b0ebc05a5': { // '025f1456582e70c4c06b61d5c8ed3ce229e6d0db538be337a2dc6d163b0ebc05a5': {
// name: 'paywithmoon.com', // name: 'paywithmoon.com',
// uri: '025f1456582e70c4c06b61d5c8ed3ce229e6d0db538be337a2dc6d163b0ebc05a5@52.86.210.65:9735', // uri: '025f1456582e70c4c06b61d5c8ed3ce229e6d0db538be337a2dc6d163b0ebc05a5@52.86.210.65:9735',
// }, // },
// '0279c22ed7a068d10dc1a38ae66d2d6461e269226c60258c021b1ddcdfe4b00bc4': { // '0279c22ed7a068d10dc1a38ae66d2d6461e269226c60258c021b1ddcdfe4b00bc4': {
// name: 'ln1.satoshilabs.com', // name: 'ln1.satoshilabs.com',
// uri: '0279c22ed7a068d10dc1a38ae66d2d6461e269226c60258c021b1ddcdfe4b00bc4@157.230.28.160:9735', // uri: '0279c22ed7a068d10dc1a38ae66d2d6461e269226c60258c021b1ddcdfe4b00bc4@157.230.28.160:9735',
// }, // },
// '02004c625d622245606a1ea2c1c69cfb4516b703b47945a3647713c05fe4aaeb1c': { // '02004c625d622245606a1ea2c1c69cfb4516b703b47945a3647713c05fe4aaeb1c': {
// name: 'LivingRoomOfSatoshi', // name: 'LivingRoomOfSatoshi',
// uri: '02004c625d622245606a1ea2c1c69cfb4516b703b47945a3647713c05fe4aaeb1c@172.81.178.151:9735', // uri: '02004c625d622245606a1ea2c1c69cfb4516b703b47945a3647713c05fe4aaeb1c@172.81.178.151:9735',
// }, // },
'02816caed43171d3c9854e3b0ab2cf0c42be086ff1bd4005acc2a5f7db70d83774': { '02816caed43171d3c9854e3b0ab2cf0c42be086ff1bd4005acc2a5f7db70d83774': {
name: 'ln.pizza aka fold', name: 'ln.pizza aka fold',
uri: '02816caed43171d3c9854e3b0ab2cf0c42be086ff1bd4005acc2a5f7db70d83774@35.238.153.25:9735', uri: '02816caed43171d3c9854e3b0ab2cf0c42be086ff1bd4005acc2a5f7db70d83774@35.238.153.25:9735',
wumbo: 1, wumbo: 1,
}, },
// '0331f80652fb840239df8dc99205792bba2e559a05469915804c08420230e23c7c': { // '0331f80652fb840239df8dc99205792bba2e559a05469915804c08420230e23c7c': {
// name: 'LightningPowerUsers.com', // name: 'LightningPowerUsers.com',
// uri: '0331f80652fb840239df8dc99205792bba2e559a05469915804c08420230e23c7c@34.200.181.109:9735', // uri: '0331f80652fb840239df8dc99205792bba2e559a05469915804c08420230e23c7c@34.200.181.109:9735',
// }, // },
// '033d8656219478701227199cbd6f670335c8d408a92ae88b962c49d4dc0e83e025': { // '033d8656219478701227199cbd6f670335c8d408a92ae88b962c49d4dc0e83e025': {
// name: 'bfx-lnd0', // name: 'bfx-lnd0',
// uri: '033d8656219478701227199cbd6f670335c8d408a92ae88b962c49d4dc0e83e025@34.65.85.39:9735', // uri: '033d8656219478701227199cbd6f670335c8d408a92ae88b962c49d4dc0e83e025@34.65.85.39:9735',
// }, // },
// '037f990e61acee8a7697966afd29dd88f3b1f8a7b14d625c4f8742bd952003a590': { // '037f990e61acee8a7697966afd29dd88f3b1f8a7b14d625c4f8742bd952003a590': {
// name: 'fixedfloat.com', // name: 'fixedfloat.com',
// uri: '037f990e61acee8a7697966afd29dd88f3b1f8a7b14d625c4f8742bd952003a590@185.5.53.91:9735', // uri: '037f990e61acee8a7697966afd29dd88f3b1f8a7b14d625c4f8742bd952003a590@185.5.53.91:9735',
// }, // },
'03c2abfa93eacec04721c019644584424aab2ba4dff3ac9bdab4e9c97007491dda': { '03c2abfa93eacec04721c019644584424aab2ba4dff3ac9bdab4e9c97007491dda': {
name: 'tippin.me', name: 'tippin.me',
uri: '03c2abfa93eacec04721c019644584424aab2ba4dff3ac9bdab4e9c97007491dda@157.245.68.47:9735', uri: '03c2abfa93eacec04721c019644584424aab2ba4dff3ac9bdab4e9c97007491dda@157.245.68.47:9735',