Merge branch 'master' into nymkappa/bugfix/clightning-wrong-channel-id
This commit is contained in:
commit
c85b6d53c5
@ -21,7 +21,9 @@
|
|||||||
"EXTERNAL_RETRY_INTERVAL": 0,
|
"EXTERNAL_RETRY_INTERVAL": 0,
|
||||||
"USER_AGENT": "mempool",
|
"USER_AGENT": "mempool",
|
||||||
"STDOUT_LOG_MIN_PRIORITY": "debug",
|
"STDOUT_LOG_MIN_PRIORITY": "debug",
|
||||||
"AUTOMATIC_BLOCK_REINDEXING": false
|
"AUTOMATIC_BLOCK_REINDEXING": false,
|
||||||
|
"POOLS_JSON_URL": "https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json",
|
||||||
|
"POOLS_JSON_TREE_URL": "https://api.github.com/repos/mempool/mining-pools/git/trees/master"
|
||||||
},
|
},
|
||||||
"CORE_RPC": {
|
"CORE_RPC": {
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
|
@ -22,6 +22,8 @@ import poolsParser from './pools-parser';
|
|||||||
import BlocksSummariesRepository from '../repositories/BlocksSummariesRepository';
|
import BlocksSummariesRepository from '../repositories/BlocksSummariesRepository';
|
||||||
import mining from './mining/mining';
|
import mining from './mining/mining';
|
||||||
import DifficultyAdjustmentsRepository from '../repositories/DifficultyAdjustmentsRepository';
|
import DifficultyAdjustmentsRepository from '../repositories/DifficultyAdjustmentsRepository';
|
||||||
|
import PricesRepository from '../repositories/PricesRepository';
|
||||||
|
import priceUpdater from '../tasks/price-updater';
|
||||||
|
|
||||||
class Blocks {
|
class Blocks {
|
||||||
private blocks: BlockExtended[] = [];
|
private blocks: BlockExtended[] = [];
|
||||||
@ -457,6 +459,19 @@ class Blocks {
|
|||||||
}
|
}
|
||||||
await blocksRepository.$saveBlockInDatabase(blockExtended);
|
await blocksRepository.$saveBlockInDatabase(blockExtended);
|
||||||
|
|
||||||
|
const lastestPriceId = await PricesRepository.$getLatestPriceId();
|
||||||
|
if (priceUpdater.historyInserted === true && lastestPriceId !== null) {
|
||||||
|
await blocksRepository.$saveBlockPrices([{
|
||||||
|
height: blockExtended.height,
|
||||||
|
priceId: lastestPriceId,
|
||||||
|
}]);
|
||||||
|
} else {
|
||||||
|
logger.info(`Cannot save block price for ${blockExtended.height} because the price updater hasnt completed yet. Trying again in 10 seconds.`)
|
||||||
|
setTimeout(() => {
|
||||||
|
indexer.runSingleTask('blocksPrices');
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
// Save blocks summary for visualization if it's enabled
|
// Save blocks summary for visualization if it's enabled
|
||||||
if (Common.blocksSummariesIndexingEnabled() === true) {
|
if (Common.blocksSummariesIndexingEnabled() === true) {
|
||||||
await this.$getStrippedBlockTransactions(blockExtended.id, true);
|
await this.$getStrippedBlockTransactions(blockExtended.id, true);
|
||||||
|
@ -96,7 +96,31 @@ class ChannelsApi {
|
|||||||
|
|
||||||
public async $getChannel(id: string): Promise<any> {
|
public async $getChannel(id: string): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const query = `SELECT n1.alias AS alias_left, n2.alias AS alias_right, channels.*, ns1.channels AS channels_left, ns1.capacity AS capacity_left, ns2.channels AS channels_right, ns2.capacity AS capacity_right FROM channels LEFT JOIN nodes AS n1 ON n1.public_key = channels.node1_public_key LEFT JOIN nodes AS n2 ON n2.public_key = channels.node2_public_key LEFT JOIN node_stats AS ns1 ON ns1.public_key = channels.node1_public_key LEFT JOIN node_stats AS ns2 ON ns2.public_key = channels.node2_public_key WHERE (ns1.id = (SELECT MAX(id) FROM node_stats WHERE public_key = channels.node1_public_key) AND ns2.id = (SELECT MAX(id) FROM node_stats WHERE public_key = channels.node2_public_key)) AND channels.id = ?`;
|
const query = `
|
||||||
|
SELECT n1.alias AS alias_left, n1.longitude as node1_longitude, n1.latitude as node1_latitude,
|
||||||
|
n2.alias AS alias_right, n2.longitude as node2_longitude, n2.latitude as node2_latitude,
|
||||||
|
channels.*,
|
||||||
|
ns1.channels AS channels_left, ns1.capacity AS capacity_left, ns2.channels AS channels_right, ns2.capacity AS capacity_right
|
||||||
|
FROM channels
|
||||||
|
LEFT JOIN nodes AS n1 ON n1.public_key = channels.node1_public_key
|
||||||
|
LEFT JOIN nodes AS n2 ON n2.public_key = channels.node2_public_key
|
||||||
|
LEFT JOIN node_stats AS ns1 ON ns1.public_key = channels.node1_public_key
|
||||||
|
LEFT JOIN node_stats AS ns2 ON ns2.public_key = channels.node2_public_key
|
||||||
|
WHERE (
|
||||||
|
ns1.id = (
|
||||||
|
SELECT MAX(id)
|
||||||
|
FROM node_stats
|
||||||
|
WHERE public_key = channels.node1_public_key
|
||||||
|
)
|
||||||
|
AND ns2.id = (
|
||||||
|
SELECT MAX(id)
|
||||||
|
FROM node_stats
|
||||||
|
WHERE public_key = channels.node2_public_key
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND channels.id = ?
|
||||||
|
`;
|
||||||
|
|
||||||
const [rows]: any = await DB.query(query, [id]);
|
const [rows]: any = await DB.query(query, [id]);
|
||||||
if (rows[0]) {
|
if (rows[0]) {
|
||||||
return this.convertChannel(rows[0]);
|
return this.convertChannel(rows[0]);
|
||||||
@ -289,6 +313,8 @@ class ChannelsApi {
|
|||||||
'max_htlc_mtokens': channel.node1_max_htlc_mtokens,
|
'max_htlc_mtokens': channel.node1_max_htlc_mtokens,
|
||||||
'min_htlc_mtokens': channel.node1_min_htlc_mtokens,
|
'min_htlc_mtokens': channel.node1_min_htlc_mtokens,
|
||||||
'updated_at': channel.node1_updated_at,
|
'updated_at': channel.node1_updated_at,
|
||||||
|
'longitude': channel.node1_longitude,
|
||||||
|
'latitude': channel.node1_latitude,
|
||||||
},
|
},
|
||||||
'node_right': {
|
'node_right': {
|
||||||
'alias': channel.alias_right,
|
'alias': channel.alias_right,
|
||||||
@ -302,6 +328,8 @@ class ChannelsApi {
|
|||||||
'max_htlc_mtokens': channel.node2_max_htlc_mtokens,
|
'max_htlc_mtokens': channel.node2_max_htlc_mtokens,
|
||||||
'min_htlc_mtokens': channel.node2_min_htlc_mtokens,
|
'min_htlc_mtokens': channel.node2_min_htlc_mtokens,
|
||||||
'updated_at': channel.node2_updated_at,
|
'updated_at': channel.node2_updated_at,
|
||||||
|
'longitude': channel.node2_longitude,
|
||||||
|
'latitude': channel.node2_latitude,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,9 @@ class ChannelsRoutes {
|
|||||||
res.status(404).send('Channel not found');
|
res.status(404).send('Channel not found');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
res.header('Pragma', 'public');
|
||||||
|
res.header('Cache-control', 'public');
|
||||||
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(channel);
|
res.json(channel);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e instanceof Error ? e.message : e);
|
res.status(500).send(e instanceof Error ? e.message : e);
|
||||||
|
@ -168,7 +168,7 @@ class NodesApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async $getNodesISP(groupBy: string, showTor: boolean) {
|
public async $getNodesISPRanking(groupBy: string, showTor: boolean) {
|
||||||
try {
|
try {
|
||||||
const orderBy = groupBy === 'capacity' ? `CAST(SUM(capacity) as INT)` : `COUNT(DISTINCT nodes.public_key)`;
|
const orderBy = groupBy === 'capacity' ? `CAST(SUM(capacity) as INT)` : `COUNT(DISTINCT nodes.public_key)`;
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ class NodesRoutes {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodesPerAs = await nodesApi.$getNodesISP(groupBy, showTor);
|
const nodesPerAs = await nodesApi.$getNodesISPRanking(groupBy, showTor);
|
||||||
|
|
||||||
res.header('Pragma', 'public');
|
res.header('Pragma', 'public');
|
||||||
res.header('Cache-control', 'public');
|
res.header('Cache-control', 'public');
|
||||||
|
@ -473,7 +473,7 @@ class Mining {
|
|||||||
|
|
||||||
for (const block of blocksWithoutPrices) {
|
for (const block of blocksWithoutPrices) {
|
||||||
// Quick optimisation, out mtgox feed only goes back to 2010-07-19 02:00:00, so skip the first 68951 blocks
|
// Quick optimisation, out mtgox feed only goes back to 2010-07-19 02:00:00, so skip the first 68951 blocks
|
||||||
if (block.height < 68951) {
|
if (['mainnet', 'testnet'].includes(config.MEMPOOL.NETWORK) && block.height < 68951) {
|
||||||
blocksPrices.push({
|
blocksPrices.push({
|
||||||
height: block.height,
|
height: block.height,
|
||||||
priceId: prices[0].id,
|
priceId: prices[0].id,
|
||||||
@ -492,11 +492,11 @@ class Mining {
|
|||||||
|
|
||||||
if (blocksPrices.length >= 100000) {
|
if (blocksPrices.length >= 100000) {
|
||||||
totalInserted += blocksPrices.length;
|
totalInserted += blocksPrices.length;
|
||||||
|
let logStr = `Linking ${blocksPrices.length} blocks to their closest price`;
|
||||||
if (blocksWithoutPrices.length > 200000) {
|
if (blocksWithoutPrices.length > 200000) {
|
||||||
logger.debug(`Linking ${blocksPrices.length} newly indexed blocks to their closest price | Progress ${Math.round(totalInserted / blocksWithoutPrices.length * 100)}%`);
|
logStr += ` | Progress ${Math.round(totalInserted / blocksWithoutPrices.length * 100)}%`;
|
||||||
} else {
|
|
||||||
logger.debug(`Linking ${blocksPrices.length} newly indexed blocks to their closest price`);
|
|
||||||
}
|
}
|
||||||
|
logger.debug(logStr);
|
||||||
await BlocksRepository.$saveBlockPrices(blocksPrices);
|
await BlocksRepository.$saveBlockPrices(blocksPrices);
|
||||||
blocksPrices.length = 0;
|
blocksPrices.length = 0;
|
||||||
}
|
}
|
||||||
@ -504,11 +504,11 @@ class Mining {
|
|||||||
|
|
||||||
if (blocksPrices.length > 0) {
|
if (blocksPrices.length > 0) {
|
||||||
totalInserted += blocksPrices.length;
|
totalInserted += blocksPrices.length;
|
||||||
|
let logStr = `Linking ${blocksPrices.length} blocks to their closest price`;
|
||||||
if (blocksWithoutPrices.length > 200000) {
|
if (blocksWithoutPrices.length > 200000) {
|
||||||
logger.debug(`Linking ${blocksPrices.length} newly indexed blocks to their closest price | Progress ${Math.round(totalInserted / blocksWithoutPrices.length * 100)}%`);
|
logStr += ` | Progress ${Math.round(totalInserted / blocksWithoutPrices.length * 100)}%`;
|
||||||
} else {
|
|
||||||
logger.debug(`Linking ${blocksPrices.length} newly indexed blocks to their closest price`);
|
|
||||||
}
|
}
|
||||||
|
logger.debug(logStr);
|
||||||
await BlocksRepository.$saveBlockPrices(blocksPrices);
|
await BlocksRepository.$saveBlockPrices(blocksPrices);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -24,6 +24,8 @@ interface IConfig {
|
|||||||
USER_AGENT: string;
|
USER_AGENT: string;
|
||||||
STDOUT_LOG_MIN_PRIORITY: 'emerg' | 'alert' | 'crit' | 'err' | 'warn' | 'notice' | 'info' | 'debug';
|
STDOUT_LOG_MIN_PRIORITY: 'emerg' | 'alert' | 'crit' | 'err' | 'warn' | 'notice' | 'info' | 'debug';
|
||||||
AUTOMATIC_BLOCK_REINDEXING: boolean;
|
AUTOMATIC_BLOCK_REINDEXING: boolean;
|
||||||
|
POOLS_JSON_URL: string,
|
||||||
|
POOLS_JSON_TREE_URL: string,
|
||||||
};
|
};
|
||||||
ESPLORA: {
|
ESPLORA: {
|
||||||
REST_API_URL: string;
|
REST_API_URL: string;
|
||||||
@ -136,6 +138,8 @@ const defaults: IConfig = {
|
|||||||
'USER_AGENT': 'mempool',
|
'USER_AGENT': 'mempool',
|
||||||
'STDOUT_LOG_MIN_PRIORITY': 'debug',
|
'STDOUT_LOG_MIN_PRIORITY': 'debug',
|
||||||
'AUTOMATIC_BLOCK_REINDEXING': false,
|
'AUTOMATIC_BLOCK_REINDEXING': false,
|
||||||
|
'POOLS_JSON_URL': 'https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json',
|
||||||
|
'POOLS_JSON_TREE_URL': 'https://api.github.com/repos/mempool/mining-pools/git/trees/master',
|
||||||
},
|
},
|
||||||
'ESPLORA': {
|
'ESPLORA': {
|
||||||
'REST_API_URL': 'http://127.0.0.1:3000',
|
'REST_API_URL': 'http://127.0.0.1:3000',
|
||||||
|
@ -6,13 +6,12 @@ import logger from './logger';
|
|||||||
import HashratesRepository from './repositories/HashratesRepository';
|
import HashratesRepository from './repositories/HashratesRepository';
|
||||||
import bitcoinClient from './api/bitcoin/bitcoin-client';
|
import bitcoinClient from './api/bitcoin/bitcoin-client';
|
||||||
import priceUpdater from './tasks/price-updater';
|
import priceUpdater from './tasks/price-updater';
|
||||||
|
import PricesRepository from './repositories/PricesRepository';
|
||||||
|
|
||||||
class Indexer {
|
class Indexer {
|
||||||
runIndexer = true;
|
runIndexer = true;
|
||||||
indexerRunning = false;
|
indexerRunning = false;
|
||||||
|
tasksRunning: string[] = [];
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public reindex() {
|
public reindex() {
|
||||||
if (Common.indexingEnabled()) {
|
if (Common.indexingEnabled()) {
|
||||||
@ -20,6 +19,28 @@ class Indexer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async runSingleTask(task: 'blocksPrices') {
|
||||||
|
if (!Common.indexingEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task === 'blocksPrices' && !this.tasksRunning.includes(task)) {
|
||||||
|
this.tasksRunning.push(task);
|
||||||
|
const lastestPriceId = await PricesRepository.$getLatestPriceId();
|
||||||
|
if (priceUpdater.historyInserted === false || lastestPriceId === null) {
|
||||||
|
logger.debug(`Blocks prices indexer is waiting for the price updater to complete`)
|
||||||
|
setTimeout(() => {
|
||||||
|
this.tasksRunning = this.tasksRunning.filter(runningTask => runningTask != task)
|
||||||
|
this.runSingleTask('blocksPrices');
|
||||||
|
}, 10000);
|
||||||
|
} else {
|
||||||
|
logger.debug(`Blocks prices indexer will run now`)
|
||||||
|
await mining.$indexBlockPrices();
|
||||||
|
this.tasksRunning = this.tasksRunning.filter(runningTask => runningTask != task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async $run() {
|
public async $run() {
|
||||||
if (!Common.indexingEnabled() || this.runIndexer === false ||
|
if (!Common.indexingEnabled() || this.runIndexer === false ||
|
||||||
this.indexerRunning === true || mempool.hasPriority()
|
this.indexerRunning === true || mempool.hasPriority()
|
||||||
@ -50,7 +71,7 @@ class Indexer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await mining.$indexBlockPrices();
|
this.runSingleTask('blocksPrices');
|
||||||
await mining.$indexDifficultyAdjustments();
|
await mining.$indexDifficultyAdjustments();
|
||||||
await this.$resetHashratesIndexingState(); // TODO - Remove this as it's not efficient
|
await this.$resetHashratesIndexingState(); // TODO - Remove this as it's not efficient
|
||||||
await mining.$generateNetworkHashrateHistory();
|
await mining.$generateNetworkHashrateHistory();
|
||||||
|
@ -27,6 +27,11 @@ class PricesRepository {
|
|||||||
return oldestRow[0] ? oldestRow[0].time : 0;
|
return oldestRow[0] ? oldestRow[0].time : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async $getLatestPriceId(): Promise<number | null> {
|
||||||
|
const [oldestRow] = await DB.query(`SELECT id from prices WHERE USD != -1 ORDER BY time DESC LIMIT 1`);
|
||||||
|
return oldestRow[0] ? oldestRow[0].id : null;
|
||||||
|
}
|
||||||
|
|
||||||
public async $getLatestPriceTime(): Promise<number> {
|
public async $getLatestPriceTime(): Promise<number> {
|
||||||
const [oldestRow] = await DB.query(`SELECT UNIX_TIMESTAMP(time) as time from prices WHERE USD != -1 ORDER BY time DESC LIMIT 1`);
|
const [oldestRow] = await DB.query(`SELECT UNIX_TIMESTAMP(time) as time from prices WHERE USD != -1 ORDER BY time DESC LIMIT 1`);
|
||||||
return oldestRow[0] ? oldestRow[0].time : 0;
|
return oldestRow[0] ? oldestRow[0].time : 0;
|
||||||
|
@ -12,14 +12,11 @@ import * as https from 'https';
|
|||||||
*/
|
*/
|
||||||
class PoolsUpdater {
|
class PoolsUpdater {
|
||||||
lastRun: number = 0;
|
lastRun: number = 0;
|
||||||
currentSha: any = undefined;
|
currentSha: string | undefined = undefined;
|
||||||
poolsUrl: string = 'https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json';
|
poolsUrl: string = config.MEMPOOL.POOLS_JSON_URL;
|
||||||
treeUrl: string = 'https://api.github.com/repos/mempool/mining-pools/git/trees/master';
|
treeUrl: string = config.MEMPOOL.POOLS_JSON_TREE_URL;
|
||||||
|
|
||||||
constructor() {
|
public async updatePoolsJson(): Promise<void> {
|
||||||
}
|
|
||||||
|
|
||||||
public async updatePoolsJson() {
|
|
||||||
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) {
|
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -77,7 +74,7 @@ class PoolsUpdater {
|
|||||||
/**
|
/**
|
||||||
* Fetch our latest pools.json sha from the db
|
* Fetch our latest pools.json sha from the db
|
||||||
*/
|
*/
|
||||||
private async updateDBSha(githubSha: string) {
|
private async updateDBSha(githubSha: string): Promise<void> {
|
||||||
this.currentSha = githubSha;
|
this.currentSha = githubSha;
|
||||||
if (config.DATABASE.ENABLED === true) {
|
if (config.DATABASE.ENABLED === true) {
|
||||||
try {
|
try {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
import { Common } from '../api/common';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
import PricesRepository from '../repositories/PricesRepository';
|
import PricesRepository from '../repositories/PricesRepository';
|
||||||
@ -34,10 +35,10 @@ export interface Prices {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PriceUpdater {
|
class PriceUpdater {
|
||||||
historyInserted: boolean = false;
|
public historyInserted = false;
|
||||||
lastRun: number = 0;
|
lastRun = 0;
|
||||||
lastHistoricalRun: number = 0;
|
lastHistoricalRun = 0;
|
||||||
running: boolean = false;
|
running = false;
|
||||||
feeds: PriceFeed[] = [];
|
feeds: PriceFeed[] = [];
|
||||||
currencies: string[] = ['USD', 'EUR', 'GBP', 'CAD', 'CHF', 'AUD', 'JPY'];
|
currencies: string[] = ['USD', 'EUR', 'GBP', 'CAD', 'CHF', 'AUD', 'JPY'];
|
||||||
latestPrices: Prices;
|
latestPrices: Prices;
|
||||||
|
@ -102,7 +102,9 @@ Below we list all settings from `mempool-config.json` and the corresponding over
|
|||||||
"PRICE_FEED_UPDATE_INTERVAL": 600,
|
"PRICE_FEED_UPDATE_INTERVAL": 600,
|
||||||
"USE_SECOND_NODE_FOR_MINFEE": false,
|
"USE_SECOND_NODE_FOR_MINFEE": false,
|
||||||
"EXTERNAL_ASSETS": ["https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json"],
|
"EXTERNAL_ASSETS": ["https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json"],
|
||||||
"STDOUT_LOG_MIN_PRIORITY": "info"
|
"STDOUT_LOG_MIN_PRIORITY": "info",
|
||||||
|
"POOLS_JSON_URL": "https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json",
|
||||||
|
"POOLS_JSON_TREE_URL": "https://api.github.com/repos/mempool/mining-pools/git/trees/master"
|
||||||
},
|
},
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -126,6 +128,8 @@ Corresponding `docker-compose.yml` overrides:
|
|||||||
MEMPOOL_USE_SECOND_NODE_FOR_MINFEE: ""
|
MEMPOOL_USE_SECOND_NODE_FOR_MINFEE: ""
|
||||||
MEMPOOL_EXTERNAL_ASSETS: ""
|
MEMPOOL_EXTERNAL_ASSETS: ""
|
||||||
MEMPOOL_STDOUT_LOG_MIN_PRIORITY: ""
|
MEMPOOL_STDOUT_LOG_MIN_PRIORITY: ""
|
||||||
|
MEMPOOL_POOLS_JSON_URL: ""
|
||||||
|
MEMPOOL_POOLS_JSON_TREE_URL: ""
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ __MEMPOOL_USER_AGENT__=${MEMPOOL_USER_AGENT:=mempool}
|
|||||||
__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__=${MEMPOOL_STDOUT_LOG_MIN_PRIORITY:=info}
|
__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__=${MEMPOOL_STDOUT_LOG_MIN_PRIORITY:=info}
|
||||||
__MEMPOOL_INDEXING_BLOCKS_AMOUNT__=${MEMPOOL_INDEXING_BLOCKS_AMOUNT:=false}
|
__MEMPOOL_INDEXING_BLOCKS_AMOUNT__=${MEMPOOL_INDEXING_BLOCKS_AMOUNT:=false}
|
||||||
__MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__=${MEMPOOL_AUTOMATIC_BLOCK_REINDEXING:=false}
|
__MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__=${MEMPOOL_AUTOMATIC_BLOCK_REINDEXING:=false}
|
||||||
|
__MEMPOOL_POOLS_JSON_URL__=${MEMPOOL_POOLS_JSON_URL:=false}
|
||||||
|
__MEMPOOL_POOLS_JSON_TREE_URL__=${MEMPOOL_POOLS_JSON_TREE_URL:=false}
|
||||||
|
|
||||||
# CORE_RPC
|
# CORE_RPC
|
||||||
__CORE_RPC_HOST__=${CORE_RPC_HOST:=127.0.0.1}
|
__CORE_RPC_HOST__=${CORE_RPC_HOST:=127.0.0.1}
|
||||||
@ -114,6 +116,8 @@ sed -i "s!__MEMPOOL_USER_AGENT__!${__MEMPOOL_USER_AGENT__}!g" mempool-config.jso
|
|||||||
sed -i "s/__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__/${__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__}/g" mempool-config.json
|
sed -i "s/__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__/${__MEMPOOL_STDOUT_LOG_MIN_PRIORITY__}/g" mempool-config.json
|
||||||
sed -i "s/__MEMPOOL_INDEXING_BLOCKS_AMOUNT__/${__MEMPOOL_INDEXING_BLOCKS_AMOUNT__}/g" mempool-config.json
|
sed -i "s/__MEMPOOL_INDEXING_BLOCKS_AMOUNT__/${__MEMPOOL_INDEXING_BLOCKS_AMOUNT__}/g" mempool-config.json
|
||||||
sed -i "s/__MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__/${__MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__}/g" mempool-config.json
|
sed -i "s/__MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__/${__MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__}/g" mempool-config.json
|
||||||
|
sed -i "s/__MEMPOOL_POOLS_JSON_URL__/${__MEMPOOL_POOLS_JSON_URL__}/g" mempool-config.json
|
||||||
|
sed -i "s/__MEMPOOL_POOLS_JSON_TREE_URL__/${__MEMPOOL_POOLS_JSON_TREE_URL__}/g" mempool-config.json
|
||||||
|
|
||||||
sed -i "s/__CORE_RPC_HOST__/${__CORE_RPC_HOST__}/g" mempool-config.json
|
sed -i "s/__CORE_RPC_HOST__/${__CORE_RPC_HOST__}/g" mempool-config.json
|
||||||
sed -i "s/__CORE_RPC_PORT__/${__CORE_RPC_PORT__}/g" mempool-config.json
|
sed -i "s/__CORE_RPC_PORT__/${__CORE_RPC_PORT__}/g" mempool-config.json
|
||||||
|
@ -76,11 +76,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [class]="!widget ? 'bottom-padding' : 'pb-0'" class="container pb-lg-0">
|
<div [class]="!widget ? 'bottom-padding' : 'pb-0'" class="container pb-lg-0">
|
||||||
<div>
|
|
||||||
<div [class]="widget ? 'chart-widget' : 'chart'" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
<div [class]="widget ? 'chart-widget' : 'chart'" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||||
(chartInit)="onChartInit($event)">
|
(chartInit)="onChartInit($event)">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||||
<div class="spinner-border text-light"></div>
|
<div class="spinner-border text-light"></div>
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
|
<app-nodes-channels-map *ngIf="!error" [style]="'channelpage'" [channel]="channelGeo"></app-nodes-channels-map>
|
||||||
|
|
||||||
<div class="box">
|
<div class="box">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute, ParamMap } from '@angular/router';
|
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
import { catchError, switchMap } from 'rxjs/operators';
|
import { catchError, switchMap, tap } from 'rxjs/operators';
|
||||||
import { SeoService } from 'src/app/services/seo.service';
|
import { SeoService } from 'src/app/services/seo.service';
|
||||||
import { LightningApiService } from '../lightning-api.service';
|
import { LightningApiService } from '../lightning-api.service';
|
||||||
|
|
||||||
@ -14,6 +14,7 @@ import { LightningApiService } from '../lightning-api.service';
|
|||||||
export class ChannelComponent implements OnInit {
|
export class ChannelComponent implements OnInit {
|
||||||
channel$: Observable<any>;
|
channel$: Observable<any>;
|
||||||
error: any = null;
|
error: any = null;
|
||||||
|
channelGeo: number[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private lightningApiService: LightningApiService,
|
private lightningApiService: LightningApiService,
|
||||||
@ -29,9 +30,23 @@ export class ChannelComponent implements OnInit {
|
|||||||
this.seoService.setTitle(`Channel: ${params.get('short_id')}`);
|
this.seoService.setTitle(`Channel: ${params.get('short_id')}`);
|
||||||
return this.lightningApiService.getChannel$(params.get('short_id'))
|
return this.lightningApiService.getChannel$(params.get('short_id'))
|
||||||
.pipe(
|
.pipe(
|
||||||
|
tap((data) => {
|
||||||
|
if (!data.node_left.longitude || !data.node_left.latitude ||
|
||||||
|
!data.node_right.longitude || !data.node_right.latitude) {
|
||||||
|
this.channelGeo = [];
|
||||||
|
} else {
|
||||||
|
this.channelGeo = [
|
||||||
|
data.node_left.public_key,
|
||||||
|
data.node_left.alias,
|
||||||
|
data.node_left.longitude, data.node_left.latitude,
|
||||||
|
data.node_right.public_key,
|
||||||
|
data.node_right.alias,
|
||||||
|
data.node_right.longitude, data.node_right.latitude,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}),
|
||||||
catchError((err) => {
|
catchError((err) => {
|
||||||
this.error = err;
|
this.error = err;
|
||||||
console.log(this.error);
|
|
||||||
return of(null);
|
return of(null);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
<div class="row row-cols-1 row-cols-md-2">
|
<div class="row row-cols-1 row-cols-md-2">
|
||||||
|
|
||||||
|
<!-- Network capacity/channels/nodes -->
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="main-title">
|
<div class="main-title">
|
||||||
<span i18n="lightning.statistics-title">Network Statistics</span>
|
<span i18n="lightning.statistics-title">Network Statistics</span>
|
||||||
@ -17,6 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Channels stats -->
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="main-title">
|
<div class="main-title">
|
||||||
<span i18n="lightning.statistics-title">Channels Statistics</span>
|
<span i18n="lightning.statistics-title">Channels Statistics</span>
|
||||||
@ -30,18 +32,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col">
|
<!-- ISP pie chart -->
|
||||||
|
<div class="col" style="margin-bottom: 1.47rem">
|
||||||
|
<div class="card graph-card">
|
||||||
|
<div class="card-body pl-2 pr-2">
|
||||||
|
<app-nodes-per-isp-chart [widget]="true"></app-nodes-per-isp-chart>
|
||||||
|
<div class="mt-1"><a [attr.data-cy]="'pool-distribution-view-more'" [routerLink]="['/graphs/lightning/nodes-per-isp' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="col">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<app-nodes-networks-chart [widget]=true></app-nodes-networks-chart>
|
<app-nodes-networks-chart [widget]=true></app-nodes-networks-chart>
|
||||||
<div class="mt-1"><a [routerLink]="['/graphs/lightning/nodes-networks' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
<div class="mt-1"><a [routerLink]="['/graphs/lightning/nodes-networks' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card">
|
<div class="card graph-card">
|
||||||
<div class="card-body">
|
<div class="card-body pl-2 pr-2">
|
||||||
<app-lightning-statistics-chart [widget]=true></app-lightning-statistics-chart>
|
<app-lightning-statistics-chart [widget]=true></app-lightning-statistics-chart>
|
||||||
<div class="mt-1"><a [routerLink]="['/graphs/lightning/capacity' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
<div class="mt-1"><a [routerLink]="['/graphs/lightning/capacity' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div>
|
||||||
</div>
|
</div>
|
||||||
@ -52,7 +64,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Top Capacity Nodes</h5>
|
<h5 class="card-title">Top Capacity Nodes</h5>
|
||||||
<app-nodes-list [nodes$]="nodesByCapacity$"></app-nodes-list>
|
<app-nodes-list [nodes$]="nodesByCapacity$" [show]="'mobile-capacity'"></app-nodes-list>
|
||||||
<!-- <div><a [routerLink]="['/lightning/nodes' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div> -->
|
<!-- <div><a [routerLink]="['/lightning/nodes' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -62,7 +74,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Most Connected Nodes</h5>
|
<h5 class="card-title">Most Connected Nodes</h5>
|
||||||
<app-nodes-list [nodes$]="nodesByChannels$"></app-nodes-list>
|
<app-nodes-list [nodes$]="nodesByChannels$" [show]="'mobile-channels'"></app-nodes-list>
|
||||||
<!-- <div><a [routerLink]="['/lightning/nodes' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div> -->
|
<!-- <div><a [routerLink]="['/lightning/nodes' | relativeUrl]" i18n="dashboard.view-more">View more »</a></div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,6 +14,13 @@
|
|||||||
background-color: #1d1f31;
|
background-color: #1d1f31;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.graph-card {
|
||||||
|
height: 100%;
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
height: 385px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.card-title {
|
.card-title {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: #4a68b9;
|
color: #4a68b9;
|
||||||
@ -22,9 +29,6 @@
|
|||||||
color: #4a68b9;
|
color: #4a68b9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-body {
|
|
||||||
padding: 1.25rem 1rem 0.75rem 1rem;
|
|
||||||
}
|
|
||||||
.card-body.pool-ranking {
|
.card-body.pool-ranking {
|
||||||
padding: 1.25rem 0.25rem 0.75rem 0.25rem;
|
padding: 1.25rem 0.25rem 0.75rem 0.25rem;
|
||||||
}
|
}
|
||||||
@ -32,6 +36,21 @@
|
|||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#blockchain-container {
|
||||||
|
position: relative;
|
||||||
|
overflow-x: scroll;
|
||||||
|
overflow-y: hidden;
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#blockchain-container::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-border {
|
||||||
|
-webkit-mask-image: linear-gradient(to right, transparent 0%, black 10%, black 80%, transparent 100%)
|
||||||
|
}
|
||||||
|
|
||||||
.main-title {
|
.main-title {
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -45,7 +64,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.more-padding {
|
.more-padding {
|
||||||
padding: 18px;
|
padding: 24px 20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-wrapper {
|
.card-wrapper {
|
||||||
@ -78,3 +97,10 @@
|
|||||||
.card-text {
|
.card-text {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.title-link, .title-link:hover, .title-link:focus, .title-link:active {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
@ -30,21 +30,28 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.widget {
|
.widget {
|
||||||
width: 99vw;
|
width: 90vw;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
height: 250px;
|
height: 250px;
|
||||||
-webkit-mask: linear-gradient(0deg, #11131f00 5%, #11131fff 25%);
|
-webkit-mask: linear-gradient(0deg, #11131f00 5%, #11131fff 25%);
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.widget > .chart {
|
.widget > .chart {
|
||||||
-webkit-mask: linear-gradient(180deg, #11131f00 0%, #11131fff 20%);
|
|
||||||
min-height: 250px;
|
min-height: 250px;
|
||||||
|
-webkit-mask: linear-gradient(180deg, #11131f00 0%, #11131fff 20%);
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
padding-bottom: 0px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart {
|
.chart {
|
||||||
min-height: 500px;
|
min-height: 500px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-right: 10px;
|
|
||||||
@media (max-width: 992px) {
|
@media (max-width: 992px) {
|
||||||
padding-bottom: 25px;
|
padding-bottom: 25px;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.
|
|||||||
import { StateService } from 'src/app/services/state.service';
|
import { StateService } from 'src/app/services/state.service';
|
||||||
import { EChartsOption, registerMap } from 'echarts';
|
import { EChartsOption, registerMap } from 'echarts';
|
||||||
import 'echarts-gl';
|
import 'echarts-gl';
|
||||||
|
import { isMobile } from 'src/app/shared/common.utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-nodes-channels-map',
|
selector: 'app-nodes-channels-map',
|
||||||
@ -16,8 +17,9 @@ import 'echarts-gl';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class NodesChannelsMap implements OnInit, OnDestroy {
|
export class NodesChannelsMap implements OnInit, OnDestroy {
|
||||||
@Input() style: 'graph' | 'nodepage' | 'widget' = 'graph';
|
@Input() style: 'graph' | 'nodepage' | 'widget' | 'channelpage' = 'graph';
|
||||||
@Input() publicKey: string | undefined;
|
@Input() publicKey: string | undefined;
|
||||||
|
@Input() channel: any[] = [];
|
||||||
|
|
||||||
observable$: Observable<any>;
|
observable$: Observable<any>;
|
||||||
|
|
||||||
@ -25,6 +27,8 @@ export class NodesChannelsMap implements OnInit, OnDestroy {
|
|||||||
zoom: number | undefined;
|
zoom: number | undefined;
|
||||||
channelWidth = 0.6;
|
channelWidth = 0.6;
|
||||||
channelOpacity = 0.1;
|
channelOpacity = 0.1;
|
||||||
|
channelColor = '#466d9d';
|
||||||
|
channelCurve = 0;
|
||||||
|
|
||||||
chartInstance = undefined;
|
chartInstance = undefined;
|
||||||
chartOptions: EChartsOption = {};
|
chartOptions: EChartsOption = {};
|
||||||
@ -47,7 +51,14 @@ export class NodesChannelsMap implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.center = this.style === 'widget' ? [0, 40] : [0, 5];
|
this.center = this.style === 'widget' ? [0, 40] : [0, 5];
|
||||||
this.zoom = this.style === 'widget' ? 3.5 : 1.3;
|
this.zoom = 1.3;
|
||||||
|
if (this.style === 'widget' && !isMobile()) {
|
||||||
|
this.zoom = 3.5;
|
||||||
|
}
|
||||||
|
if (this.style === 'widget' && isMobile()) {
|
||||||
|
this.zoom = 1.4;
|
||||||
|
this.center = [0, 10];
|
||||||
|
}
|
||||||
|
|
||||||
if (this.style === 'graph') {
|
if (this.style === 'graph') {
|
||||||
this.seoService.setTitle($localize`Lightning nodes channels world map`);
|
this.seoService.setTitle($localize`Lightning nodes channels world map`);
|
||||||
@ -67,13 +78,29 @@ export class NodesChannelsMap implements OnInit, OnDestroy {
|
|||||||
const nodes = [];
|
const nodes = [];
|
||||||
const nodesPubkeys = {};
|
const nodesPubkeys = {};
|
||||||
let thisNodeGPS: number[] | undefined = undefined;
|
let thisNodeGPS: number[] | undefined = undefined;
|
||||||
for (const channel of data[1]) {
|
|
||||||
|
let geoloc = data[1];
|
||||||
|
if (this.style === 'channelpage') {
|
||||||
|
if (this.channel.length === 0) {
|
||||||
|
geoloc = [];
|
||||||
|
} else {
|
||||||
|
geoloc = [this.channel];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const channel of geoloc) {
|
||||||
if (!thisNodeGPS && data[2] === channel[0]) {
|
if (!thisNodeGPS && data[2] === channel[0]) {
|
||||||
thisNodeGPS = [channel[2], channel[3]];
|
thisNodeGPS = [channel[2], channel[3]];
|
||||||
} else if (!thisNodeGPS && data[2] === channel[4]) {
|
} else if (!thisNodeGPS && data[2] === channel[4]) {
|
||||||
thisNodeGPS = [channel[6], channel[7]];
|
thisNodeGPS = [channel[6], channel[7]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 0 - node1 pubkey
|
||||||
|
// 1 - node1 alias
|
||||||
|
// 2,3 - node1 GPS
|
||||||
|
// 4 - node2 pubkey
|
||||||
|
// 5 - node2 alias
|
||||||
|
// 6,7 - node2 GPS
|
||||||
|
|
||||||
// We add a bit of noise so nodes at the same location are not all
|
// We add a bit of noise so nodes at the same location are not all
|
||||||
// on top of each other
|
// on top of each other
|
||||||
let random = Math.random() * 2 * Math.PI;
|
let random = Math.random() * 2 * Math.PI;
|
||||||
@ -115,6 +142,22 @@ export class NodesChannelsMap implements OnInit, OnDestroy {
|
|||||||
this.channelWidth = 1;
|
this.channelWidth = 1;
|
||||||
this.channelOpacity = 1;
|
this.channelOpacity = 1;
|
||||||
}
|
}
|
||||||
|
if (this.style === 'channelpage' && this.channel.length > 0) {
|
||||||
|
this.channelWidth = 2;
|
||||||
|
this.channelOpacity = 1;
|
||||||
|
this.channelColor = '#bafcff';
|
||||||
|
this.channelCurve = 0.1;
|
||||||
|
this.center = [
|
||||||
|
(this.channel[2] + this.channel[6]) / 2,
|
||||||
|
(this.channel[3] + this.channel[7]) / 2
|
||||||
|
];
|
||||||
|
const distance = Math.sqrt(
|
||||||
|
Math.pow(this.channel[7] - this.channel[3], 2) +
|
||||||
|
Math.pow(this.channel[6] - this.channel[2], 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.zoom = -0.05 * distance + 8;
|
||||||
|
}
|
||||||
|
|
||||||
this.prepareChartOptions(nodes, channelsLoc);
|
this.prepareChartOptions(nodes, channelsLoc);
|
||||||
}));
|
}));
|
||||||
@ -202,8 +245,8 @@ export class NodesChannelsMap implements OnInit, OnDestroy {
|
|||||||
lineStyle: {
|
lineStyle: {
|
||||||
opacity: this.channelOpacity,
|
opacity: this.channelOpacity,
|
||||||
width: this.channelWidth,
|
width: this.channelWidth,
|
||||||
curveness: 0,
|
curveness: this.channelCurve,
|
||||||
color: '#466d9d',
|
color: this.channelColor,
|
||||||
},
|
},
|
||||||
blendMode: 'lighter',
|
blendMode: 'lighter',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
@ -3,18 +3,18 @@
|
|||||||
<table class="table table-borderless">
|
<table class="table table-borderless">
|
||||||
<thead>
|
<thead>
|
||||||
<th class="alias text-left" i18n="nodes.alias">Alias</th>
|
<th class="alias text-left" i18n="nodes.alias">Alias</th>
|
||||||
<th class="capacity text-right" i18n="node.capacity">Capacity</th>
|
<th class="capacity text-right" [class]="show" i18n="node.capacity">Capacity</th>
|
||||||
<th class="channels text-right" i18n="node.channels">Channels</th>
|
<th class="channels text-right" [class]="show" i18n="node.channels">Channels</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody *ngIf="nodes$ | async as nodes; else skeleton">
|
<tbody *ngIf="nodes$ | async as nodes; else skeleton">
|
||||||
<tr *ngFor="let node of nodes; let i = index;">
|
<tr *ngFor="let node of nodes; let i = index;">
|
||||||
<td class="alias text-left">
|
<td class="alias text-left">
|
||||||
<a [routerLink]="['/lightning/node' | relativeUrl, node.public_key]">{{ node.alias }}</a>
|
<a [routerLink]="['/lightning/node' | relativeUrl, node.public_key]">{{ node.alias }}</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="capacity text-right">
|
<td class="capacity text-right" [class]="show">
|
||||||
<app-amount [satoshis]="node.capacity" digitsInfo="1.2-2"></app-amount>
|
<app-amount [satoshis]="node.capacity" digitsInfo="1.2-2"></app-amount>
|
||||||
</td>
|
</td>
|
||||||
<td class="channels text-right">
|
<td class="channels text-right" [class]="show">
|
||||||
{{ node.channels | number }}
|
{{ node.channels | number }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
.capacity.mobile-channels {
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.channels.mobile-capacity {
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ import { Observable } from 'rxjs';
|
|||||||
})
|
})
|
||||||
export class NodesListComponent implements OnInit {
|
export class NodesListComponent implements OnInit {
|
||||||
@Input() nodes$: Observable<any>;
|
@Input() nodes$: Observable<any>;
|
||||||
|
@Input() show: string;
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
|
@ -1,6 +1,29 @@
|
|||||||
<div class="full-container h-100">
|
<div [class]="widget === false ? 'full-container' : ''">
|
||||||
|
|
||||||
<div class="card-header">
|
<div *ngIf="widget">
|
||||||
|
<div class="pool-distribution" *ngIf="(nodesPerAsObservable$ | async) as stats; else loadingReward">
|
||||||
|
<div class="item">
|
||||||
|
<h5 class="card-title d-inline-block" i18n="lightning.tagged-isp">Tagged ISPs</h5>
|
||||||
|
<p class="card-text">
|
||||||
|
{{ stats.taggedISP }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<h5 class="card-title d-inline-block" i18n="lightning.tagged-capacity">Tagged capacity</h5>
|
||||||
|
<p class="card-text" i18n-ngbTooltip="mining.blocks-count-desc">
|
||||||
|
<app-amount [satoshis]="stats.taggedCapacity" [digitsInfo]="'1.2-2'" [noFiat]="true"></app-amount>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<h5 class="card-title d-inline-block" i18n="lightning.tagged-nodes">Tagged nodes</h5>
|
||||||
|
<p class="card-text" i18n-ngbTooltip="mining.pools-count-desc">
|
||||||
|
{{ stats.taggedNodeCount }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-header" *ngIf="!widget">
|
||||||
<div class="d-flex d-md-block align-items-baseline" style="margin-bottom: -5px">
|
<div class="d-flex d-md-block align-items-baseline" style="margin-bottom: -5px">
|
||||||
<span i18n="lightning.nodes-per-isp">Lightning nodes per ISP</span>
|
<span i18n="lightning.nodes-per-isp">Lightning nodes per ISP</span>
|
||||||
<button class="btn p-0 pl-2" style="margin: 0 0 4px 0px" (click)="onSaveChart()">
|
<button class="btn p-0 pl-2" style="margin: 0 0 4px 0px" (click)="onSaveChart()">
|
||||||
@ -12,23 +35,21 @@
|
|||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container pb-lg-0 bottom-padding">
|
<div [class]="!widget ? 'bottom-padding' : 'pb-0'" class="container pb-lg-0">
|
||||||
<div class="pb-lg-5" *ngIf="nodesPerAsObservable$ | async">
|
<div [class]="widget ? 'chart-widget' : 'chart'" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
||||||
<div class="chart w-100" echarts [initOpts]="chartInitOptions" [options]="chartOptions"
|
|
||||||
(chartInit)="onChartInit($event)">
|
(chartInit)="onChartInit($event)">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
<div class="text-center loadingGraphs" *ngIf="isLoading">
|
||||||
<div class="spinner-border text-light"></div>
|
<div class="spinner-border text-light"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex toggle">
|
<div class="d-flex toggle" *ngIf="!widget">
|
||||||
<app-toggle [textLeft]="'Show Tor'" [textRight]="" (toggleStatusChanged)="onTorToggleStatusChanged($event)"></app-toggle>
|
<app-toggle [textLeft]="'Show Tor'" [textRight]="" (toggleStatusChanged)="onTorToggleStatusChanged($event)"></app-toggle>
|
||||||
<app-toggle [textLeft]="'Nodes'" [textRight]="'Capacity'" (toggleStatusChanged)="onGroupToggleStatusChanged($event)"></app-toggle>
|
<app-toggle [textLeft]="'Nodes'" [textRight]="'Capacity'" (toggleStatusChanged)="onGroupToggleStatusChanged($event)"></app-toggle>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="table table-borderless text-center m-auto" style="max-width: 900px">
|
<table class="table table-borderless text-center m-auto" style="max-width: 900px" *ngIf="!widget">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="rank text-left pl-0" i18n="mining.rank">Rank</th>
|
<th class="rank text-left pl-0" i18n="mining.rank">Rank</th>
|
||||||
@ -39,7 +60,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody [attr.data-cy]="'pools-table'" *ngIf="(nodesPerAsObservable$ | async) as asList">
|
<tbody [attr.data-cy]="'pools-table'" *ngIf="(nodesPerAsObservable$ | async) as asList">
|
||||||
<tr *ngFor="let asEntry of asList">
|
<tr *ngFor="let asEntry of asList.data">
|
||||||
<td class="rank text-left pl-0">{{ asEntry.rank }}</td>
|
<td class="rank text-left pl-0">{{ asEntry.rank }}</td>
|
||||||
<td class="name text-left text-truncate">
|
<td class="name text-left text-truncate">
|
||||||
<a *ngIf="asEntry.ispId" [routerLink]="[('/lightning/nodes/isp/' + asEntry.ispId) | relativeUrl]">{{ asEntry.name }}</a>
|
<a *ngIf="asEntry.ispId" [routerLink]="[('/lightning/nodes/isp/' + asEntry.ispId) | relativeUrl]">{{ asEntry.name }}</a>
|
||||||
@ -54,3 +75,26 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ng-template #loadingReward>
|
||||||
|
<div class="pool-distribution">
|
||||||
|
<div class="item">
|
||||||
|
<h5 class="card-title" i18n="lightning.tagged-isp">Tagged ISPs</h5>
|
||||||
|
<p class="card-text">
|
||||||
|
<span class="skeleton-loader skeleton-loader-big"></span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<h5 class="card-title" i18n="lightning.tagged-capacity">Tagged capacity</h5>
|
||||||
|
<p class="card-text">
|
||||||
|
<span class="skeleton-loader skeleton-loader-big"></span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<h5 class="card-title" i18n="lightning.tagged-nodes">Tagged nodes</h5>
|
||||||
|
<p class="card-text">
|
||||||
|
<span class="skeleton-loader skeleton-loader-big"></span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
@ -22,7 +22,40 @@
|
|||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
@media (max-width: 767.98px) {
|
@media (max-width: 767.98px) {
|
||||||
max-height: 230px;
|
max-height: 230px;
|
||||||
margin-top: -35px;
|
margin-top: -40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.chart-widget {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
height: 240px;
|
||||||
|
@media (max-width: 485px) {
|
||||||
|
max-height: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.formRadioGroup {
|
||||||
|
margin-top: 6px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
@media (min-width: 991px) {
|
||||||
|
position: relative;
|
||||||
|
top: -65px;
|
||||||
|
}
|
||||||
|
@media (min-width: 830px) and (max-width: 991px) {
|
||||||
|
position: relative;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
@media (min-width: 830px) {
|
||||||
|
flex-direction: row;
|
||||||
|
float: right;
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
.btn-sm {
|
||||||
|
font-size: 9px;
|
||||||
|
@media (min-width: 830px) {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,6 +68,79 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
.pools-table th,
|
||||||
|
.pools-table td {
|
||||||
|
padding: .3em !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loadingGraphs {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: calc(50% - 15px);
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pool-distribution {
|
||||||
|
min-height: 56px;
|
||||||
|
display: block;
|
||||||
|
@media (min-width: 485px) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
h5 {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.item {
|
||||||
|
max-width: 160px;
|
||||||
|
width: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0px auto 20px;
|
||||||
|
&:nth-child(2) {
|
||||||
|
order: 2;
|
||||||
|
@media (min-width: 485px) {
|
||||||
|
order: 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:nth-child(3) {
|
||||||
|
width: 50%;
|
||||||
|
order: 3;
|
||||||
|
@media (min-width: 485px) {
|
||||||
|
order: 2;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.card-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #4a68b9;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.card-text {
|
||||||
|
font-size: 18px;
|
||||||
|
span {
|
||||||
|
color: #ffffff66;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-loader {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
max-width: 80px;
|
||||||
|
margin: 15px auto 3px;
|
||||||
|
}
|
||||||
|
|
||||||
.rank {
|
.rank {
|
||||||
width: 15%;
|
width: 15%;
|
||||||
@media (max-width: 576px) {
|
@media (max-width: 576px) {
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit, HostBinding, NgZone } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit, HostBinding, NgZone, Input } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { EChartsOption, PieSeriesOption } from 'echarts';
|
import { EChartsOption, PieSeriesOption } from 'echarts';
|
||||||
import { combineLatest, map, Observable, share, Subject, switchMap, tap } from 'rxjs';
|
import { combineLatest, map, Observable, share, startWith, Subject, switchMap, tap } from 'rxjs';
|
||||||
import { chartColors } from 'src/app/app.constants';
|
import { chartColors } from 'src/app/app.constants';
|
||||||
import { ApiService } from 'src/app/services/api.service';
|
import { ApiService } from 'src/app/services/api.service';
|
||||||
import { SeoService } from 'src/app/services/seo.service';
|
import { SeoService } from 'src/app/services/seo.service';
|
||||||
import { StateService } from 'src/app/services/state.service';
|
import { StateService } from 'src/app/services/state.service';
|
||||||
|
import { isMobile } from 'src/app/shared/common.utils';
|
||||||
import { download } from 'src/app/shared/graphs.utils';
|
import { download } from 'src/app/shared/graphs.utils';
|
||||||
import { AmountShortenerPipe } from 'src/app/shared/pipes/amount-shortener.pipe';
|
import { AmountShortenerPipe } from 'src/app/shared/pipes/amount-shortener.pipe';
|
||||||
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe';
|
||||||
@ -17,6 +18,8 @@ import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class NodesPerISPChartComponent implements OnInit {
|
export class NodesPerISPChartComponent implements OnInit {
|
||||||
|
@Input() widget: boolean = false;
|
||||||
|
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
chartOptions: EChartsOption = {};
|
chartOptions: EChartsOption = {};
|
||||||
chartInitOptions = {
|
chartInitOptions = {
|
||||||
@ -46,7 +49,11 @@ export class NodesPerISPChartComponent implements OnInit {
|
|||||||
this.seoService.setTitle($localize`Lightning nodes per ISP`);
|
this.seoService.setTitle($localize`Lightning nodes per ISP`);
|
||||||
|
|
||||||
this.showTorObservable$ = this.showTorSubject.asObservable();
|
this.showTorObservable$ = this.showTorSubject.asObservable();
|
||||||
this.nodesPerAsObservable$ = combineLatest([this.groupBySubject, this.showTorSubject])
|
|
||||||
|
this.nodesPerAsObservable$ = combineLatest([
|
||||||
|
this.groupBySubject.pipe(startWith(false)),
|
||||||
|
this.showTorSubject.pipe(startWith(false)),
|
||||||
|
])
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((selectedFilters) => {
|
switchMap((selectedFilters) => {
|
||||||
return this.apiService.getNodesPerAs(
|
return this.apiService.getNodesPerAs(
|
||||||
@ -62,23 +69,41 @@ export class NodesPerISPChartComponent implements OnInit {
|
|||||||
for (let i = 0; i < data.length; ++i) {
|
for (let i = 0; i < data.length; ++i) {
|
||||||
data[i].rank = i + 1;
|
data[i].rank = i + 1;
|
||||||
}
|
}
|
||||||
return data.slice(0, 100);
|
return {
|
||||||
|
taggedISP: data.length,
|
||||||
|
taggedCapacity: data.reduce((partialSum, isp) => partialSum + isp.capacity, 0),
|
||||||
|
taggedNodeCount: data.reduce((partialSum, isp) => partialSum + isp.count, 0),
|
||||||
|
data: data.slice(0, 100),
|
||||||
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
share()
|
share()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (this.widget) {
|
||||||
|
this.showTorSubject.next(false);
|
||||||
|
this.groupBySubject.next(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
generateChartSerieData(as): PieSeriesOption[] {
|
generateChartSerieData(as): PieSeriesOption[] {
|
||||||
const shareThreshold = this.isMobile() ? 2 : 0.5;
|
let shareThreshold = 0.5;
|
||||||
|
if (this.widget && isMobile() || isMobile()) {
|
||||||
|
shareThreshold = 1;
|
||||||
|
} else if (this.widget) {
|
||||||
|
shareThreshold = 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
const data: object[] = [];
|
const data: object[] = [];
|
||||||
let totalShareOther = 0;
|
let totalShareOther = 0;
|
||||||
let totalNodeOther = 0;
|
let totalNodeOther = 0;
|
||||||
|
|
||||||
let edgeDistance: string | number = '10%';
|
let edgeDistance: string | number = '10%';
|
||||||
if (this.isMobile()) {
|
if (isMobile() && this.widget) {
|
||||||
edgeDistance = 0;
|
edgeDistance = 0;
|
||||||
|
} else if (isMobile() && !this.widget || this.widget) {
|
||||||
|
edgeDistance = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
as.forEach((as) => {
|
as.forEach((as) => {
|
||||||
@ -92,15 +117,16 @@ export class NodesPerISPChartComponent implements OnInit {
|
|||||||
color: as.ispId === null ? '#7D4698' : undefined,
|
color: as.ispId === null ? '#7D4698' : undefined,
|
||||||
},
|
},
|
||||||
value: as.share,
|
value: as.share,
|
||||||
name: as.name + (this.isMobile() ? `` : ` (${as.share}%)`),
|
name: as.name + (isMobile() || this.widget ? `` : ` (${as.share}%)`),
|
||||||
label: {
|
label: {
|
||||||
overflow: 'truncate',
|
overflow: 'truncate',
|
||||||
|
width: isMobile() ? 75 : this.widget ? 125 : 250,
|
||||||
color: '#b1b1b1',
|
color: '#b1b1b1',
|
||||||
alignTo: 'edge',
|
alignTo: 'edge',
|
||||||
edgeDistance: edgeDistance,
|
edgeDistance: edgeDistance,
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
show: !this.isMobile(),
|
show: !isMobile(),
|
||||||
backgroundColor: 'rgba(17, 19, 31, 1)',
|
backgroundColor: 'rgba(17, 19, 31, 1)',
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
@ -125,7 +151,7 @@ export class NodesPerISPChartComponent implements OnInit {
|
|||||||
color: 'grey',
|
color: 'grey',
|
||||||
},
|
},
|
||||||
value: totalShareOther,
|
value: totalShareOther,
|
||||||
name: 'Other' + (this.isMobile() ? `` : ` (${totalShareOther.toFixed(2)}%)`),
|
name: 'Other' + (isMobile() || this.widget ? `` : ` (${totalShareOther.toFixed(2)}%)`),
|
||||||
label: {
|
label: {
|
||||||
overflow: 'truncate',
|
overflow: 'truncate',
|
||||||
color: '#b1b1b1',
|
color: '#b1b1b1',
|
||||||
@ -153,7 +179,7 @@ export class NodesPerISPChartComponent implements OnInit {
|
|||||||
|
|
||||||
prepareChartOptions(as): void {
|
prepareChartOptions(as): void {
|
||||||
let pieSize = ['20%', '80%']; // Desktop
|
let pieSize = ['20%', '80%']; // Desktop
|
||||||
if (this.isMobile()) {
|
if (isMobile() && !this.widget) {
|
||||||
pieSize = ['15%', '60%'];
|
pieSize = ['15%', '60%'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,8 +203,8 @@ export class NodesPerISPChartComponent implements OnInit {
|
|||||||
lineStyle: {
|
lineStyle: {
|
||||||
width: 2,
|
width: 2,
|
||||||
},
|
},
|
||||||
length: this.isMobile() ? 1 : 20,
|
length: isMobile() ? 1 : 20,
|
||||||
length2: this.isMobile() ? 1 : undefined,
|
length2: isMobile() ? 1 : undefined,
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
@ -204,10 +230,6 @@ export class NodesPerISPChartComponent implements OnInit {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
isMobile(): boolean {
|
|
||||||
return (window.innerWidth <= 767.98);
|
|
||||||
}
|
|
||||||
|
|
||||||
onChartInit(ec): void {
|
onChartInit(ec): void {
|
||||||
if (this.chartInstance !== undefined) {
|
if (this.chartInstance !== undefined) {
|
||||||
return;
|
return;
|
||||||
@ -244,5 +266,9 @@ export class NodesPerISPChartComponent implements OnInit {
|
|||||||
onGroupToggleStatusChanged(e): void {
|
onGroupToggleStatusChanged(e): void {
|
||||||
this.groupBySubject.next(e);
|
this.groupBySubject.next(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isEllipsisActive(e) {
|
||||||
|
return (e.offsetWidth < e.scrollWidth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,8 +51,7 @@
|
|||||||
}
|
}
|
||||||
.chart-widget {
|
.chart-widget {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 320px;
|
||||||
max-height: 270px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.formRadioGroup {
|
.formRadioGroup {
|
||||||
|
@ -34,10 +34,11 @@ esac
|
|||||||
TOR_INSTALL=ON
|
TOR_INSTALL=ON
|
||||||
CERTBOT_INSTALL=ON
|
CERTBOT_INSTALL=ON
|
||||||
|
|
||||||
# install 3 network daemons
|
# install 4 network daemons
|
||||||
BITCOIN_INSTALL=ON
|
BITCOIN_INSTALL=ON
|
||||||
BISQ_INSTALL=ON
|
BISQ_INSTALL=ON
|
||||||
ELEMENTS_INSTALL=ON
|
ELEMENTS_INSTALL=ON
|
||||||
|
CLN_INSTALL=ON
|
||||||
|
|
||||||
# install UNFURL
|
# install UNFURL
|
||||||
UNFURL_INSTALL=ON
|
UNFURL_INSTALL=ON
|
||||||
@ -191,6 +192,7 @@ case $OS in
|
|||||||
NGINX_ETC_FOLDER=/usr/local/etc/nginx
|
NGINX_ETC_FOLDER=/usr/local/etc/nginx
|
||||||
NGINX_CONFIGURATION=/usr/local/etc/nginx/nginx.conf
|
NGINX_CONFIGURATION=/usr/local/etc/nginx/nginx.conf
|
||||||
CERTBOT_PKG=py39-certbot
|
CERTBOT_PKG=py39-certbot
|
||||||
|
CLN_PKG=c-lightning
|
||||||
;;
|
;;
|
||||||
|
|
||||||
Debian)
|
Debian)
|
||||||
@ -275,6 +277,12 @@ ELECTRS_LIQUID_DATA=${ELECTRS_DATA_ROOT}/liquid
|
|||||||
ELECTRS_LIQUIDTESTNET_ZPOOL=${ZPOOL}
|
ELECTRS_LIQUIDTESTNET_ZPOOL=${ZPOOL}
|
||||||
ELECTRS_LIQUIDTESTNET_DATA=${ELECTRS_DATA_ROOT}/liquidtestnet
|
ELECTRS_LIQUIDTESTNET_DATA=${ELECTRS_DATA_ROOT}/liquidtestnet
|
||||||
|
|
||||||
|
# Core Lightning user/group
|
||||||
|
CLN_USER=cln
|
||||||
|
CLN_GROUP=cln
|
||||||
|
# Core Lightning home folder
|
||||||
|
CLN_HOME=/cln
|
||||||
|
|
||||||
# bisq user/group
|
# bisq user/group
|
||||||
BISQ_USER=bisq
|
BISQ_USER=bisq
|
||||||
BISQ_GROUP=bisq
|
BISQ_GROUP=bisq
|
||||||
@ -596,6 +604,10 @@ zfsCreateFilesystems()
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "${CLN_INSTALL}" = ON ];then
|
||||||
|
zfs create -o "mountpoint=${CLN_HOME}" "${ZPOOL}/cln"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "${BISQ_INSTALL}" = ON ];then
|
if [ "${BISQ_INSTALL}" = ON ];then
|
||||||
zfs create -o "mountpoint=${BISQ_HOME}" "${ZPOOL}/bisq"
|
zfs create -o "mountpoint=${BISQ_HOME}" "${ZPOOL}/bisq"
|
||||||
fi
|
fi
|
||||||
@ -675,6 +687,10 @@ ext4CreateDir()
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "${CLN_INSTALL}" = ON ];then
|
||||||
|
mkdir -p "${CLN_HOME}"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "${BISQ_INSTALL}" = ON ];then
|
if [ "${BISQ_INSTALL}" = ON ];then
|
||||||
mkdir -p "${BISQ_HOME}"
|
mkdir -p "${BISQ_HOME}"
|
||||||
fi
|
fi
|
||||||
@ -735,6 +751,7 @@ Testnet:Enable Bitcoin Testnet:ON
|
|||||||
Signet:Enable Bitcoin Signet:ON
|
Signet:Enable Bitcoin Signet:ON
|
||||||
Liquid:Enable Elements Liquid:ON
|
Liquid:Enable Elements Liquid:ON
|
||||||
Liquidtestnet:Enable Elements Liquidtestnet:ON
|
Liquidtestnet:Enable Elements Liquidtestnet:ON
|
||||||
|
CoreLN:Enable Core Lightning:ON
|
||||||
Bisq:Enable Bisq:ON
|
Bisq:Enable Bisq:ON
|
||||||
Unfurl:Enable Unfurl:ON
|
Unfurl:Enable Unfurl:ON
|
||||||
EOF
|
EOF
|
||||||
@ -810,6 +827,11 @@ else
|
|||||||
ELEMENTS_INSTALL=OFF
|
ELEMENTS_INSTALL=OFF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if grep CoreLN $tempfile >/dev/null 2>&1;then
|
||||||
|
CLN_INSTALL=ON
|
||||||
|
else
|
||||||
|
CLN_INSTALL=OFF
|
||||||
|
|
||||||
if [ "${BITCOIN_MAINNET_ENABLE}" = ON -o "${BITCOIN_TESTNET_ENABLE}" = ON -o "${BITCOIN_SIGNET_ENABLE}" = ON ];then
|
if [ "${BITCOIN_MAINNET_ENABLE}" = ON -o "${BITCOIN_TESTNET_ENABLE}" = ON -o "${BITCOIN_SIGNET_ENABLE}" = ON ];then
|
||||||
BITCOIN_ELECTRS_INSTALL=ON
|
BITCOIN_ELECTRS_INSTALL=ON
|
||||||
else
|
else
|
||||||
@ -1234,6 +1256,33 @@ if [ "${ELEMENTS_ELECTRS_INSTALL}" = ON ];then
|
|||||||
osSudo "${ELEMENTS_USER}" sh -c "cd ${ELEMENTS_ELECTRS_HOME} && cargo run --release --features liquid --bin electrs -- --network liquid --version" || true
|
osSudo "${ELEMENTS_USER}" sh -c "cd ${ELEMENTS_ELECTRS_HOME} && cargo run --release --features liquid --bin electrs -- --network liquid --version" || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
# Core Lightning for Bitcoin Mainnet #
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
echo "[*] Installing Core Lightning"
|
||||||
|
case $OS in
|
||||||
|
FreeBSD)
|
||||||
|
echo "[*] Creating Core Lightning user"
|
||||||
|
osGroupCreate "${CLN_GROUP}"
|
||||||
|
osUserCreate "${CLN_USER}" "${CLN_HOME}" "${CLN_GROUP}"
|
||||||
|
osSudo "${ROOT_USER}" chsh -s `which zsh` "${CLN_USER}"
|
||||||
|
osSudo "${CLN_USER}" touch "${CLN_HOME}/.zshrc"
|
||||||
|
osSudo "${ROOT_USER}" chown -R "${CLN_USER}:${CLN_GROUP}" "${CLN_HOME}"
|
||||||
|
|
||||||
|
echo "[*] Installing Core Lightning package"
|
||||||
|
osPackageInstall ${CLN_PKG}
|
||||||
|
|
||||||
|
echo "[*] Installing Core Lightning mainnet Cronjob"
|
||||||
|
crontab_cln+='@reboot sleep 30 ; screen -dmS main lightningd --alias `hostname` --bitcoin-datadir /bitcoin\n'
|
||||||
|
crontab_cln+='@reboot sleep 60 ; screen -dmS sig lightningd --alias `hostname` --bitcoin-datadir /bitcoin --network signet\n'
|
||||||
|
crontab_cln+='@reboot sleep 90 ; screen -dmS tes lightningd --alias `hostname` --bitcoin-datadir /bitcoin --network testnet\n'
|
||||||
|
echo "${crontab_cln}" | crontab -u "${CLN_USER}" -
|
||||||
|
;;
|
||||||
|
Debian)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
#####################
|
#####################
|
||||||
# Bisq installation #
|
# Bisq installation #
|
||||||
#####################
|
#####################
|
||||||
|
Loading…
x
Reference in New Issue
Block a user