Merge branch 'master' into nymkappa/feature/align-dashboards

This commit is contained in:
softsimon 2023-03-01 06:31:25 +04:00 committed by GitHub
commit 0929d53c56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 5527 additions and 2485 deletions

View File

@ -9,7 +9,7 @@ jobs:
if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')" if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')"
strategy: strategy:
matrix: matrix:
node: ["16.16.0", "18.14.1", "19.6.1"] node: ["16.16.0", "18.14.1"]
flavor: ["dev", "prod"] flavor: ["dev", "prod"]
fail-fast: false fail-fast: false
runs-on: "ubuntu-latest" runs-on: "ubuntu-latest"
@ -55,7 +55,7 @@ jobs:
if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')" if: "!contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')"
strategy: strategy:
matrix: matrix:
node: ["16.16.0", "18.14.1", "19.6.1"] node: ["16.16.0", "18.14.1"]
flavor: ["dev", "prod"] flavor: ["dev", "prod"]
fail-fast: false fail-fast: false
runs-on: "ubuntu-latest" runs-on: "ubuntu-latest"

View File

@ -160,7 +160,7 @@ npm install -g ts-node nodemon
Then, run the watcher: Then, run the watcher:
``` ```
nodemon src/index.ts --ignore cache/ --ignore pools.json nodemon src/index.ts --ignore cache/
``` ```
`nodemon` should be in npm's global binary folder. If needed, you can determine where that is with `npm -g bin`. `nodemon` should be in npm's global binary folder. If needed, you can determine where that is with `npm -g bin`.
@ -219,6 +219,16 @@ Generate block at regular interval (every 10 seconds in this example):
watch -n 10 "./src/bitcoin-cli -regtest -rpcport=8332 generatetoaddress 1 $address" watch -n 10 "./src/bitcoin-cli -regtest -rpcport=8332 generatetoaddress 1 $address"
``` ```
### Mining pools update
By default, mining pools will be not automatically updated regularly (`config.MEMPOOL.AUTOMATIC_BLOCK_REINDEXING` is set to `false`).
To manually update your mining pools, you can use the `--update-pools` command line flag when you run the nodejs backend. For example `npm run start --update-pools`. This will trigger the mining pools update and automatically re-index appropriate blocks.
You can enabled the automatic mining pools update by settings `config.MEMPOOL.AUTOMATIC_BLOCK_REINDEXING` to `true` in your `mempool-config.json`.
When a `coinbase tag` or `coinbase address` change is detected, all blocks tagged to the `unknown` mining pools (starting from height 130635) will be deleted from the `blocks` table. Additionaly, all blocks which were tagged to the pool which has been updated will also be deleted from the `blocks` table. Of course, those blocks will be automatically reindexed.
### Re-index tables ### Re-index tables
You can manually force the nodejs backend to drop all data from a specified set of tables for future re-index. This is mostly useful for the mining dashboard and the lightning explorer. You can manually force the nodejs backend to drop all data from a specified set of tables for future re-index. This is mostly useful for the mining dashboard and the lightning explorer.

View File

@ -22,7 +22,7 @@
"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_URL": "https://raw.githubusercontent.com/mempool/mining-pools/master/pools-v2.json",
"POOLS_JSON_TREE_URL": "https://api.github.com/repos/mempool/mining-pools/git/trees/master", "POOLS_JSON_TREE_URL": "https://api.github.com/repos/mempool/mining-pools/git/trees/master",
"AUDIT": false, "AUDIT": false,
"ADVANCED_GBT_AUDIT": false, "ADVANCED_GBT_AUDIT": false,

View File

@ -7,7 +7,7 @@
"HTTP_PORT": 1, "HTTP_PORT": 1,
"SPAWN_CLUSTER_PROCS": 2, "SPAWN_CLUSTER_PROCS": 2,
"API_URL_PREFIX": "__MEMPOOL_API_URL_PREFIX__", "API_URL_PREFIX": "__MEMPOOL_API_URL_PREFIX__",
"AUTOMATIC_BLOCK_REINDEXING": true, "AUTOMATIC_BLOCK_REINDEXING": false,
"POLL_RATE_MS": 3, "POLL_RATE_MS": 3,
"CACHE_DIR": "__MEMPOOL_CACHE_DIR__", "CACHE_DIR": "__MEMPOOL_CACHE_DIR__",
"CLEAR_PROTECTION_MINUTES": 4, "CLEAR_PROTECTION_MINUTES": 4,

View File

@ -119,7 +119,8 @@ class Audit {
} }
const numCensored = Object.keys(isCensored).length; const numCensored = Object.keys(isCensored).length;
const score = matches.length > 0 ? (matches.length / (matches.length + numCensored)) : 0; const numMatches = matches.length - 1; // adjust for coinbase tx
const score = numMatches > 0 ? (numMatches / (numMatches + numCensored)) : 0;
return { return {
censored: Object.keys(isCensored), censored: Object.keys(isCensored),

View File

@ -237,14 +237,21 @@ export class Common {
].join('x'); ].join('x');
} }
static utcDateToMysql(date?: number): string { static utcDateToMysql(date?: number | null): string | null {
if (date === null) {
return null;
}
const d = new Date((date || 0) * 1000); const d = new Date((date || 0) * 1000);
return d.toISOString().split('T')[0] + ' ' + d.toTimeString().split(' ')[0]; return d.toISOString().split('T')[0] + ' ' + d.toTimeString().split(' ')[0];
} }
static findSocketNetwork(addr: string): {network: string | null, url: string} { static findSocketNetwork(addr: string): {network: string | null, url: string} {
let network: string | null = null; let network: string | null = null;
let url = addr.split('://')[1]; let url: string = addr;
if (config.LIGHTNING.BACKEND === 'cln') {
url = addr.split('://')[1];
}
if (!url) { if (!url) {
return { return {
@ -261,7 +268,7 @@ export class Common {
} }
} else if (addr.indexOf('i2p') !== -1) { } else if (addr.indexOf('i2p') !== -1) {
network = 'i2p'; network = 'i2p';
} else if (addr.indexOf('ipv4') !== -1) { } else if (addr.indexOf('ipv4') !== -1 || (config.LIGHTNING.BACKEND === 'lnd' && isIP(url.split(':')[0]) === 4)) {
const ipv = isIP(url.split(':')[0]); const ipv = isIP(url.split(':')[0]);
if (ipv === 4) { if (ipv === 4) {
network = 'ipv4'; network = 'ipv4';
@ -271,7 +278,7 @@ export class Common {
url: addr, url: addr,
}; };
} }
} else if (addr.indexOf('ipv6') !== -1) { } else if (addr.indexOf('ipv6') !== -1 || (config.LIGHTNING.BACKEND === 'lnd' && url.indexOf(']:'))) {
url = url.split('[')[1].split(']')[0]; url = url.split('[')[1].split(']')[0];
const ipv = isIP(url); const ipv = isIP(url);
if (ipv === 6) { if (ipv === 6) {

View File

@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository';
import { RowDataPacket } from 'mysql2'; import { RowDataPacket } from 'mysql2';
class DatabaseMigration { class DatabaseMigration {
private static currentVersion = 56; private static currentVersion = 57;
private queryTimeout = 3600_000; private queryTimeout = 3600_000;
private statisticsAddedIndexed = false; private statisticsAddedIndexed = false;
private uniqueLogs: string[] = []; private uniqueLogs: string[] = [];
@ -500,6 +500,11 @@ class DatabaseMigration {
this.uniqueLog(logger.notice, '`pools` table has been truncated`'); this.uniqueLog(logger.notice, '`pools` table has been truncated`');
await this.updateToSchemaVersion(56); await this.updateToSchemaVersion(56);
} }
if (databaseSchemaVersion < 57) {
await this.$executeQuery(`ALTER TABLE nodes MODIFY updated_at datetime NULL`);
await this.updateToSchemaVersion(57);
}
} }
/** /**
@ -1012,26 +1017,16 @@ class DatabaseMigration {
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
} }
public async $truncateIndexedData(tables: string[]) { public async $blocksReindexingTruncate(): Promise<void> {
const allowedTables = ['blocks', 'hashrates', 'prices']; logger.warn(`Truncating pools, blocks and hashrates for re-indexing (using '--reindex-blocks'). You can cancel this command within 5 seconds`);
await Common.sleep$(5000);
try { await this.$executeQuery(`TRUNCATE blocks`);
for (const table of tables) { await this.$executeQuery(`TRUNCATE hashrates`);
if (!allowedTables.includes(table)) { await this.$executeQuery('DELETE FROM `pools`');
logger.debug(`Table ${table} cannot to be re-indexed (not allowed)`); await this.$executeQuery('ALTER TABLE pools AUTO_INCREMENT = 1');
continue; await this.$executeQuery(`UPDATE state SET string = NULL WHERE name = 'pools_json_sha'`);
} }
await this.$executeQuery(`TRUNCATE ${table}`, true);
if (table === 'hashrates') {
await this.$executeQuery('UPDATE state set number = 0 where name = "last_hashrates_indexing"', true);
}
logger.notice(`Table ${table} has been truncated`);
}
} catch (e) {
logger.warn(`Unable to erase indexed data`);
}
}
private async $convertCompactCpfpTables(): Promise<void> { private async $convertCompactCpfpTables(): Promise<void> {
try { try {

View File

@ -559,6 +559,17 @@ class ChannelsApi {
const policy1: Partial<ILightningApi.RoutingPolicy> = channel.node1_policy || {}; const policy1: Partial<ILightningApi.RoutingPolicy> = channel.node1_policy || {};
const policy2: Partial<ILightningApi.RoutingPolicy> = channel.node2_policy || {}; const policy2: Partial<ILightningApi.RoutingPolicy> = channel.node2_policy || {};
// https://github.com/mempool/mempool/issues/3006
if ((channel.last_update ?? 0) < 1514736061) { // January 1st 2018
channel.last_update = null;
}
if ((policy1.last_update ?? 0) < 1514736061) { // January 1st 2018
policy1.last_update = null;
}
if ((policy2.last_update ?? 0) < 1514736061) { // January 1st 2018
policy2.last_update = null;
}
const query = `INSERT INTO channels const query = `INSERT INTO channels
( (
id, id,

View File

@ -642,6 +642,11 @@ class NodesApi {
*/ */
public async $saveNode(node: ILightningApi.Node): Promise<void> { public async $saveNode(node: ILightningApi.Node): Promise<void> {
try { try {
// https://github.com/mempool/mempool/issues/3006
if ((node.last_update ?? 0) < 1514736061) { // January 1st 2018
node.last_update = null;
}
const sockets = (node.addresses?.map(a => a.addr).join(',')) ?? ''; const sockets = (node.addresses?.map(a => a.addr).join(',')) ?? '';
const query = `INSERT INTO nodes( const query = `INSERT INTO nodes(
public_key, public_key,

View File

@ -21,7 +21,7 @@ export namespace ILightningApi {
export interface Channel { export interface Channel {
channel_id: string; channel_id: string;
chan_point: string; chan_point: string;
last_update: number; last_update: number | null;
node1_pub: string; node1_pub: string;
node2_pub: string; node2_pub: string;
capacity: string; capacity: string;
@ -36,11 +36,11 @@ export namespace ILightningApi {
fee_rate_milli_msat: string; fee_rate_milli_msat: string;
disabled: boolean; disabled: boolean;
max_htlc_msat: string; max_htlc_msat: string;
last_update: number; last_update: number | null;
} }
export interface Node { export interface Node {
last_update: number; last_update: number | null;
pub_key: string; pub_key: string;
alias: string; alias: string;
addresses: { addresses: {

View File

@ -73,7 +73,7 @@ class PoolsParser {
} }
} }
logger.info('Mining pools.json import completed'); logger.info('Mining pools-v2.json import completed');
} }
/** /**
@ -115,7 +115,7 @@ class PoolsParser {
return; return;
} }
// Get oldest blocks mined by the pool and assume pools.json updates only concern most recent years // Get oldest blocks mined by the pool and assume pools-v2.json updates only concern most recent years
// Ignore early days of Bitcoin as there were no mining pool yet // Ignore early days of Bitcoin as there were no mining pool yet
const [oldestPoolBlock]: any[] = await DB.query(` const [oldestPoolBlock]: any[] = await DB.query(`
SELECT height SELECT height

View File

@ -36,7 +36,6 @@ import bitcoinRoutes from './api/bitcoin/bitcoin.routes';
import fundingTxFetcher from './tasks/lightning/sync-tasks/funding-tx-fetcher'; import fundingTxFetcher from './tasks/lightning/sync-tasks/funding-tx-fetcher';
import forensicsService from './tasks/lightning/forensics.service'; import forensicsService from './tasks/lightning/forensics.service';
import priceUpdater from './tasks/price-updater'; import priceUpdater from './tasks/price-updater';
import mining from './api/mining/mining';
import chainTips from './api/chain-tips'; import chainTips from './api/chain-tips';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
@ -84,11 +83,8 @@ class Server {
if (config.DATABASE.ENABLED) { if (config.DATABASE.ENABLED) {
await DB.checkDbConnection(); await DB.checkDbConnection();
try { try {
if (process.env.npm_config_reindex !== undefined) { // Re-index requests if (process.env.npm_config_reindex_blocks === 'true') { // Re-index requests
const tables = process.env.npm_config_reindex.split(','); await databaseMigration.$blocksReindexingTruncate();
logger.warn(`Indexed data for "${process.env.npm_config_reindex}" tables will be erased in 5 seconds (using '--reindex')`);
await Common.sleep$(5000);
await databaseMigration.$truncateIndexedData(tables);
} }
await databaseMigration.$initializeOrMigrateDatabase(); await databaseMigration.$initializeOrMigrateDatabase();
if (Common.indexingEnabled()) { if (Common.indexingEnabled()) {

View File

@ -16,6 +16,9 @@ class BlocksRepository {
* Save indexed block data in the database * Save indexed block data in the database
*/ */
public async $saveBlockInDatabase(block: BlockExtended) { public async $saveBlockInDatabase(block: BlockExtended) {
const truncatedCoinbaseSignature = block?.extras?.coinbaseSignature?.substring(0, 500);
const truncatedCoinbaseSignatureAscii = block?.extras?.coinbaseSignatureAscii?.substring(0, 500);
try { try {
const query = `INSERT INTO blocks( const query = `INSERT INTO blocks(
height, hash, blockTimestamp, size, height, hash, blockTimestamp, size,
@ -65,7 +68,7 @@ class BlocksRepository {
block.extras.medianTimestamp, block.extras.medianTimestamp,
block.extras.header, block.extras.header,
block.extras.coinbaseAddress, block.extras.coinbaseAddress,
block.extras.coinbaseSignature, truncatedCoinbaseSignature,
block.extras.utxoSetSize, block.extras.utxoSetSize,
block.extras.utxoSetChange, block.extras.utxoSetChange,
block.extras.avgTxSize, block.extras.avgTxSize,
@ -78,7 +81,7 @@ class BlocksRepository {
block.extras.segwitTotalSize, block.extras.segwitTotalSize,
block.extras.segwitTotalWeight, block.extras.segwitTotalWeight,
block.extras.medianFeeAmt, block.extras.medianFeeAmt,
block.extras.coinbaseSignatureAscii, truncatedCoinbaseSignatureAscii,
]; ];
await DB.query(query, params); await DB.query(query, params);

View File

@ -99,7 +99,7 @@ class PoolsRepository {
rows[0].regexes = JSON.parse(rows[0].regexes); rows[0].regexes = JSON.parse(rows[0].regexes);
} }
if (['testnet', 'signet'].includes(config.MEMPOOL.NETWORK)) { if (['testnet', 'signet'].includes(config.MEMPOOL.NETWORK)) {
rows[0].addresses = []; // pools.json only contains mainnet addresses rows[0].addresses = []; // pools-v2.json only contains mainnet addresses
} else if (parse) { } else if (parse) {
rows[0].addresses = JSON.parse(rows[0].addresses); rows[0].addresses = JSON.parse(rows[0].addresses);
} }

View File

@ -72,7 +72,7 @@ class NetworkSyncService {
const graphNodesPubkeys: string[] = []; const graphNodesPubkeys: string[] = [];
for (const node of nodes) { for (const node of nodes) {
const latestUpdated = await channelsApi.$getLatestChannelUpdateForNode(node.pub_key); const latestUpdated = await channelsApi.$getLatestChannelUpdateForNode(node.pub_key);
node.last_update = Math.max(node.last_update, latestUpdated); node.last_update = Math.max(node.last_update ?? 0, latestUpdated);
await nodesApi.$saveNode(node); await nodesApi.$saveNode(node);
graphNodesPubkeys.push(node.pub_key); graphNodesPubkeys.push(node.pub_key);

View File

@ -17,11 +17,6 @@ class PoolsUpdater {
treeUrl: string = config.MEMPOOL.POOLS_JSON_TREE_URL; treeUrl: string = config.MEMPOOL.POOLS_JSON_TREE_URL;
public async updatePoolsJson(): Promise<void> { public async updatePoolsJson(): Promise<void> {
if (config.MEMPOOL.AUTOMATIC_BLOCK_REINDEXING === false) {
logger.info(`Not updating mining pools to avoid inconsistency because AUTOMATIC_BLOCK_REINDEXING is set to false`)
return;
}
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) { if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) {
return; return;
} }
@ -36,12 +31,6 @@ class PoolsUpdater {
this.lastRun = now; this.lastRun = now;
if (config.SOCKS5PROXY.ENABLED) {
logger.info(`Updating latest mining pools from ${this.poolsUrl} over the Tor network`, logger.tags.mining);
} else {
logger.info(`Updating latest mining pools from ${this.poolsUrl} over clearnet`, logger.tags.mining);
}
try { try {
const githubSha = await this.fetchPoolsSha(); // Fetch pools-v2.json sha from github const githubSha = await this.fetchPoolsSha(); // Fetch pools-v2.json sha from github
if (githubSha === undefined) { if (githubSha === undefined) {
@ -57,10 +46,21 @@ class PoolsUpdater {
return; return;
} }
// See backend README for more details about the mining pools update process
if (this.currentSha !== undefined && // If we don't have any mining pool, download it at least once
config.MEMPOOL.AUTOMATIC_BLOCK_REINDEXING !== true && // Automatic pools update is disabled
!process.env.npm_config_update_pools // We're not manually updating mining pool
) {
logger.warn(`Updated mining pools data is available (${githubSha}) but AUTOMATIC_BLOCK_REINDEXING is disabled`);
logger.info(`You can update your mining pools using the --update-pools command flag. You may want to clear your nginx cache as well if applicable`);
return;
}
const network = config.SOCKS5PROXY.ENABLED ? 'tor' : 'clearnet';
if (this.currentSha === undefined) { if (this.currentSha === undefined) {
logger.info(`Downloading pools-v2.json for the first time from ${this.poolsUrl}`, logger.tags.mining); logger.info(`Downloading pools-v2.json for the first time from ${this.poolsUrl} over ${network}`, logger.tags.mining);
} else { } else {
logger.warn(`pools-v2.json is outdated, fetch latest from ${this.poolsUrl}`, logger.tags.mining); logger.warn(`pools-v2.json is outdated, fetch latest from ${this.poolsUrl} over ${network}`, logger.tags.mining);
} }
const poolsJson = await this.query(this.poolsUrl); const poolsJson = await this.query(this.poolsUrl);
if (poolsJson === undefined) { if (poolsJson === undefined) {

View File

@ -102,11 +102,11 @@ Below we list all settings from `mempool-config.json` and the corresponding over
"MEMPOOL_BLOCKS_AMOUNT": 8, "MEMPOOL_BLOCKS_AMOUNT": 8,
"BLOCKS_SUMMARIES_INDEXING": false, "BLOCKS_SUMMARIES_INDEXING": false,
"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": [],
"STDOUT_LOG_MIN_PRIORITY": "info", "STDOUT_LOG_MIN_PRIORITY": "info",
"INDEXING_BLOCKS_AMOUNT": false, "INDEXING_BLOCKS_AMOUNT": false,
"AUTOMATIC_BLOCK_REINDEXING": false, "AUTOMATIC_BLOCK_REINDEXING": false,
"POOLS_JSON_URL": "https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json", "POOLS_JSON_URL": "https://raw.githubusercontent.com/mempool/mining-pools/master/pools-v2.json",
"POOLS_JSON_TREE_URL": "https://api.github.com/repos/mempool/mining-pools/git/trees/master", "POOLS_JSON_TREE_URL": "https://api.github.com/repos/mempool/mining-pools/git/trees/master",
"ADVANCED_GBT_AUDIT": false, "ADVANCED_GBT_AUDIT": false,
"ADVANCED_GBT_MEMPOOL": false, "ADVANCED_GBT_MEMPOOL": false,

View File

@ -24,7 +24,7 @@ __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:=https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json} __MEMPOOL_POOLS_JSON_URL__=${MEMPOOL_POOLS_JSON_URL:=https://raw.githubusercontent.com/mempool/mining-pools/master/pools-v2.json}
__MEMPOOL_POOLS_JSON_TREE_URL__=${MEMPOOL_POOLS_JSON_TREE_URL:=https://api.github.com/repos/mempool/mining-pools/git/trees/master} __MEMPOOL_POOLS_JSON_TREE_URL__=${MEMPOOL_POOLS_JSON_TREE_URL:=https://api.github.com/repos/mempool/mining-pools/git/trees/master}
__MEMPOOL_AUDIT__=${MEMPOOL_AUDIT:=false} __MEMPOOL_AUDIT__=${MEMPOOL_AUDIT:=false}
__MEMPOOL_ADVANCED_GBT_AUDIT__=${MEMPOOL_ADVANCED_GBT_AUDIT:=false} __MEMPOOL_ADVANCED_GBT_AUDIT__=${MEMPOOL_ADVANCED_GBT_AUDIT:=false}

View File

@ -1,7 +1,7 @@
import { Component, OnInit, OnDestroy, Input, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; import { Component, OnInit, OnDestroy, Input, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { StateService } from '../../services/state.service'; import { StateService } from '../../services/state.service';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { Price } from 'src/app/services/price.service'; import { Price } from '../../services/price.service';
@Component({ @Component({
selector: 'app-amount', selector: 'app-amount',

View File

@ -5,7 +5,7 @@ import BlockScene from './block-scene';
import TxSprite from './tx-sprite'; import TxSprite from './tx-sprite';
import TxView from './tx-view'; import TxView from './tx-view';
import { Position } from './sprite-types'; import { Position } from './sprite-types';
import { Price } from 'src/app/services/price.service'; import { Price } from '../../services/price.service';
@Component({ @Component({
selector: 'app-block-overview-graph', selector: 'app-block-overview-graph',

View File

@ -1,7 +1,7 @@
import { Component, ElementRef, ViewChild, Input, OnChanges, ChangeDetectionStrategy } from '@angular/core'; import { Component, ElementRef, ViewChild, Input, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import { TransactionStripped } from '../../interfaces/websocket.interface'; import { TransactionStripped } from '../../interfaces/websocket.interface';
import { Position } from '../../components/block-overview-graph/sprite-types.js'; import { Position } from '../../components/block-overview-graph/sprite-types.js';
import { Price } from 'src/app/services/price.service'; import { Price } from '../../services/price.service';
@Component({ @Component({
selector: 'app-block-overview-tooltip', selector: 'app-block-overview-tooltip',

View File

@ -13,7 +13,7 @@ import { BlockAudit, BlockExtended, TransactionStripped } from '../../interfaces
import { ApiService } from '../../services/api.service'; import { ApiService } from '../../services/api.service';
import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component'; import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component';
import { detectWebGL } from '../../shared/graphs.utils'; import { detectWebGL } from '../../shared/graphs.utils';
import { PriceService, Price } from 'src/app/services/price.service'; import { PriceService, Price } from '../../services/price.service';
@Component({ @Component({
selector: 'app-block', selector: 'app-block',

View File

@ -22,7 +22,7 @@ import { SeoService } from '../../services/seo.service';
import { BlockExtended, CpfpInfo } from '../../interfaces/node-api.interface'; import { BlockExtended, CpfpInfo } from '../../interfaces/node-api.interface';
import { LiquidUnblinding } from './liquid-ublinding'; import { LiquidUnblinding } from './liquid-ublinding';
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
import { Price, PriceService } from 'src/app/services/price.service'; import { Price, PriceService } from '../../services/price.service';
@Component({ @Component({
selector: 'app-transaction', selector: 'app-transaction',

View File

@ -9,7 +9,7 @@ import { AssetsService } from '../../services/assets.service';
import { filter, map, tap, switchMap, shareReplay } from 'rxjs/operators'; import { filter, map, tap, switchMap, shareReplay } from 'rxjs/operators';
import { BlockExtended } from '../../interfaces/node-api.interface'; import { BlockExtended } from '../../interfaces/node-api.interface';
import { ApiService } from '../../services/api.service'; import { ApiService } from '../../services/api.service';
import { PriceService } from 'src/app/services/price.service'; import { PriceService } from '../../services/price.service';
@Component({ @Component({
selector: 'app-transactions-list', selector: 'app-transactions-list',

View File

@ -1,6 +1,6 @@
import { Component, ElementRef, ViewChild, Input, OnChanges, OnInit } from '@angular/core'; import { Component, ElementRef, ViewChild, Input, OnChanges, OnInit } from '@angular/core';
import { tap } from 'rxjs'; import { tap } from 'rxjs';
import { Price, PriceService } from 'src/app/services/price.service'; import { Price, PriceService } from '../../services/price.service';
interface Xput { interface Xput {
type: 'input' | 'output' | 'fee'; type: 'input' | 'output' | 'fee';

View File

@ -1,4 +1,6 @@
<div class="container-xl" *ngIf="(channel$ | async) as channel; else skeletonLoader"> <div class="container-xl" *ngIf="(channel$ | async) as channel; else skeletonLoader">
<ng-container *ngIf="!error">
<h5 class="mb-0" style="color: #ffffff66" i18n="lightning.channel">Lightning channel</h5> <h5 class="mb-0" style="color: #ffffff66" i18n="lightning.channel">Lightning channel</h5>
<div class="title-container"> <div class="title-container">
<h1 class="mb-0">{{ channel.short_id }}</h1> <h1 class="mb-0">{{ channel.short_id }}</h1>
@ -13,13 +15,18 @@
<span class="badge rounded-pill badge-danger" *ngIf="channel.status === 2" i18n="status.closed">Closed</span> <span class="badge rounded-pill badge-danger" *ngIf="channel.status === 2" i18n="status.closed">Closed</span>
<app-closing-type *ngIf="channel.closing_reason" [type]="channel.closing_reason"></app-closing-type> <app-closing-type *ngIf="channel.closing_reason" [type]="channel.closing_reason"></app-closing-type>
</div> </div>
</ng-container>
<div class="clearfix"></div> <div class="clearfix"></div>
<div *ngIf="error" class="d-flex flex-column justify-content-around align-items-center mt-5 w-100" style="min-height: 100px">
<span class="text-center" i18n="lightning.channel-not-found">No channel found for short id "{{ channel.short_id }}"</span>
</div>
<app-nodes-channels-map *ngIf="!error && (channelGeo$ | async) as channelGeo" [style]="'channelpage'" <app-nodes-channels-map *ngIf="!error && (channelGeo$ | async) as channelGeo" [style]="'channelpage'"
[channel]="channelGeo"></app-nodes-channels-map> [channel]="channelGeo"></app-nodes-channels-map>
<div class="box"> <div class="box" *ngIf="!error">
<div class="row"> <div class="row">
<div class="col-md"> <div class="col-md">
@ -65,7 +72,7 @@
<br> <br>
<div class="row row-cols-1 row-cols-md-2"> <div class="row row-cols-1 row-cols-md-2" *ngIf="!error">
<div class="col"> <div class="col">
<app-channel-box [channel]="channel.node_left"></app-channel-box> <app-channel-box [channel]="channel.node_left"></app-channel-box>
<app-channel-close-box *ngIf="showCloseBoxes(channel)" [channel]="channel" [local]="channel.node_left" [remote]="channel.node_right"></app-channel-close-box> <app-channel-close-box *ngIf="showCloseBoxes(channel)" [channel]="channel" [local]="channel.node_left" [remote]="channel.node_right"></app-channel-close-box>
@ -104,14 +111,6 @@
<br> <br>
<ng-template [ngIf]="error">
<div class="text-center">
<span i18n="error.general-loading-data">Error loading data.</span>
<br><br>
<i>{{ error.status }}: {{ error.error }}</i>
</div>
</ng-template>
<ng-template #skeletonLoader> <ng-template #skeletonLoader>
<div class="container-xl"> <div class="container-xl">
<h5 class="mb-0" style="color: #ffffff66" i18n="lightning.channel">Lightning channel</h5> <h5 class="mb-0" style="color: #ffffff66" i18n="lightning.channel">Lightning channel</h5>

View File

@ -38,7 +38,9 @@ export class ChannelComponent implements OnInit {
}), }),
catchError((err) => { catchError((err) => {
this.error = err; this.error = err;
return of(null); return [{
short_id: params.get('short_id')
}];
}) })
); );
}), }),

View File

@ -1,6 +1,8 @@
<div class="container-xl" *ngIf="(node$ | async) as node; else skeletonLoader"> <div class="container-xl" *ngIf="(node$ | async) as node; else skeletonLoader">
<ng-container *ngIf="!error">
<h5 class="mb-0" style="color: #ffffff66" i18n="lightning.node">Lightning node</h5> <h5 class="mb-0" style="color: #ffffff66" i18n="lightning.node">Lightning node</h5>
<div class="title-container mb-2" *ngIf="!error"> <div class="title-container mb-2">
<h1 class="mb-0 text-truncate">{{ node.alias }}</h1> <h1 class="mb-0 text-truncate">{{ node.alias }}</h1>
<span class="tx-link"> <span class="tx-link">
<span class="node-id"> <span class="node-id">
@ -10,11 +12,12 @@
</span> </span>
</span> </span>
</div> </div>
</ng-container>
<div class="clearfix"></div> <div class="clearfix"></div>
<div *ngIf="error" class="d-flex flex-column justify-content-around align-items-center mt-5 w-100" style="min-height: 100px"> <div *ngIf="error" class="d-flex flex-column justify-content-around align-items-center mt-5 w-100" style="min-height: 100px">
<span i18n="lightning.node-not-found">No node found for public key "{{ node.public_key | shortenString : 12}}"</span> <span class="text-center" i18n="lightning.node-not-found">No node found for public key "{{ node.public_key | shortenString : 12}}"</span>
</div> </div>
<div class="box" *ngIf="!error"> <div class="box" *ngIf="!error">

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@
"AUDIT": true, "AUDIT": true,
"CPFP_INDEXING": true, "CPFP_INDEXING": true,
"ADVANCED_GBT_AUDIT": true, "ADVANCED_GBT_AUDIT": true,
"ADVANCED_GBT_MEMPOOL": false, "ADVANCED_GBT_MEMPOOL": true,
"USE_SECOND_NODE_FOR_MINFEE": true "USE_SECOND_NODE_FOR_MINFEE": true
}, },
"SYSLOG" : { "SYSLOG" : {

View File

@ -9,7 +9,7 @@
"INDEXING_BLOCKS_AMOUNT": -1, "INDEXING_BLOCKS_AMOUNT": -1,
"AUDIT": true, "AUDIT": true,
"ADVANCED_GBT_AUDIT": true, "ADVANCED_GBT_AUDIT": true,
"ADVANCED_GBT_MEMPOOL": false, "ADVANCED_GBT_MEMPOOL": true,
"POLL_RATE_MS": 1000 "POLL_RATE_MS": 1000
}, },
"SYSLOG" : { "SYSLOG" : {

View File

@ -9,7 +9,7 @@
"INDEXING_BLOCKS_AMOUNT": -1, "INDEXING_BLOCKS_AMOUNT": -1,
"AUDIT": true, "AUDIT": true,
"ADVANCED_GBT_AUDIT": true, "ADVANCED_GBT_AUDIT": true,
"ADVANCED_GBT_MEMPOOL": false, "ADVANCED_GBT_MEMPOOL": true,
"POLL_RATE_MS": 1000 "POLL_RATE_MS": 1000
}, },
"SYSLOG" : { "SYSLOG" : {

View File

@ -1,6 +1,6 @@
#!/usr/bin/env zsh #!/usr/bin/env zsh
hostname=$(hostname) hostname=$(hostname)
slugs=(`curl -sSL https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json | jq -r '.slugs[]'`) slugs=(`curl -sSL https://${hostname}/api/v1/mining/pools/3y|jq -r -S '(.pools[].slug)'`)
warm() warm()
{ {