From c675d1c498d4e20b2645d20fc8fdb40cb9fb9e4e Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Tue, 28 Mar 2023 23:07:50 +0900 Subject: [PATCH 01/28] Make sure to scan closed channels even if config.MEMPOOL.ENABLE = false --- backend/src/tasks/lightning/network-sync.service.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/src/tasks/lightning/network-sync.service.ts b/backend/src/tasks/lightning/network-sync.service.ts index 3e5ae1366..fc04f5023 100644 --- a/backend/src/tasks/lightning/network-sync.service.ts +++ b/backend/src/tasks/lightning/network-sync.service.ts @@ -269,7 +269,11 @@ class NetworkSyncService { } private async $scanForClosedChannels(): Promise { - if (this.closedChannelsScanBlock === blocks.getCurrentBlockHeight()) { + let currentBlockHeight = blocks.getCurrentBlockHeight(); + if (config.MEMPOOL.ENABLED === false) { // https://github.com/mempool/mempool/issues/3582 + currentBlockHeight = await bitcoinApi.$getBlockHeightTip(); + } + if (this.closedChannelsScanBlock === currentBlockHeight) { logger.debug(`We've already scan closed channels for this block, skipping.`); return; } @@ -305,7 +309,7 @@ class NetworkSyncService { } } - this.closedChannelsScanBlock = blocks.getCurrentBlockHeight(); + this.closedChannelsScanBlock = currentBlockHeight; logger.debug(`Closed channels scan completed at block ${this.closedChannelsScanBlock}`, logger.tags.ln); } catch (e) { logger.err(`$scanForClosedChannels() error: ${e instanceof Error ? e.message : e}`, logger.tags.ln); From aa882aa36a1f757f069a7c1f096713c86e3f7cb5 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 2 Apr 2023 05:34:46 +0900 Subject: [PATCH 02/28] Fix RTL locale unfurls --- unfurler/src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/unfurler/src/index.ts b/unfurler/src/index.ts index 0b423ff92..fedf32110 100644 --- a/unfurler/src/index.ts +++ b/unfurler/src/index.ts @@ -109,7 +109,10 @@ class Server { page.waitForSelector('meta[property="og:preview:fail"]', { timeout: config.PUPPETEER.RENDER_TIMEOUT || 3000 }).then(() => false) ]) if (success === true) { - const screenshot = await page.screenshot(); + const screenshot = await page.screenshot({ + captureBeyondViewport: false, + clip: { width: 1200, height: 600, x: 0, y: 0, scale: 1 }, + }); return screenshot; } else if (success === false) { logger.warn(`failed to render ${path} for ${action} due to client-side error, e.g. requested an invalid txid`); From 22ee9916ddbe8f65130d2f63bae7aab739798e57 Mon Sep 17 00:00:00 2001 From: TechMiX Date: Wed, 19 Apr 2023 12:14:21 +0200 Subject: [PATCH 03/28] fix change component and audit button position in RTL mode --- frontend/src/app/components/change/change.component.html | 2 +- frontend/src/styles.scss | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/components/change/change.component.html b/frontend/src/app/components/change/change.component.html index 117a0c534..ffc00bf5f 100644 --- a/frontend/src/app/components/change/change.component.html +++ b/frontend/src/app/components/change/change.component.html @@ -1,3 +1,3 @@ - {{ change >= 0 ? '+' : '' }}{{ change | amountShortener }}% + ‎{{ change >= 0 ? '+' : '' }}{{ change | amountShortener }}% diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index d9ea867dc..d64450b93 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -988,6 +988,10 @@ th { margin-right: 10px; } } + + .btn-audit { + margin-left: .5em; + } } .scriptmessage { From 7b9fd8ac635e8d7d1acea436a8c62c75cb0000b2 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 26 Apr 2023 04:55:42 +0900 Subject: [PATCH 04/28] prevent table overflow in unfurl previews --- .../components/address/address-preview.component.html | 2 +- .../components/address/address-preview.component.scss | 5 +++++ .../app/components/block/block-preview.component.html | 2 +- .../app/components/block/block-preview.component.scss | 5 +++++ .../app/lightning/channel/channel-preview.component.html | 2 +- .../app/lightning/channel/channel-preview.component.scss | 5 +++++ .../src/app/lightning/node/node-preview.component.scss | 9 +++++---- 7 files changed, 23 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/components/address/address-preview.component.html b/frontend/src/app/components/address/address-preview.component.html index 1924d1a4c..392cc971e 100644 --- a/frontend/src/app/components/address/address-preview.component.html +++ b/frontend/src/app/components/address/address-preview.component.html @@ -3,7 +3,7 @@ Address
-
+

diff --git a/frontend/src/app/components/address/address-preview.component.scss b/frontend/src/app/components/address/address-preview.component.scss index afa8cb4b4..21e7faab5 100644 --- a/frontend/src/app/components/address/address-preview.component.scss +++ b/frontend/src/app/components/address/address-preview.component.scss @@ -20,6 +20,11 @@ margin-right: 15px; } +.table-col { + max-width: calc(100% - 470px); + overflow: hidden; +} + .table { font-size: 32px; margin-top: 48px; diff --git a/frontend/src/app/components/block/block-preview.component.html b/frontend/src/app/components/block/block-preview.component.html index 29da36373..2109e5753 100644 --- a/frontend/src/app/components/block/block-preview.component.html +++ b/frontend/src/app/components/block/block-preview.component.html @@ -3,7 +3,7 @@ Block
-
+

diff --git a/frontend/src/app/components/block/block-preview.component.scss b/frontend/src/app/components/block/block-preview.component.scss index 829050c4c..0f21a3466 100644 --- a/frontend/src/app/components/block/block-preview.component.scss +++ b/frontend/src/app/components/block/block-preview.component.scss @@ -44,6 +44,11 @@ } } +.table-col { + max-width: calc(100% - 470px); + overflow: hidden; +} + .chart-container { flex-grow: 0; flex-shrink: 0; diff --git a/frontend/src/app/lightning/channel/channel-preview.component.html b/frontend/src/app/lightning/channel/channel-preview.component.html index fe7f45a13..e59361e42 100644 --- a/frontend/src/app/lightning/channel/channel-preview.component.html +++ b/frontend/src/app/lightning/channel/channel-preview.component.html @@ -15,7 +15,7 @@

-
+
{{ channel.id }} diff --git a/frontend/src/app/lightning/channel/channel-preview.component.scss b/frontend/src/app/lightning/channel/channel-preview.component.scss index 23a874ee8..6b6ac5152 100644 --- a/frontend/src/app/lightning/channel/channel-preview.component.scss +++ b/frontend/src/app/lightning/channel/channel-preview.component.scss @@ -1,3 +1,8 @@ +.table-col { + max-width: calc(100% - 470px); + overflow: hidden; +} + .table { font-size: 32px; margin-top: 10px; diff --git a/frontend/src/app/lightning/node/node-preview.component.scss b/frontend/src/app/lightning/node/node-preview.component.scss index baa33915b..da8794010 100644 --- a/frontend/src/app/lightning/node/node-preview.component.scss +++ b/frontend/src/app/lightning/node/node-preview.component.scss @@ -1,3 +1,8 @@ +.table-col { + max-width: calc(100% - 470px); + overflow: hidden; +} + .table { margin-top: 6px; font-size: 32px; @@ -18,10 +23,6 @@ } } -.table-col { - max-width: calc(100% - 470px); -} - .map-col { flex-grow: 0; flex-shrink: 0; From 6e83bee23fc93234a2f81666e290a5fd93ddff54 Mon Sep 17 00:00:00 2001 From: wiz Date: Thu, 11 May 2023 11:08:09 -0500 Subject: [PATCH 05/28] ops: Disable mempool loop for lightning backends --- production/mempool-config.mainnet-lightning.json | 1 + production/mempool-config.signet-lightning.json | 1 + production/mempool-config.testnet-lightning.json | 1 + 3 files changed, 3 insertions(+) diff --git a/production/mempool-config.mainnet-lightning.json b/production/mempool-config.mainnet-lightning.json index 21e7109e9..41e42a5bd 100644 --- a/production/mempool-config.mainnet-lightning.json +++ b/production/mempool-config.mainnet-lightning.json @@ -1,5 +1,6 @@ { "MEMPOOL": { + "ENABLED": false, "NETWORK": "mainnet", "BACKEND": "esplora", "HTTP_PORT": 8993, diff --git a/production/mempool-config.signet-lightning.json b/production/mempool-config.signet-lightning.json index 7751d8f0e..9971729e2 100644 --- a/production/mempool-config.signet-lightning.json +++ b/production/mempool-config.signet-lightning.json @@ -1,5 +1,6 @@ { "MEMPOOL": { + "ENABLED": false, "NETWORK": "signet", "BACKEND": "esplora", "HTTP_PORT": 8991, diff --git a/production/mempool-config.testnet-lightning.json b/production/mempool-config.testnet-lightning.json index d8283b779..ff7d4766f 100644 --- a/production/mempool-config.testnet-lightning.json +++ b/production/mempool-config.testnet-lightning.json @@ -1,5 +1,6 @@ { "MEMPOOL": { + "ENABLED": false, "NETWORK": "testnet", "BACKEND": "esplora", "HTTP_PORT": 8992, From e81839e7ed549ec806684d1518ee858ffa07ce59 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 11 Jul 2023 09:54:03 +0900 Subject: [PATCH 06/28] Return null for avg of zero matching health scores --- backend/src/repositories/BlocksRepository.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index 080de8480..078b85a03 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -401,7 +401,7 @@ class BlocksRepository { /** * Get average block health for all blocks for a single pool */ - public async $getAvgBlockHealthPerPoolId(poolId: number): Promise { + public async $getAvgBlockHealthPerPoolId(poolId: number): Promise { const params: any[] = []; const query = ` SELECT AVG(blocks_audits.match_rate) AS avg_match_rate @@ -413,8 +413,8 @@ class BlocksRepository { try { const [rows] = await DB.query(query, params); - if (!rows[0] || !rows[0].avg_match_rate) { - return 0; + if (!rows[0] || rows[0].avg_match_rate == null) { + return null; } return Math.round(rows[0].avg_match_rate * 100) / 100; } catch (e) { From 94c0222efec787fb79666542c301ead23484766e Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 11 Jul 2023 10:50:25 +0900 Subject: [PATCH 07/28] reset blocks$ and transactions$ observables when network changes --- frontend/src/app/services/state.service.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 0c5f5a5d9..c1b4421df 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -1,11 +1,11 @@ import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core'; import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable, merge } from 'rxjs'; import { Transaction } from '../interfaces/electrs.interface'; -import { IBackendInfo, MempoolBlock, MempoolBlockWithTransactions, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface'; +import { IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, TransactionStripped } from '../interfaces/websocket.interface'; import { BlockExtended, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface'; import { Router, NavigationStart } from '@angular/router'; import { isPlatformBrowser } from '@angular/common'; -import { map, scan, shareReplay, tap } from 'rxjs/operators'; +import { map, scan, shareReplay } from 'rxjs/operators'; import { StorageService } from './storage.service'; export interface MarkBlockState { @@ -198,6 +198,11 @@ export class StateService { this.networkChanged$.next(this.env.BASE_MODULE); } + this.networkChanged$.subscribe((network) => { + this.transactions$ = new ReplaySubject(6); + this.blocks$ = new ReplaySubject<[BlockExtended, string]>(this.env.KEEP_BLOCKS_AMOUNT); + }); + this.blockVSize = this.env.BLOCK_WEIGHT_UNITS / 4; const savedTimePreference = this.storageService.getValue('time-preference-ltr'); From 01bd9dd957e11809f1e2ab30e8231656bad81a6c Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 11 Jul 2023 11:18:17 +0900 Subject: [PATCH 08/28] Add spacer for missing fiat values --- frontend/src/app/fiat/fiat.component.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/fiat/fiat.component.html b/frontend/src/app/fiat/fiat.component.html index afee30d27..ebf59ffb1 100644 --- a/frontend/src/app/fiat/fiat.component.html +++ b/frontend/src/app/fiat/fiat.component.html @@ -8,7 +8,10 @@ - + {{ (conversions[currency] > -1 ? conversions[currency] : 0) * value / 100000000 | fiatCurrency : digitsInfo : currency }} + +   + \ No newline at end of file From a01336d8ac96dfb339a4fe80eb51620b8e0cd090 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 11 Jul 2023 11:44:30 +0900 Subject: [PATCH 09/28] Fix mined rbf conflict prevention --- backend/src/api/common.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts index 9836559ae..735e240c1 100644 --- a/backend/src/api/common.ts +++ b/backend/src/api/common.ts @@ -86,19 +86,19 @@ export class Common { const match = spendMap.get(`${vin.txid}:${vin.vout}`); if (match && match.txid !== tx.txid) { replaced.add(match); + // remove this tx from the spendMap + // prevents the same tx being replaced more than once + for (const replacedVin of match.vin) { + const key = `${replacedVin.txid}:${replacedVin.vout}`; + spendMap.delete(key); + } } + const key = `${vin.txid}:${vin.vout}`; + spendMap.delete(key); } if (replaced.size) { matches[tx.txid] = { replaced: Array.from(replaced), replacedBy: tx }; } - // remove this tx from the spendMap - // prevents the same tx being replaced more than once - for (const vin of tx.vin) { - const key = `${vin.txid}:${vin.vout}`; - if (spendMap.get(key)?.txid === tx.txid) { - spendMap.delete(key); - } - } } return matches; } From ca2830d6d8c4e223c9400fcad52acc92e5597ced Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 11 Jul 2023 16:03:44 +0900 Subject: [PATCH 10/28] fix price updater loop on testnet/signet --- backend/src/indexer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/indexer.ts b/backend/src/indexer.ts index 4b120867f..88f44d587 100644 --- a/backend/src/indexer.ts +++ b/backend/src/indexer.ts @@ -6,6 +6,7 @@ import logger from './logger'; import bitcoinClient from './api/bitcoin/bitcoin-client'; import priceUpdater from './tasks/price-updater'; import PricesRepository from './repositories/PricesRepository'; +import config from './config'; export interface CoreIndex { name: string; @@ -72,7 +73,7 @@ class Indexer { return; } - if (task === 'blocksPrices' && !this.tasksRunning.includes(task)) { + if (task === 'blocksPrices' && !this.tasksRunning.includes(task) && !['testnet', 'signet'].includes(config.MEMPOOL.NETWORK)) { this.tasksRunning.push(task); const lastestPriceId = await PricesRepository.$getLatestPriceId(); if (priceUpdater.historyInserted === false || lastestPriceId === null) { From 0dd9867a1f3ecdde3a86df9858ccf61ac85635f0 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 12 Jul 2023 12:58:48 +0900 Subject: [PATCH 11/28] always show latest difficulty on hashrate chart --- .../hashrate-chart/hashrate-chart.component.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts index fcff0dddb..d562375b8 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -109,6 +109,14 @@ export class HashrateChartComponent implements OnInit { tap((response: any) => { const data = response.body; + // always include the latest difficulty + if (data.difficulty.length && data.difficulty[data.difficulty.length - 1].difficulty !== data.currentDifficulty) { + data.difficulty.push({ + timestamp: Date.now() / 1000, + difficulty: data.currentDifficulty + }); + } + // We generate duplicated data point so the tooltip works nicely const diffFixed = []; let diffIndex = 1; @@ -137,6 +145,14 @@ export class HashrateChartComponent implements OnInit { ++diffIndex; } + while (diffIndex <= data.difficulty.length) { + diffFixed.push({ + timestamp: data.difficulty[diffIndex - 1].time, + difficulty: data.difficulty[diffIndex - 1].difficulty + }); + diffIndex++; + } + let maResolution = 15; const hashrateMa = []; for (let i = maResolution - 1; i < data.hashrates.length; ++i) { From 95e50ddf02f6188ca2ffb5758e5882a8e8523849 Mon Sep 17 00:00:00 2001 From: wiz Date: Wed, 12 Jul 2023 17:16:40 +0900 Subject: [PATCH 12/28] Fix production rust GBT build --- backend/package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index f277f6d6d..4f36005a7 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -7570,7 +7570,7 @@ "name": "gbt", "version": "0.1.0", "hasInstallScript": true, - "devDependencies": { + "dependencies": { "@napi-rs/cli": "^2.16.1" }, "engines": { From 415b70da1448dea6c78daca197fd22d3caf41cbe Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 11 Jul 2023 15:49:38 +0900 Subject: [PATCH 13/28] show "loading" message while checking for cached txs --- .../transaction/transaction.component.html | 13 ++++++++++--- .../components/transaction/transaction.component.ts | 8 ++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index 25707b007..3e739a30f 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -453,12 +453,19 @@ -
-

Transaction not found.

-
Waiting for it to appear in the mempool...
+
+

Loading transaction

+ +
+

Transaction not found.

+
Waiting for it to appear in the mempool...
+
+
+
+

{{ error.error }}

diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index bbf679dcf..08f331e60 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -39,6 +39,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { isLoadingTx = true; error: any = undefined; errorUnblinded: any = undefined; + loadingCachedTx = false; waitingForTransaction = false; latestBlock: BlockExtended; transactionTime = -1; @@ -199,6 +200,9 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.fetchCachedTxSubscription = this.fetchCachedTx$ .pipe( + tap(() => { + this.loadingCachedTx = true; + }), switchMap((txId) => this.apiService .getRbfCachedTx$(txId) @@ -207,6 +211,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { return of(null); }) ).subscribe((tx) => { + this.loadingCachedTx = false; if (!tx) { return; } @@ -221,6 +226,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.tx.feePerVsize = tx.fee / (tx.weight / 4); this.isLoadingTx = false; this.error = undefined; + this.loadingCachedTx = false; this.waitingForTransaction = false; this.graphExpanded = false; this.transactionTime = tx.firstSeen || 0; @@ -338,6 +344,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.tx.feePerVsize = tx.fee / (tx.weight / 4); this.isLoadingTx = false; this.error = undefined; + this.loadingCachedTx = false; this.waitingForTransaction = false; this.websocketService.startTrackTransaction(tx.txid); this.graphExpanded = false; @@ -409,6 +416,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.txReplacedSubscription = this.stateService.txReplaced$.subscribe((rbfTransaction) => { if (!this.tx) { this.error = new Error(); + this.loadingCachedTx = false; this.waitingForTransaction = false; } this.rbfTransaction = rbfTransaction; From 55cc3a0c074ff7641d77a021a0f0e2bfeafc38cf Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 11 Jul 2023 16:36:48 +0900 Subject: [PATCH 14/28] fix loading transaction i18n tag --- .../src/app/components/transaction/transaction.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index 3e739a30f..8c88d6fc1 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -454,7 +454,7 @@
-

Loading transaction

+

Loading transaction

From 132923e7db1245aaa0fa4ce6abf61c95a9231582 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 12 Jul 2023 17:43:48 +0900 Subject: [PATCH 15/28] Show skeleton loader instead of "Loading transaction..." --- .../transaction/transaction.component.html | 17 +++++------------ .../transaction/transaction.component.ts | 1 - 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index 8c88d6fc1..203a5df5c 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -306,7 +306,7 @@
- +
@@ -451,21 +451,14 @@ - + -
-

Loading transaction

+
+

Transaction not found.

+
Waiting for it to appear in the mempool...
- -
-

Transaction not found.

-
Waiting for it to appear in the mempool...
-
-
-
-

{{ error.error }}

diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 08f331e60..797282c7b 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -226,7 +226,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.tx.feePerVsize = tx.fee / (tx.weight / 4); this.isLoadingTx = false; this.error = undefined; - this.loadingCachedTx = false; this.waitingForTransaction = false; this.graphExpanded = false; this.transactionTime = tx.firstSeen || 0; From e8c703fdbc733f5ccf2ae3fdea717cd2f0d275e9 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 8 Jul 2023 01:07:06 -0400 Subject: [PATCH 16/28] replace client recent blocks on reorg --- backend/src/api/blocks.ts | 3 ++ backend/src/api/websocket-handler.ts | 34 ++++++++++++ .../bisq-transaction.component.ts | 2 +- .../bisq-transfers.component.ts | 2 +- .../app/components/block/block.component.ts | 21 ++++---- .../blockchain-blocks.component.ts | 52 +++++++++---------- .../clock-face/clock-face.component.ts | 11 ++-- .../app/components/clock/clock.component.ts | 13 ++--- .../difficulty-mining.component.ts | 7 +-- .../difficulty/difficulty.component.ts | 7 +-- .../mempool-blocks.component.ts | 17 +++--- .../src/app/components/pool/pool.component.ts | 8 +-- .../reward-stats/reward-stats.component.ts | 7 +-- .../app/components/start/start.component.ts | 10 ++-- .../transaction/transaction.component.ts | 6 +-- .../transactions-list.component.ts | 2 +- .../src/app/dashboard/dashboard.component.ts | 4 +- frontend/src/app/services/cache.service.ts | 17 ++++-- frontend/src/app/services/state.service.ts | 17 ++++-- .../src/app/services/websocket.service.ts | 12 ++--- 20 files changed, 153 insertions(+), 99 deletions(-) diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 4d218ed54..5939421a7 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -25,6 +25,7 @@ import DifficultyAdjustmentsRepository from '../repositories/DifficultyAdjustmen import PricesRepository from '../repositories/PricesRepository'; import priceUpdater from '../tasks/price-updater'; import chainTips from './chain-tips'; +import websocketHandler from './websocket-handler'; class Blocks { private blocks: BlockExtended[] = []; @@ -686,6 +687,8 @@ class Blocks { this.updateTimerProgress(timer, `reindexed difficulty adjustments`); logger.info(`Re-indexed 10 blocks and summaries. Also re-indexed the last difficulty adjustments. Will re-index latest hashrates in a few seconds.`, logger.tags.mining); indexer.reindex(); + + websocketHandler.handleReorg(); } } diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index ae536b72e..f91947dcb 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -333,6 +333,40 @@ class WebsocketHandler { }); } + handleReorg(): void { + if (!this.wss) { + throw new Error('WebSocket.Server is not set'); + } + + const da = difficultyAdjustment.getDifficultyAdjustment(); + + // update init data + this.updateSocketDataFields({ + 'blocks': blocks.getBlocks(), + 'da': da?.previousTime ? da : undefined, + }); + + this.wss.clients.forEach((client) => { + if (client.readyState !== WebSocket.OPEN) { + return; + } + + const response = {}; + + if (client['want-blocks']) { + response['blocks'] = this.socketData['blocks']; + } + if (client['want-stats']) { + response['da'] = this.socketData['da']; + } + + if (Object.keys(response).length) { + const serializedResponse = this.serializeResponse(response); + client.send(serializedResponse); + } + }); + } + async $handleMempoolChange(newMempool: { [txid: string]: MempoolTransactionExtended }, mempoolSize: number, newTransactions: MempoolTransactionExtended[], deletedTransactions: MempoolTransactionExtended[]): Promise { if (!this.wss) { diff --git a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts index fb30fc59f..47ac0d6db 100644 --- a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts +++ b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts @@ -112,7 +112,7 @@ export class BisqTransactionComponent implements OnInit, OnDestroy { this.error = error; }); - this.latestBlock$ = this.stateService.blocks$.pipe(map((([block]) => block))); + this.latestBlock$ = this.stateService.blocks$.pipe(map((blocks) => blocks[0])); this.stateService.bsqPrice$ .subscribe((bsqPrice) => { diff --git a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.ts b/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.ts index 4346f15d3..a46cbf07f 100644 --- a/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.ts +++ b/frontend/src/app/bisq/bisq-transfers/bisq-transfers.component.ts @@ -27,7 +27,7 @@ export class BisqTransfersComponent implements OnInit, OnChanges { } ngOnInit() { - this.latestBlock$ = this.stateService.blocks$.pipe(map(([block]) => block)); + this.latestBlock$ = this.stateService.blocks$.pipe(map((blocks) => blocks[0])); } ngOnChanges() { diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index ad008089d..720203220 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -129,18 +129,19 @@ export class BlockComponent implements OnInit, OnDestroy { ); this.blocksSubscription = this.stateService.blocks$ - .subscribe(([block]) => { - this.latestBlock = block; - this.latestBlocks.unshift(block); - this.latestBlocks = this.latestBlocks.slice(0, this.stateService.env.KEEP_BLOCKS_AMOUNT); + .subscribe((blocks) => { + this.latestBlock = blocks[0]; + this.latestBlocks = blocks; this.setNextAndPreviousBlockLink(); - if (block.id === this.blockHash) { - this.block = block; - block.extras.minFee = this.getMinBlockFee(block); - block.extras.maxFee = this.getMaxBlockFee(block); - if (block?.extras?.reward != undefined) { - this.fees = block.extras.reward / 100000000 - this.blockSubsidy; + for (const block of blocks) { + if (block.id === this.blockHash) { + this.block = block; + block.extras.minFee = this.getMinBlockFee(block); + block.extras.maxFee = this.getMaxBlockFee(block); + if (block?.extras?.reward != undefined) { + this.fees = block.extras.reward / 100000000 - this.blockSubsidy; + } } } }); diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts index 5242c1fe5..23efb0c78 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts @@ -41,6 +41,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { networkSubscription: Subscription; tabHiddenSubscription: Subscription; markBlockSubscription: Subscription; + txConfirmedSubscription: Subscription; loadingBlocks$: Observable; blockStyles = []; emptyBlockStyles = []; @@ -104,31 +105,22 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { this.tabHiddenSubscription = this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden); if (!this.static) { this.blocksSubscription = this.stateService.blocks$ - .subscribe(([block, txConfirmed]) => { - if (this.blocks.some((b) => b.height === block.height)) { + .subscribe((blocks) => { + if (!blocks?.length) { return; } + const latestHeight = blocks[0].height; + const animate = latestHeight > blocks[0].height; - if (this.blocks.length && block.height !== this.blocks[0].height + 1) { - this.blocks = []; - this.blocksFilled = false; + for (const block of blocks) { + block.extras.minFee = this.getMinBlockFee(block); + block.extras.maxFee = this.getMaxBlockFee(block); } - block.extras.minFee = this.getMinBlockFee(block); - block.extras.maxFee = this.getMaxBlockFee(block); - - this.blocks.unshift(block); - this.blocks = this.blocks.slice(0, this.dynamicBlocksAmount); - - if (txConfirmed && block.height > this.chainTip) { - this.markHeight = block.height; - this.moveArrowToPosition(true, true); - } else { - this.moveArrowToPosition(true, false); - } + this.blocks = blocks; this.blockStyles = []; - if (this.blocksFilled && block.height > this.chainTip) { + if (animate) { this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i, i ? -this.blockOffset : -this.dividerBlockOffset))); setTimeout(() => { this.blockStyles = []; @@ -139,13 +131,18 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i))); } - if (this.blocks.length === this.dynamicBlocksAmount) { - this.blocksFilled = true; - } - - this.chainTip = Math.max(this.chainTip, block.height); + this.chainTip = latestHeight; this.cd.markForCheck(); }); + + this.txConfirmedSubscription = this.stateService.txConfirmed$.subscribe(([txid, block]) => { + if (txid) { + this.markHeight = block.height; + this.moveArrowToPosition(true, true); + } else { + this.moveArrowToPosition(true, false); + } + }) } else { this.blockPageSubscription = this.cacheService.loadedBlocks$.subscribe((block) => { if (block.height <= this.height && block.height > this.height - this.count) { @@ -164,9 +161,9 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { this.cd.markForCheck(); }); - if (this.static) { - this.updateStaticBlocks(); - } + if (this.static) { + this.updateStaticBlocks(); + } } ngOnChanges(changes: SimpleChanges): void { @@ -190,6 +187,9 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { if (this.blockPageSubscription) { this.blockPageSubscription.unsubscribe(); } + if (this.txConfirmedSubscription) { + this.txConfirmedSubscription.unsubscribe(); + } this.networkSubscription.unsubscribe(); this.tabHiddenSubscription.unsubscribe(); this.markBlockSubscription.unsubscribe(); diff --git a/frontend/src/app/components/clock-face/clock-face.component.ts b/frontend/src/app/components/clock-face/clock-face.component.ts index c2c946b74..63d87c436 100644 --- a/frontend/src/app/components/clock-face/clock-face.component.ts +++ b/frontend/src/app/components/clock-face/clock-face.component.ts @@ -39,13 +39,10 @@ export class ClockFaceComponent implements OnInit, OnChanges, OnDestroy { }) ).subscribe(); this.blocksSubscription = this.stateService.blocks$ - .subscribe(([block]) => { - if (block) { - this.blockTimes.push([block.height, new Date(block.timestamp * 1000)]); - // using block-reported times, so ensure they are sorted chronologically - this.blockTimes = this.blockTimes.sort((a, b) => a[1].getTime() - b[1].getTime()); - this.updateSegments(); - } + .subscribe((blocks) => { + this.blockTimes = blocks.map(block => [block.height, new Date(block.timestamp * 1000)]); + this.blockTimes = this.blockTimes.sort((a, b) => a[1].getTime() - b[1].getTime()); + this.updateSegments(); }); } diff --git a/frontend/src/app/components/clock/clock.component.ts b/frontend/src/app/components/clock/clock.component.ts index b1a9d2159..7ae38583a 100644 --- a/frontend/src/app/components/clock/clock.component.ts +++ b/frontend/src/app/components/clock/clock.component.ts @@ -60,14 +60,11 @@ export class ClockComponent implements OnInit { this.websocketService.want(['blocks', 'stats', 'mempool-blocks']); this.blocksSubscription = this.stateService.blocks$ - .subscribe(([block]) => { - if (block) { - this.blocks.unshift(block); - this.blocks = this.blocks.slice(0, 16); - if (this.blocks[this.blockIndex]) { - this.blockStyle = this.getStyleForBlock(this.blocks[this.blockIndex]); - this.cd.markForCheck(); - } + .subscribe((blocks) => { + this.blocks = blocks.slice(0, 16); + if (this.blocks[this.blockIndex]) { + this.blockStyle = this.getStyleForBlock(this.blocks[this.blockIndex]); + this.cd.markForCheck(); } }); diff --git a/frontend/src/app/components/difficulty-mining/difficulty-mining.component.ts b/frontend/src/app/components/difficulty-mining/difficulty-mining.component.ts index fbf31f238..c23d7d4b9 100644 --- a/frontend/src/app/components/difficulty-mining/difficulty-mining.component.ts +++ b/frontend/src/app/components/difficulty-mining/difficulty-mining.component.ts @@ -38,11 +38,12 @@ export class DifficultyMiningComponent implements OnInit { ngOnInit(): void { this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$; this.difficultyEpoch$ = combineLatest([ - this.stateService.blocks$.pipe(map(([block]) => block)), + this.stateService.blocks$, this.stateService.difficultyAdjustment$, ]) .pipe( - map(([block, da]) => { + map(([blocks, da]) => { + const maxHeight = blocks.reduce((max, block) => Math.max(max, block.height), 0); let colorAdjustments = '#ffffff66'; if (da.difficultyChange > 0) { colorAdjustments = '#3bcc49'; @@ -63,7 +64,7 @@ export class DifficultyMiningComponent implements OnInit { colorPreviousAdjustments = '#ffffff66'; } - const blocksUntilHalving = 210000 - (block.height % 210000); + const blocksUntilHalving = 210000 - (maxHeight % 210000); const timeUntilHalving = new Date().getTime() + (blocksUntilHalving * 600000); const data = { diff --git a/frontend/src/app/components/difficulty/difficulty.component.ts b/frontend/src/app/components/difficulty/difficulty.component.ts index b246a14fe..d3983c939 100644 --- a/frontend/src/app/components/difficulty/difficulty.component.ts +++ b/frontend/src/app/components/difficulty/difficulty.component.ts @@ -67,11 +67,12 @@ export class DifficultyComponent implements OnInit { ngOnInit(): void { this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$; this.difficultyEpoch$ = combineLatest([ - this.stateService.blocks$.pipe(map(([block]) => block)), + this.stateService.blocks$, this.stateService.difficultyAdjustment$, ]) .pipe( - map(([block, da]) => { + map(([blocks, da]) => { + const maxHeight = blocks.reduce((max, block) => Math.max(max, block.height), 0); let colorAdjustments = '#ffffff66'; if (da.difficultyChange > 0) { colorAdjustments = '#3bcc49'; @@ -92,7 +93,7 @@ export class DifficultyComponent implements OnInit { colorPreviousAdjustments = '#ffffff66'; } - const blocksUntilHalving = 210000 - (block.height % 210000); + const blocksUntilHalving = 210000 - (maxHeight % 210000); const timeUntilHalving = new Date().getTime() + (blocksUntilHalving * 600000); const newEpochStart = Math.floor(this.stateService.latestBlockHeight / EPOCH_BLOCK_LENGTH) * EPOCH_BLOCK_LENGTH; const newExpectedHeight = Math.floor(newEpochStart + da.expectedBlocks); diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts index bc3633be0..3ec240b78 100644 --- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts +++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts @@ -124,7 +124,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { ) .pipe( switchMap(() => combineLatest([ - this.stateService.blocks$.pipe(map(([block]) => block)), + this.stateService.blocks$.pipe(map((blocks) => blocks[0])), this.stateService.mempoolBlocks$ .pipe( map((mempoolBlocks) => { @@ -186,8 +186,11 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { this.cd.markForCheck(); }); - this.blockSubscription = this.stateService.blocks$ - .subscribe(([block]) => { + this.blockSubscription = this.stateService.blocks$.pipe(map((blocks) => blocks[0])) + .subscribe((block) => { + if (!block) { + return; + } if (this.chainTip === -1) { this.animateEntry = block.height === this.stateService.latestBlockHeight; } else { @@ -221,8 +224,8 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { this.router.navigate([this.relativeUrlPipe.transform('mempool-block/'), this.markIndex - 1]); } else { this.stateService.blocks$ - .pipe(take(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT)) - .subscribe(([block]) => { + .pipe(map((blocks) => blocks[0])) + .subscribe((block) => { if (this.stateService.latestBlockHeight === block.height) { this.router.navigate([this.relativeUrlPipe.transform('/block/'), block.id], { state: { data: { block } }}); } @@ -297,7 +300,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { while (blocks.length > blocksAmount) { const block = blocks.pop(); if (!this.count) { - const lastBlock = blocks[blocks.length - 1]; + const lastBlock = blocks[0]; lastBlock.blockSize += block.blockSize; lastBlock.blockVSize += block.blockVSize; lastBlock.nTx += block.nTx; @@ -308,7 +311,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { } } if (blocks.length) { - blocks[blocks.length - 1].isStack = blocks[blocks.length - 1].blockVSize > this.stateService.blockVSize; + blocks[0].isStack = blocks[0].blockVSize > this.stateService.blockVSize; } return blocks; } diff --git a/frontend/src/app/components/pool/pool.component.ts b/frontend/src/app/components/pool/pool.component.ts index 85fd028ef..139da5ef0 100644 --- a/frontend/src/app/components/pool/pool.component.ts +++ b/frontend/src/app/components/pool/pool.component.ts @@ -37,7 +37,7 @@ export class PoolComponent implements OnInit { auditAvailable = false; - loadMoreSubject: BehaviorSubject = new BehaviorSubject(this.blocks[this.blocks.length - 1]?.height); + loadMoreSubject: BehaviorSubject = new BehaviorSubject(this.blocks[0]?.height); constructor( @Inject(LOCALE_ID) public locale: string, @@ -68,7 +68,7 @@ export class PoolComponent implements OnInit { return this.apiService.getPoolStats$(slug); }), tap(() => { - this.loadMoreSubject.next(this.blocks[this.blocks.length - 1]?.height); + this.loadMoreSubject.next(this.blocks[0]?.height); }), map((poolStats) => { this.seoService.setTitle(poolStats.pool.name); @@ -91,7 +91,7 @@ export class PoolComponent implements OnInit { if (this.slug === undefined) { return []; } - return this.apiService.getPoolBlocks$(this.slug, this.blocks[this.blocks.length - 1]?.height); + return this.apiService.getPoolBlocks$(this.slug, this.blocks[0]?.height); }), tap((newBlocks) => { this.blocks = this.blocks.concat(newBlocks); @@ -237,7 +237,7 @@ export class PoolComponent implements OnInit { } loadMore() { - this.loadMoreSubject.next(this.blocks[this.blocks.length - 1]?.height); + this.loadMoreSubject.next(this.blocks[0]?.height); } trackByBlock(index: number, block: BlockExtended) { diff --git a/frontend/src/app/components/reward-stats/reward-stats.component.ts b/frontend/src/app/components/reward-stats/reward-stats.component.ts index 30bf26488..5aac641b0 100644 --- a/frontend/src/app/components/reward-stats/reward-stats.component.ts +++ b/frontend/src/app/components/reward-stats/reward-stats.component.ts @@ -29,11 +29,12 @@ export class RewardStatsComponent implements OnInit { // Or when we receive a newer block, newer than the latest reward stats api call this.stateService.blocks$ .pipe( - switchMap((block) => { - if (block[0].height <= this.lastBlockHeight) { + switchMap((blocks) => { + const maxHeight = blocks.reduce((max, block) => Math.max(max, block.height), 0); + if (maxHeight <= this.lastBlockHeight) { return []; // Return an empty stream so the last pipe is not executed } - this.lastBlockHeight = block[0].height; + this.lastBlockHeight = maxHeight; return this.apiService.getRewardStats$(); }) ) diff --git a/frontend/src/app/components/start/start.component.ts b/frontend/src/app/components/start/start.component.ts index 22d3d6350..33770bb24 100644 --- a/frontend/src/app/components/start/start.component.ts +++ b/frontend/src/app/components/start/start.component.ts @@ -2,6 +2,7 @@ import { Component, ElementRef, HostListener, OnInit, OnDestroy, ViewChild, Inpu import { Subscription } from 'rxjs'; import { MarkBlockState, StateService } from '../../services/state.service'; import { specialBlocks } from '../../app.constants'; +import { BlockExtended } from '../../interfaces/node-api.interface'; @Component({ selector: 'app-start', @@ -55,8 +56,8 @@ export class StartComponent implements OnInit, OnDestroy { ngOnInit() { this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount); - this.blockCounterSubscription = this.stateService.blocks$.subscribe(() => { - this.blockCount++; + this.blockCounterSubscription = this.stateService.blocks$.subscribe((blocks) => { + this.blockCount = blocks.length; this.dynamicBlocksAmount = Math.min(this.blockCount, this.stateService.env.KEEP_BLOCKS_AMOUNT, 8); this.firstPageWidth = 40 + (this.blockWidth * this.dynamicBlocksAmount); if (this.blockCount <= Math.min(8, this.stateService.env.KEEP_BLOCKS_AMOUNT)) { @@ -110,9 +111,12 @@ export class StartComponent implements OnInit, OnDestroy { } }); this.stateService.blocks$ - .subscribe((blocks: any) => { + .subscribe((blocks: BlockExtended[]) => { this.countdown = 0; const block = blocks[0]; + if (!block) { + return; + } for (const sb in specialBlocks) { if (specialBlocks[sb].networks.includes(this.stateService.network || 'mainnet')) { diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index bbf679dcf..684f343eb 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -49,7 +49,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { txReplacedSubscription: Subscription; txRbfInfoSubscription: Subscription; mempoolPositionSubscription: Subscription; - blocksSubscription: Subscription; queryParamsSubscription: Subscription; urlFragmentSubscription: Subscription; mempoolBlocksSubscription: Subscription; @@ -391,9 +390,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { } ); - this.blocksSubscription = this.stateService.blocks$.subscribe(([block, txConfirmed]) => { - this.latestBlock = block; - + this.stateService.txConfirmed$.subscribe(([txConfirmed, block]) => { if (txConfirmed && this.tx && !this.tx.status.confirmed && txConfirmed === this.tx.txid) { this.tx.status = { confirmed: true, @@ -593,7 +590,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.fetchCachedTxSubscription.unsubscribe(); this.txReplacedSubscription.unsubscribe(); this.txRbfInfoSubscription.unsubscribe(); - this.blocksSubscription.unsubscribe(); this.queryParamsSubscription.unsubscribe(); this.flowPrefSubscription.unsubscribe(); this.urlFragmentSubscription.unsubscribe(); diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index 53ddb449c..c49ff0e3c 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -56,7 +56,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { ) { } ngOnInit(): void { - this.latestBlock$ = this.stateService.blocks$.pipe(map(([block]) => block)); + this.latestBlock$ = this.stateService.blocks$.pipe(map((blocks) => blocks[0])); this.stateService.networkChanged$.subscribe((network) => this.network = network); if (this.network === 'liquid' || this.network === 'liquidtestnet') { diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index 7e4645fe0..74a48c74c 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -132,8 +132,8 @@ export class DashboardComponent implements OnInit, OnDestroy { this.blocks$ = this.stateService.blocks$ .pipe( - tap(([block]) => { - this.latestBlockHeight = block.height; + tap((blocks) => { + this.latestBlockHeight = blocks[0].height; }), scan((acc, [block]) => { if (acc.find((b) => b.height == block.height)) { diff --git a/frontend/src/app/services/cache.service.ts b/frontend/src/app/services/cache.service.ts index 5eefd6e0a..8c90dc210 100644 --- a/frontend/src/app/services/cache.service.ts +++ b/frontend/src/app/services/cache.service.ts @@ -18,6 +18,7 @@ export class CacheService { txCache: { [txid: string]: Transaction } = {}; network: string; + blockHashCache: { [hash: string]: BlockExtended } = {}; blockCache: { [height: number]: BlockExtended } = {}; blockLoading: { [height: number]: boolean } = {}; copiesInBlockQueue: { [height: number]: number } = {}; @@ -27,8 +28,10 @@ export class CacheService { private stateService: StateService, private apiService: ApiService, ) { - this.stateService.blocks$.subscribe(([block]) => { - this.addBlockToCache(block); + this.stateService.blocks$.subscribe((blocks) => { + for (const block of blocks) { + this.addBlockToCache(block); + } this.clearBlocks(); }); this.stateService.chainTip$.subscribe((height) => { @@ -56,8 +59,11 @@ export class CacheService { } addBlockToCache(block: BlockExtended) { - this.blockCache[block.height] = block; - this.bumpBlockPriority(block.height); + if (!this.blockHashCache[block.id]) { + this.blockHashCache[block.id] = block; + this.blockCache[block.height] = block; + this.bumpBlockPriority(block.height); + } } async loadBlock(height) { @@ -105,7 +111,9 @@ export class CacheService { } else if ((this.tip - height) < KEEP_RECENT_BLOCKS) { this.bumpBlockPriority(height); } else { + const block = this.blockCache[height]; delete this.blockCache[height]; + delete this.blockHashCache[block.id]; delete this.copiesInBlockQueue[height]; } } @@ -113,6 +121,7 @@ export class CacheService { // remove all blocks from the cache resetBlockCache() { + this.blockHashCache = {}; this.blockCache = {}; this.blockLoading = {}; this.copiesInBlockQueue = {}; diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index c1b4421df..4b1323939 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -90,10 +90,11 @@ export class StateService { blockVSize: number; env: Env; latestBlockHeight = -1; + blocks: BlockExtended[] = []; networkChanged$ = new ReplaySubject(1); lightningChanged$ = new ReplaySubject(1); - blocks$: ReplaySubject<[BlockExtended, string]>; + blocks$ = new BehaviorSubject([]); transactions$ = new ReplaySubject(6); conversions$ = new ReplaySubject(1); bsqPrice$ = new ReplaySubject(1); @@ -102,6 +103,7 @@ export class StateService { mempoolBlockTransactions$ = new Subject(); mempoolBlockDelta$ = new Subject(); liveMempoolBlockTransactions$: Observable<{ [txid: string]: TransactionStripped}>; + txConfirmed$ = new Subject<[string, BlockExtended]>(); txReplaced$ = new Subject(); txRbfInfo$ = new Subject(); rbfLatest$ = new Subject(); @@ -167,8 +169,6 @@ export class StateService { } }); - this.blocks$ = new ReplaySubject<[BlockExtended, string]>(this.env.KEEP_BLOCKS_AMOUNT); - this.liveMempoolBlockTransactions$ = merge( this.mempoolBlockTransactions$.pipe(map(transactions => { return { transactions }; })), this.mempoolBlockDelta$.pipe(map(delta => { return { delta }; })), @@ -341,4 +341,15 @@ export class StateService { this.chainTip$.next(height); } } + + resetBlocks(blocks: BlockExtended[]): void { + this.blocks = blocks.reverse(); + this.blocks$.next(blocks); + } + + addBlock(block: BlockExtended): void { + this.blocks.unshift(block); + this.blocks = this.blocks.slice(0, this.env.KEEP_BLOCKS_AMOUNT); + this.blocks$.next(this.blocks); + } } diff --git a/frontend/src/app/services/websocket.service.ts b/frontend/src/app/services/websocket.service.ts index d22717b2a..472501384 100644 --- a/frontend/src/app/services/websocket.service.ts +++ b/frontend/src/app/services/websocket.service.ts @@ -239,13 +239,8 @@ export class WebsocketService { if (response.blocks && response.blocks.length) { const blocks = response.blocks; - let maxHeight = 0; - blocks.forEach((block: BlockExtended) => { - if (block.height > this.stateService.latestBlockHeight) { - maxHeight = Math.max(maxHeight, block.height); - this.stateService.blocks$.next([block, '']); - } - }); + this.stateService.resetBlocks(blocks); + const maxHeight = blocks.reduce((max, block) => Math.max(max, block.height), this.stateService.latestBlockHeight); this.stateService.updateChainTip(maxHeight); } @@ -260,7 +255,8 @@ export class WebsocketService { if (response.block) { if (response.block.height === this.stateService.latestBlockHeight + 1) { this.stateService.updateChainTip(response.block.height); - this.stateService.blocks$.next([response.block, response.txConfirmed || '']); + this.stateService.addBlock(response.block); + this.stateService.txConfirmed$.next([response.txConfirmed, response.block]); } else if (response.block.height > this.stateService.latestBlockHeight + 1) { reinitBlocks = true; } From 842ac8ce39dbaed04eb2c6da3c2a1cbe0f313063 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 10 Jul 2023 13:57:18 +0900 Subject: [PATCH 17/28] Add stale block banner immediately on reorg --- .../src/app/components/block/block.component.ts | 3 +++ .../blockchain-blocks.component.ts | 14 +++++++++++--- .../transaction/transaction.component.ts | 8 +++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 720203220..faadd7b5c 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -142,6 +142,9 @@ export class BlockComponent implements OnInit, OnDestroy { if (block?.extras?.reward != undefined) { this.fees = block.extras.reward / 100000000 - this.blockSubsidy; } + } else if (block.height === this.block.height) { + this.block.stale = true; + this.block.canonical = block.id; } } }); diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts index 23efb0c78..f9f25315f 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts @@ -36,6 +36,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { emptyBlocks: BlockExtended[] = this.mountEmptyBlocks(); markHeight: number; chainTip: number; + pendingMarkBlock: { animate: boolean, newBlockFromLeft: boolean }; blocksSubscription: Subscription; blockPageSubscription: Subscription; networkSubscription: Subscription; @@ -83,7 +84,6 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { } ngOnInit() { - this.chainTip = this.stateService.latestBlockHeight; this.dynamicBlocksAmount = Math.min(8, this.stateService.env.KEEP_BLOCKS_AMOUNT); if (['', 'testnet', 'signet'].includes(this.stateService.network)) { @@ -110,7 +110,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { return; } const latestHeight = blocks[0].height; - const animate = latestHeight > blocks[0].height; + const animate = this.chainTip != null && latestHeight > this.chainTip; for (const block of blocks) { block.extras.minFee = this.getMinBlockFee(block); @@ -132,6 +132,11 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { } this.chainTip = latestHeight; + + if (this.pendingMarkBlock) { + this.moveArrowToPosition(this.pendingMarkBlock.animate, this.pendingMarkBlock.newBlockFromLeft); + this.pendingMarkBlock = null; + } this.cd.markForCheck(); }); @@ -202,7 +207,10 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { this.arrowVisible = false; return; } - const blockindex = this.blocks.findIndex((b) => b.height === this.markHeight); + if (this.chainTip == null) { + this.pendingMarkBlock = { animate, newBlockFromLeft }; + } + const blockindex = this.blocks.findIndex((b) => { console.log(b); return b.height === this.markHeight }); if (blockindex > -1) { if (!animate) { this.arrowTransition = 'inherit'; diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 684f343eb..0ae1ea3c3 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -12,7 +12,7 @@ import { tap } from 'rxjs/operators'; import { Transaction } from '../../interfaces/electrs.interface'; -import { of, merge, Subscription, Observable, Subject, timer, from, throwError } from 'rxjs'; +import { of, merge, Subscription, Observable, Subject, from, throwError } from 'rxjs'; import { StateService } from '../../services/state.service'; import { CacheService } from '../../services/cache.service'; import { WebsocketService } from '../../services/websocket.service'; @@ -52,6 +52,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { queryParamsSubscription: Subscription; urlFragmentSubscription: Subscription; mempoolBlocksSubscription: Subscription; + blocksSubscription: Subscription; fragmentParams: URLSearchParams; rbfTransaction: undefined | Transaction; replaced: boolean = false; @@ -130,6 +131,10 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.outputIndex = (!isNaN(vout) && vout >= 0) ? vout : null; }); + this.blocksSubscription = this.stateService.blocks$.subscribe((blocks) => { + this.latestBlock = blocks[0]; + }); + this.fetchCpfpSubscription = this.fetchCpfp$ .pipe( switchMap((txId) => @@ -596,6 +601,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.mempoolBlocksSubscription.unsubscribe(); this.mempoolPositionSubscription.unsubscribe(); this.mempoolBlocksSubscription.unsubscribe(); + this.blocksSubscription.unsubscribe(); this.leaveTransaction(); } } From 7230b65dc31751f4573f0e1175dc38ca0a92bc22 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 11 Jul 2023 16:35:00 +0900 Subject: [PATCH 18/28] remove console.log, fix null blocks --- .../src/app/components/block/block.component.ts | 2 +- .../blockchain-blocks.component.ts | 2 +- frontend/src/app/services/state.service.ts | 13 ++++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index faadd7b5c..5a24ebab8 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -142,7 +142,7 @@ export class BlockComponent implements OnInit, OnDestroy { if (block?.extras?.reward != undefined) { this.fees = block.extras.reward / 100000000 - this.blockSubsidy; } - } else if (block.height === this.block.height) { + } else if (block.height === this.block?.height) { this.block.stale = true; this.block.canonical = block.id; } diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts index f9f25315f..245973885 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts @@ -210,7 +210,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { if (this.chainTip == null) { this.pendingMarkBlock = { animate, newBlockFromLeft }; } - const blockindex = this.blocks.findIndex((b) => { console.log(b); return b.height === this.markHeight }); + const blockindex = this.blocks.findIndex((b) => b.height === this.markHeight); if (blockindex > -1) { if (!animate) { this.arrowTransition = 'inherit'; diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 4b1323939..a3392d738 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -5,7 +5,7 @@ import { IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommended import { BlockExtended, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface'; import { Router, NavigationStart } from '@angular/router'; import { isPlatformBrowser } from '@angular/common'; -import { map, scan, shareReplay } from 'rxjs/operators'; +import { filter, map, scan, shareReplay } from 'rxjs/operators'; import { StorageService } from './storage.service'; export interface MarkBlockState { @@ -94,7 +94,8 @@ export class StateService { networkChanged$ = new ReplaySubject(1); lightningChanged$ = new ReplaySubject(1); - blocks$ = new BehaviorSubject([]); + blocksSubject$ = new BehaviorSubject([]); + blocks$: Observable; transactions$ = new ReplaySubject(6); conversions$ = new ReplaySubject(1); bsqPrice$ = new ReplaySubject(1); @@ -200,11 +201,13 @@ export class StateService { this.networkChanged$.subscribe((network) => { this.transactions$ = new ReplaySubject(6); - this.blocks$ = new ReplaySubject<[BlockExtended, string]>(this.env.KEEP_BLOCKS_AMOUNT); + this.blocksSubject$ = new BehaviorSubject([]); }); this.blockVSize = this.env.BLOCK_WEIGHT_UNITS / 4; + this.blocks$ = this.blocksSubject$.pipe(filter(blocks => blocks != null && blocks.length > 0)); + const savedTimePreference = this.storageService.getValue('time-preference-ltr'); const rtlLanguage = (this.locale.startsWith('ar') || this.locale.startsWith('fa') || this.locale.startsWith('he')); // default time direction is right-to-left, unless locale is a RTL language @@ -344,12 +347,12 @@ export class StateService { resetBlocks(blocks: BlockExtended[]): void { this.blocks = blocks.reverse(); - this.blocks$.next(blocks); + this.blocksSubject$.next(blocks); } addBlock(block: BlockExtended): void { this.blocks.unshift(block); this.blocks = this.blocks.slice(0, this.env.KEEP_BLOCKS_AMOUNT); - this.blocks$.next(this.blocks); + this.blocksSubject$.next(this.blocks); } } From 886a099a2fd026d9c2051ba51ba6b2f2f168c8cb Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 12 Jul 2023 16:25:00 +0900 Subject: [PATCH 19/28] Detect stale blocks from client blockchain cache --- .../app/components/block/block.component.ts | 22 +++++++++++++++++++ .../src/app/services/websocket.service.ts | 5 +++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 5a24ebab8..84028820c 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -14,6 +14,7 @@ import { ApiService } from '../../services/api.service'; import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component'; import { detectWebGL } from '../../shared/graphs.utils'; import { PriceService, Price } from '../../services/price.service'; +import { CacheService } from '../../services/cache.service'; @Component({ selector: 'app-block', @@ -72,6 +73,7 @@ export class BlockComponent implements OnInit, OnDestroy { auditSubscription: Subscription; keyNavigationSubscription: Subscription; blocksSubscription: Subscription; + cacheBlocksSubscription: Subscription; networkChangedSubscription: Subscription; queryParamsSubscription: Subscription; nextBlockSubscription: Subscription = undefined; @@ -99,6 +101,7 @@ export class BlockComponent implements OnInit, OnDestroy { private relativeUrlPipe: RelativeUrlPipe, private apiService: ApiService, private priceService: PriceService, + private cacheService: CacheService, ) { this.webGlEnabled = detectWebGL(); } @@ -128,6 +131,10 @@ export class BlockComponent implements OnInit, OnDestroy { map((indicators) => indicators['blocktxs-' + this.blockHash] !== undefined ? indicators['blocktxs-' + this.blockHash] : 0) ); + this.cacheBlocksSubscription = this.cacheService.loadedBlocks$.subscribe((block) => { + this.loadedCacheBlock(block); + }); + this.blocksSubscription = this.stateService.blocks$ .subscribe((blocks) => { this.latestBlock = blocks[0]; @@ -258,6 +265,13 @@ export class BlockComponent implements OnInit, OnDestroy { this.transactionsError = null; this.isLoadingOverview = true; this.overviewError = null; + + const cachedBlock = this.cacheService.getCachedBlock(block.height); + if (!cachedBlock) { + this.cacheService.loadBlock(block.height); + } else { + this.loadedCacheBlock(cachedBlock); + } }), throttleTime(300, asyncScheduler, { leading: true, trailing: true }), shareReplay(1) @@ -463,6 +477,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.auditSubscription?.unsubscribe(); this.keyNavigationSubscription?.unsubscribe(); this.blocksSubscription?.unsubscribe(); + this.cacheBlocksSubscription?.unsubscribe(); this.networkChangedSubscription?.unsubscribe(); this.queryParamsSubscription?.unsubscribe(); this.timeLtrSubscription?.unsubscribe(); @@ -683,4 +698,11 @@ export class BlockComponent implements OnInit, OnDestroy { } return 0; } + + loadedCacheBlock(block: BlockExtended): void { + if (block.height === this.block.height && block.id !== this.block.id) { + this.block.stale = true; + this.block.canonical = block.id; + } + } } \ No newline at end of file diff --git a/frontend/src/app/services/websocket.service.ts b/frontend/src/app/services/websocket.service.ts index 472501384..7eed09e77 100644 --- a/frontend/src/app/services/websocket.service.ts +++ b/frontend/src/app/services/websocket.service.ts @@ -1,13 +1,13 @@ import { Injectable } from '@angular/core'; import { webSocket, WebSocketSubject } from 'rxjs/webSocket'; -import { WebsocketResponse, IBackendInfo } from '../interfaces/websocket.interface'; +import { WebsocketResponse } from '../interfaces/websocket.interface'; import { StateService } from './state.service'; import { Transaction } from '../interfaces/electrs.interface'; import { Subscription } from 'rxjs'; import { ApiService } from './api.service'; import { take } from 'rxjs/operators'; import { TransferState, makeStateKey } from '@angular/platform-browser'; -import { BlockExtended } from '../interfaces/node-api.interface'; +import { CacheService } from './cache.service'; const OFFLINE_RETRY_AFTER_MS = 2000; const OFFLINE_PING_CHECK_AFTER_MS = 30000; @@ -40,6 +40,7 @@ export class WebsocketService { private stateService: StateService, private apiService: ApiService, private transferState: TransferState, + private cacheService: CacheService, ) { if (!this.stateService.isBrowser) { // @ts-ignore From 1e69ea2f1d7cbc3c7ab3250fb4656006528b2f1b Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 13 Jul 2023 11:03:44 +0900 Subject: [PATCH 20/28] Fix merge conflicts --- .../blocks-list/blocks-list.component.ts | 8 ++++---- frontend/src/app/dashboard/dashboard.component.ts | 15 ++++----------- frontend/src/app/services/state.service.ts | 2 +- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.ts b/frontend/src/app/components/blocks-list/blocks-list.component.ts index 324807628..2b54058e8 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -82,12 +82,12 @@ export class BlocksList implements OnInit { ), this.stateService.blocks$ .pipe( - switchMap((block) => { - if (block[0].height <= this.lastBlockHeight) { + switchMap((blocks) => { + if (blocks[0].height <= this.lastBlockHeight) { return [null]; // Return an empty stream so the last pipe is not executed } - this.lastBlockHeight = block[0].height; - return [block]; + this.lastBlockHeight = blocks[0].height; + return blocks; }) ) ]) diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index 74a48c74c..6cf487be6 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -135,23 +135,16 @@ export class DashboardComponent implements OnInit, OnDestroy { tap((blocks) => { this.latestBlockHeight = blocks[0].height; }), - scan((acc, [block]) => { - if (acc.find((b) => b.height == block.height)) { - return acc; - } - acc.unshift(block); - acc = acc.slice(0, 6); - + switchMap((blocks) => { if (this.stateService.env.MINING_DASHBOARD === true) { - for (const block of acc) { + for (const block of blocks) { // @ts-ignore: Need to add an extra field for the template block.extras.pool.logo = `/resources/mining-pools/` + block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; } } - - return acc; - }, []), + return of(blocks.slice(0, 6)); + }) ); this.transactions$ = this.stateService.transactions$ diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index a3392d738..f38600605 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -201,7 +201,7 @@ export class StateService { this.networkChanged$.subscribe((network) => { this.transactions$ = new ReplaySubject(6); - this.blocksSubject$ = new BehaviorSubject([]); + this.blocksSubject$.next([]); }); this.blockVSize = this.env.BLOCK_WEIGHT_UNITS / 4; From eccbcbe53b557d0860104ac291f8d90c08ed78a5 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 13 Jul 2023 11:58:29 +0900 Subject: [PATCH 21/28] Add missing this.block null check --- frontend/src/app/components/block/block.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 84028820c..0d733ff6b 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -700,7 +700,7 @@ export class BlockComponent implements OnInit, OnDestroy { } loadedCacheBlock(block: BlockExtended): void { - if (block.height === this.block.height && block.id !== this.block.id) { + if (this.block && block.height === this.block.height && block.id !== this.block.id) { this.block.stale = true; this.block.canonical = block.id; } From 3287c62f9133714d88fc8fcb6a4c67523a5ce996 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 13 Jul 2023 10:42:33 +0900 Subject: [PATCH 22/28] highlight & tag fullrbf replacements in RBF timeline --- backend/src/api/rbf-cache.ts | 2 +- .../rbf-timeline-tooltip.component.html | 1 + .../rbf-timeline-tooltip.component.ts | 4 +- .../rbf-timeline/rbf-timeline.component.html | 11 +++-- .../rbf-timeline/rbf-timeline.component.scss | 25 ++++++++-- .../rbf-timeline/rbf-timeline.component.ts | 48 ++++++++++++++----- .../src/app/interfaces/node-api.interface.ts | 4 +- 7 files changed, 69 insertions(+), 26 deletions(-) diff --git a/backend/src/api/rbf-cache.ts b/backend/src/api/rbf-cache.ts index 79d5ff2d1..a3714406f 100644 --- a/backend/src/api/rbf-cache.ts +++ b/backend/src/api/rbf-cache.ts @@ -55,7 +55,7 @@ class RbfCache { if (tree) { tree.interval = newTime - tree?.time; replacedTrees.push(tree); - fullRbf = fullRbf || tree.fullRbf; + fullRbf = fullRbf || tree.fullRbf || !tree.tx.rbf; } } } else { diff --git a/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.html b/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.html index 68f8a1caf..540da7480 100644 --- a/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.html +++ b/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.html @@ -32,6 +32,7 @@
Status + Full RBF RBF RBF Mined diff --git a/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.ts b/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.ts index b9da63c86..fc3748f32 100644 --- a/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.ts +++ b/frontend/src/app/components/rbf-timeline/rbf-timeline-tooltip.component.ts @@ -1,5 +1,5 @@ import { Component, ElementRef, ViewChild, Input, OnChanges } from '@angular/core'; -import { RbfInfo } from '../../interfaces/node-api.interface'; +import { RbfTree } from '../../interfaces/node-api.interface'; @Component({ selector: 'app-rbf-timeline-tooltip', @@ -7,7 +7,7 @@ import { RbfInfo } from '../../interfaces/node-api.interface'; styleUrls: ['./rbf-timeline-tooltip.component.scss'], }) export class RbfTimelineTooltipComponent implements OnChanges { - @Input() rbfInfo: RbfInfo | void; + @Input() rbfInfo: RbfTree | null; @Input() cursorPosition: { x: number, y: number }; tooltipPosition = null; diff --git a/frontend/src/app/components/rbf-timeline/rbf-timeline.component.html b/frontend/src/app/components/rbf-timeline/rbf-timeline.component.html index ce5a9678f..a2012d45f 100644 --- a/frontend/src/app/components/rbf-timeline/rbf-timeline.component.html +++ b/frontend/src/app/components/rbf-timeline/rbf-timeline.component.html @@ -15,14 +15,15 @@
- +
-
+
+
-
-
+
+
-
+
diff --git a/frontend/src/app/components/rbf-timeline/rbf-timeline.component.scss b/frontend/src/app/components/rbf-timeline/rbf-timeline.component.scss index 3745360a5..be7aef2d6 100644 --- a/frontend/src/app/components/rbf-timeline/rbf-timeline.component.scss +++ b/frontend/src/app/components/rbf-timeline/rbf-timeline.component.scss @@ -83,15 +83,26 @@ transform: translateY(-50%); background: #105fb0; border-radius: 5px; + + &.left { + right: 50%; + } + &.right { + left: 50%; + } + + &.fullrbf { + background: #1bd8f4; + } } &.first-node { - .track { - left: 50%; + .track.left { + display: none; } } &:last-child { - .track { - right: 50%; + .track.right { + display: none; } } } @@ -177,11 +188,17 @@ height: 108px; bottom: 50%; border-right: solid 10px #105fb0; + &.fullrbf { + border-right: solid 10px #1bd8f4; + } } .corner { border-bottom: solid 10px #105fb0; border-bottom-right-radius: 10px; + &.fullrbf { + border-bottom: solid 10px #1bd8f4; + } } } } diff --git a/frontend/src/app/components/rbf-timeline/rbf-timeline.component.ts b/frontend/src/app/components/rbf-timeline/rbf-timeline.component.ts index f02e8ca35..474da7326 100644 --- a/frontend/src/app/components/rbf-timeline/rbf-timeline.component.ts +++ b/frontend/src/app/components/rbf-timeline/rbf-timeline.component.ts @@ -1,15 +1,20 @@ import { Component, Input, OnInit, OnChanges, Inject, LOCALE_ID, HostListener } from '@angular/core'; import { Router } from '@angular/router'; -import { RbfInfo, RbfTree } from '../../interfaces/node-api.interface'; +import { RbfTree, RbfTransaction } from '../../interfaces/node-api.interface'; import { StateService } from '../../services/state.service'; import { ApiService } from '../../services/api.service'; type Connector = 'pipe' | 'corner'; interface TimelineCell { - replacement?: RbfInfo, + replacement?: RbfTree, connector?: Connector, first?: boolean, + fullRbf?: boolean, +} + +function isTimelineCell(val: RbfTree | TimelineCell): boolean { + return !val || !('tx' in val); } @Component({ @@ -22,7 +27,7 @@ export class RbfTimelineComponent implements OnInit, OnChanges { @Input() txid: string; rows: TimelineCell[][] = []; - hoverInfo: RbfInfo | void = null; + hoverInfo: RbfTree | null = null; tooltipPosition = null; dir: 'rtl' | 'ltr' = 'ltr'; @@ -53,13 +58,27 @@ export class RbfTimelineComponent implements OnInit, OnChanges { buildTimelines(tree: RbfTree): TimelineCell[][] { if (!tree) return []; + this.flagFullRbf(tree); const split = this.splitTimelines(tree); const timelines = this.prepareTimelines(split); return this.connectTimelines(timelines); } + // sets the fullRbf flag on each transaction in the tree + flagFullRbf(tree: RbfTree): void { + let fullRbf = false; + for (const replaced of tree.replaces) { + if (!replaced.tx.rbf) { + fullRbf = true; + } + replaced.replacedBy = tree.tx; + this.flagFullRbf(replaced); + } + tree.tx.fullRbf = fullRbf; + } + // splits a tree into N leaf-to-root paths - splitTimelines(tree: RbfTree, tail: RbfInfo[] = []): RbfInfo[][] { + splitTimelines(tree: RbfTree, tail: RbfTree[] = []): RbfTree[][] { const replacements = [...tail, tree]; if (tree.replaces.length) { return [].concat(...tree.replaces.map(subtree => this.splitTimelines(subtree, replacements))); @@ -70,7 +89,7 @@ export class RbfTimelineComponent implements OnInit, OnChanges { // merges separate leaf-to-root paths into a coherent forking timeline // represented as a 2D array of Rbf events - prepareTimelines(lines: RbfInfo[][]): RbfInfo[][] { + prepareTimelines(lines: RbfTree[][]): (RbfTree | TimelineCell)[][] { lines.sort((a, b) => b.length - a.length); const rows = lines.map(() => []); @@ -85,7 +104,7 @@ export class RbfTimelineComponent implements OnInit, OnChanges { let emptyCount = 0; const nextGroups = []; for (const group of lineGroups) { - const toMerge: { [txid: string]: RbfInfo[][] } = {}; + const toMerge: { [txid: string]: RbfTree[][] } = {}; let emptyInGroup = 0; let first = true; for (const line of group) { @@ -97,7 +116,7 @@ export class RbfTimelineComponent implements OnInit, OnChanges { } else { // substitute duplicates with empty cells // (we'll fill these in with connecting lines later) - rows[index].unshift(null); + rows[index].unshift({ connector: true, replacement: head }); } // group the tails of the remaining lines for the next iteration if (line.length) { @@ -127,7 +146,7 @@ export class RbfTimelineComponent implements OnInit, OnChanges { } // annotates a 2D timeline array with info needed to draw connecting lines for multi-replacements - connectTimelines(timelines: RbfInfo[][]): TimelineCell[][] { + connectTimelines(timelines: (RbfTree | TimelineCell)[][]): TimelineCell[][] { const rows: TimelineCell[][] = []; timelines.forEach((lines, row) => { rows.push([]); @@ -135,11 +154,12 @@ export class RbfTimelineComponent implements OnInit, OnChanges { let finished = false; lines.forEach((replacement, column) => { const cell: TimelineCell = {}; - if (replacement) { - cell.replacement = replacement; + if (!isTimelineCell(replacement)) { + cell.replacement = replacement as RbfTree; + cell.fullRbf = (replacement as RbfTree).replacedBy?.fullRbf; } rows[row].push(cell); - if (replacement) { + if (!isTimelineCell(replacement)) { if (!started) { cell.first = true; started = true; @@ -153,11 +173,13 @@ export class RbfTimelineComponent implements OnInit, OnChanges { matched = true; } else if (i === row) { rows[i][column] = { - connector: 'corner' + connector: 'corner', + fullRbf: (replacement as TimelineCell).replacement.tx.fullRbf, }; } else if (nextCell.connector !== 'corner') { rows[i][column] = { - connector: 'pipe' + connector: 'pipe', + fullRbf: (replacement as TimelineCell).replacement.tx.fullRbf, }; } } diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts index 82e1ae50d..7a8ab3f06 100644 --- a/frontend/src/app/interfaces/node-api.interface.ts +++ b/frontend/src/app/interfaces/node-api.interface.ts @@ -39,6 +39,7 @@ export interface RbfTree extends RbfInfo { mined?: boolean; fullRbf: boolean; replaces: RbfTree[]; + replacedBy?: RbfTransaction; } export interface DifficultyAdjustment { @@ -176,9 +177,10 @@ export interface TransactionStripped { context?: 'projected' | 'actual'; } -interface RbfTransaction extends TransactionStripped { +export interface RbfTransaction extends TransactionStripped { rbf?: boolean; mined?: boolean, + fullRbf?: boolean, } export interface MempoolPosition { block: number, From 21a47a7b4bdcbb00f50064b0cfab052ca53d3c2c Mon Sep 17 00:00:00 2001 From: junderw Date: Wed, 19 Apr 2023 18:10:10 -0700 Subject: [PATCH 23/28] Push TX: Include validation to prevent DoS --- backend/src/api/bitcoin/bitcoin.routes.ts | 13 +--- backend/src/api/common.ts | 85 +++++++++++++++++++++++ backend/src/config.ts | 4 ++ 3 files changed, 91 insertions(+), 11 deletions(-) diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index 8f31e152d..17ebc9275 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -723,12 +723,7 @@ class BitcoinRoutes { private async $postTransaction(req: Request, res: Response) { res.setHeader('content-type', 'text/plain'); try { - let rawTx; - if (typeof req.body === 'object') { - rawTx = Object.keys(req.body)[0]; - } else { - rawTx = req.body; - } + const rawTx = Common.getTransactionFromRequest(req, false); const txIdResult = await bitcoinApi.$sendRawTransaction(rawTx); res.send(txIdResult); } catch (e: any) { @@ -739,12 +734,8 @@ class BitcoinRoutes { private async $postTransactionForm(req: Request, res: Response) { res.setHeader('content-type', 'text/plain'); - const matches = /tx=([a-z0-9]+)/.exec(req.body); - let txHex = ''; - if (matches && matches[1]) { - txHex = matches[1]; - } try { + const txHex = Common.getTransactionFromRequest(req, true); const txIdResult = await bitcoinClient.sendRawTransaction(txHex); res.send(txIdResult); } catch (e: any) { diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts index 735e240c1..b854c1701 100644 --- a/backend/src/api/common.ts +++ b/backend/src/api/common.ts @@ -1,3 +1,5 @@ +import * as bitcoinjs from 'bitcoinjs-lib'; +import { Request } from 'express'; import { Ancestor, CpfpInfo, CpfpSummary, CpfpCluster, EffectiveFeeStats, MempoolBlockWithTransactions, TransactionExtended, MempoolTransactionExtended, TransactionStripped, WorkingEffectiveFeeStats } from '../mempool.interfaces'; import config from '../config'; import { NodeSocket } from '../repositories/NodesSocketsRepository'; @@ -511,6 +513,89 @@ export class Common { static getNthPercentile(n: number, sortedDistribution: any[]): any { return sortedDistribution[Math.floor((sortedDistribution.length - 1) * (n / 100))]; } + + static getTransactionFromRequest(req: Request, form: boolean): string { + let rawTx: any = typeof req.body === 'object' && form + ? Object.values(req.body)[0] as any + : req.body; + if (typeof rawTx !== 'string') { + throw Object.assign(new Error('Non-string request body'), { code: -1 }); + } + + // Support both upper and lower case hex + // Support both txHash= Form and direct API POST + const reg = form ? /^txHash=((?:[a-fA-F0-9]{2})+)$/ : /^((?:[a-fA-F0-9]{2})+)$/; + const matches = reg.exec(rawTx); + if (!matches || !matches[1]) { + throw Object.assign(new Error('Non-hex request body'), { code: -2 }); + } + + // Guaranteed to be a hex string of multiple of 2 + // Guaranteed to be lower case + // Guaranteed to pass validation (see function below) + return this.validateTransactionHex(matches[1].toLowerCase()); + } + + private static validateTransactionHex(txhex: string): string { + // Do not mutate txhex + + // We assume txhex to be valid hex (output of getTransactionFromRequest above) + + // Check 1: Valid transaction parse + let tx: bitcoinjs.Transaction; + try { + tx = bitcoinjs.Transaction.fromHex(txhex); + } catch(e) { + throw Object.assign(new Error('Invalid transaction (could not parse)'), { code: -4 }); + } + + // Check 2: Simple size check + if (tx.weight() > config.MEMPOOL.MAX_PUSH_TX_SIZE_WEIGHT) { + throw Object.assign(new Error(`Transaction too large (max ${config.MEMPOOL.MAX_PUSH_TX_SIZE_WEIGHT} weight units)`), { code: -3 }); + } + + // Check 3: Check unreachable script in taproot (if not allowed) + if (!config.MEMPOOL.ALLOW_UNREACHABLE) { + tx.ins.forEach(input => { + const witness = input.witness; + // See BIP 341: Script validation rules + const hasAnnex = witness.length >= 2 && + witness[witness.length - 1].length > 1 && + witness[witness.length - 1][0] === 0x50; + const scriptSpendMinLength = hasAnnex ? 3 : 2; + const maybeScriptSpend = witness.length >= scriptSpendMinLength; + + if (maybeScriptSpend) { + const controlBlock = witness[witness.length - scriptSpendMinLength + 1]; + if (controlBlock.length === 0 || (controlBlock[0] & 0xfe) < 0xc0) { + // Skip this input, it's not taproot + return; + } + // Definitely taproot. Get script + const script = witness[witness.length - scriptSpendMinLength]; + const decompiled = bitcoinjs.script.decompile(script); + if (!decompiled || decompiled.length < 2) { + // Skip this input + return; + } + // Iterate up to second last (will look ahead 1 item) + for (let i = 0; i < decompiled.length - 1; i++) { + const first = decompiled[i]; + const second = decompiled[i + 1]; + if ( + first === bitcoinjs.opcodes.OP_FALSE && + second === bitcoinjs.opcodes.OP_IF + ) { + throw Object.assign(new Error('Unreachable taproot scripts not allowed'), { code: -5 }); + } + } + } + }) + } + + // Pass through the input string untouched + return txhex; + } } /** diff --git a/backend/src/config.ts b/backend/src/config.ts index fd7d7bc28..40b407a57 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -35,6 +35,8 @@ interface IConfig { CPFP_INDEXING: boolean; MAX_BLOCKS_BULK_QUERY: number; DISK_CACHE_BLOCK_INTERVAL: number; + MAX_PUSH_TX_SIZE_WEIGHT: number; + ALLOW_UNREACHABLE: boolean; }; ESPLORA: { REST_API_URL: string; @@ -165,6 +167,8 @@ const defaults: IConfig = { 'CPFP_INDEXING': false, 'MAX_BLOCKS_BULK_QUERY': 0, 'DISK_CACHE_BLOCK_INTERVAL': 6, + 'MAX_PUSH_TX_SIZE_WEIGHT': 400000, + 'ALLOW_UNREACHABLE': true, }, 'ESPLORA': { 'REST_API_URL': 'http://127.0.0.1:3000', From 95a8752a0af320eda3ddd781d4d646dd6fccdebb Mon Sep 17 00:00:00 2001 From: junderw Date: Wed, 19 Apr 2023 18:19:27 -0700 Subject: [PATCH 24/28] Fix: Tests for config --- backend/src/__fixtures__/mempool-config.template.json | 6 ++++-- backend/src/__tests__/config.test.ts | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/src/__fixtures__/mempool-config.template.json b/backend/src/__fixtures__/mempool-config.template.json index 62b2e5f45..600c5e430 100644 --- a/backend/src/__fixtures__/mempool-config.template.json +++ b/backend/src/__fixtures__/mempool-config.template.json @@ -30,7 +30,9 @@ "RUST_GBT": false, "CPFP_INDEXING": true, "MAX_BLOCKS_BULK_QUERY": 999, - "DISK_CACHE_BLOCK_INTERVAL": 999 + "DISK_CACHE_BLOCK_INTERVAL": 999, + "MAX_PUSH_TX_SIZE_WEIGHT": "__MEMPOOL_MAX_PUSH_TX_SIZE_WEIGHT__", + "ALLOW_UNREACHABLE": "__MEMPOOL_ALLOW_UNREACHABLE__" }, "CORE_RPC": { "HOST": "__CORE_RPC_HOST__", @@ -120,4 +122,4 @@ "CLIGHTNING": { "SOCKET": "__CLIGHTNING_SOCKET__" } -} \ No newline at end of file +} diff --git a/backend/src/__tests__/config.test.ts b/backend/src/__tests__/config.test.ts index 937011ba2..fdd8a02de 100644 --- a/backend/src/__tests__/config.test.ts +++ b/backend/src/__tests__/config.test.ts @@ -44,6 +44,8 @@ describe('Mempool Backend Config', () => { CPFP_INDEXING: false, MAX_BLOCKS_BULK_QUERY: 0, DISK_CACHE_BLOCK_INTERVAL: 6, + MAX_PUSH_TX_SIZE_WEIGHT: 400000, + ALLOW_UNREACHABLE: true, }); expect(config.ELECTRUM).toStrictEqual({ HOST: '127.0.0.1', PORT: 3306, TLS_ENABLED: true }); From 43d41fca95423ba39f48a29991bc08e5e0d7f835 Mon Sep 17 00:00:00 2001 From: junderw Date: Thu, 13 Jul 2023 13:31:57 +0900 Subject: [PATCH 25/28] Fix: Allow detection of 1 byte annexes --- backend/src/api/common.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts index b854c1701..49d2c0458 100644 --- a/backend/src/api/common.ts +++ b/backend/src/api/common.ts @@ -560,7 +560,6 @@ export class Common { const witness = input.witness; // See BIP 341: Script validation rules const hasAnnex = witness.length >= 2 && - witness[witness.length - 1].length > 1 && witness[witness.length - 1][0] === 0x50; const scriptSpendMinLength = hasAnnex ? 3 : 2; const maybeScriptSpend = witness.length >= scriptSpendMinLength; From df70ea05c6543d1ea76b4f30b8e3128a2b47fff8 Mon Sep 17 00:00:00 2001 From: junderw Date: Thu, 13 Jul 2023 13:50:54 +0900 Subject: [PATCH 26/28] Fix: Leaf version validation --- backend/src/api/common.ts | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/backend/src/api/common.ts b/backend/src/api/common.ts index 49d2c0458..cd9da3d2a 100644 --- a/backend/src/api/common.ts +++ b/backend/src/api/common.ts @@ -566,7 +566,7 @@ export class Common { if (maybeScriptSpend) { const controlBlock = witness[witness.length - scriptSpendMinLength + 1]; - if (controlBlock.length === 0 || (controlBlock[0] & 0xfe) < 0xc0) { + if (controlBlock.length === 0 || !this.isValidLeafVersion(controlBlock[0])) { // Skip this input, it's not taproot return; } @@ -595,6 +595,33 @@ export class Common { // Pass through the input string untouched return txhex; } + + private static isValidLeafVersion(leafVersion: number): boolean { + // See Note 7 in BIP341 + // https://github.com/bitcoin/bips/blob/66a1a8151021913047934ebab3f8883f2f8ca75b/bip-0341.mediawiki#cite_note-7 + // "What constraints are there on the leaf version?" + + // Must be an integer between 0 and 255 + // Since we're parsing a byte + if (Math.floor(leafVersion) !== leafVersion || leafVersion < 0 || leafVersion > 255) { + return false; + } + // "the leaf version cannot be odd" + if ((leafVersion & 0x01) === 1) { + return false; + } + // "The values that comply to this rule are + // the 32 even values between 0xc0 and 0xfe + if (leafVersion >= 0xc0 && leafVersion <= 0xfe) { + return true; + } + // and also 0x66, 0x7e, 0x80, 0x84, 0x96, 0x98, 0xba, 0xbc, 0xbe." + if ([0x66, 0x7e, 0x80, 0x84, 0x96, 0x98, 0xba, 0xbc, 0xbe].includes(leafVersion)) { + return true; + } + // Otherwise, invalid + return false; + } } /** From 222b34993b584a9727a61ffdafd7e6bb32e7c2a5 Mon Sep 17 00:00:00 2001 From: junderw Date: Thu, 13 Jul 2023 14:06:46 +0900 Subject: [PATCH 27/28] Fix: Add new configs to all config instances properly. --- backend/mempool-config.sample.json | 4 +++- backend/src/__fixtures__/mempool-config.template.json | 4 ++-- docker/backend/mempool-config.json | 4 +++- docker/backend/start.sh | 5 +++++ production/mempool-config.mainnet.json | 4 +++- production/mempool-config.signet.json | 4 +++- production/mempool-config.testnet.json | 4 +++- 7 files changed, 22 insertions(+), 7 deletions(-) diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index 3371a8587..c0a2d9d62 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -29,7 +29,9 @@ "ADVANCED_GBT_MEMPOOL": false, "RUST_GBT": false, "CPFP_INDEXING": false, - "DISK_CACHE_BLOCK_INTERVAL": 6 + "DISK_CACHE_BLOCK_INTERVAL": 6, + "MAX_PUSH_TX_SIZE_WEIGHT": 4000000, + "ALLOW_UNREACHABLE": true }, "CORE_RPC": { "HOST": "127.0.0.1", diff --git a/backend/src/__fixtures__/mempool-config.template.json b/backend/src/__fixtures__/mempool-config.template.json index 600c5e430..776f01de1 100644 --- a/backend/src/__fixtures__/mempool-config.template.json +++ b/backend/src/__fixtures__/mempool-config.template.json @@ -31,8 +31,8 @@ "CPFP_INDEXING": true, "MAX_BLOCKS_BULK_QUERY": 999, "DISK_CACHE_BLOCK_INTERVAL": 999, - "MAX_PUSH_TX_SIZE_WEIGHT": "__MEMPOOL_MAX_PUSH_TX_SIZE_WEIGHT__", - "ALLOW_UNREACHABLE": "__MEMPOOL_ALLOW_UNREACHABLE__" + "MAX_PUSH_TX_SIZE_WEIGHT": 4000000, + "ALLOW_UNREACHABLE": true }, "CORE_RPC": { "HOST": "__CORE_RPC_HOST__", diff --git a/docker/backend/mempool-config.json b/docker/backend/mempool-config.json index 45f95a53e..d070d8010 100644 --- a/docker/backend/mempool-config.json +++ b/docker/backend/mempool-config.json @@ -29,6 +29,8 @@ "CPFP_INDEXING": __MEMPOOL_CPFP_INDEXING__, "MAX_BLOCKS_BULK_QUERY": __MEMPOOL_MAX_BLOCKS_BULK_QUERY__, "DISK_CACHE_BLOCK_INTERVAL": __MEMPOOL_DISK_CACHE_BLOCK_INTERVAL__, + "MAX_PUSH_TX_SIZE_WEIGHT": __MEMPOOL_MAX_PUSH_TX_SIZE_WEIGHT__, + "ALLOW_UNREACHABLE": __MEMPOOL_ALLOW_UNREACHABLE__, "POOLS_JSON_TREE_URL": "__MEMPOOL_POOLS_JSON_TREE_URL__", "POOLS_JSON_URL": "__MEMPOOL_POOLS_JSON_URL__" }, @@ -126,4 +128,4 @@ "GEOLITE2_ASN": "__MAXMIND_GEOLITE2_ASN__", "GEOIP2_ISP": "__MAXMIND_GEOIP2_ISP__" } -} \ No newline at end of file +} diff --git a/docker/backend/start.sh b/docker/backend/start.sh index b746512a9..7241444fb 100755 --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -32,6 +32,9 @@ __MEMPOOL_RUST_GBT__=${MEMPOOL_RUST_GBT:=false} __MEMPOOL_CPFP_INDEXING__=${MEMPOOL_CPFP_INDEXING:=false} __MEMPOOL_MAX_BLOCKS_BULK_QUERY__=${MEMPOOL_MAX_BLOCKS_BULK_QUERY:=0} __MEMPOOL_DISK_CACHE_BLOCK_INTERVAL__=${MEMPOOL_DISK_CACHE_BLOCK_INTERVAL:=6} +__MEMPOOL_MAX_PUSH_TX_SIZE_WEIGHT__=${MEMPOOL_MAX_PUSH_TX_SIZE_WEIGHT:=4000000} +__MEMPOOL_ALLOW_UNREACHABLE__=${MEMPOOL_ALLOW_UNREACHABLE:=true} + # CORE_RPC __CORE_RPC_HOST__=${CORE_RPC_HOST:=127.0.0.1} @@ -161,6 +164,8 @@ sed -i "s!__MEMPOOL_ADVANCED_GBT_AUDIT__!${__MEMPOOL_ADVANCED_GBT_AUDIT__}!g" me sed -i "s!__MEMPOOL_CPFP_INDEXING__!${__MEMPOOL_CPFP_INDEXING__}!g" mempool-config.json sed -i "s!__MEMPOOL_MAX_BLOCKS_BULK_QUERY__!${__MEMPOOL_MAX_BLOCKS_BULK_QUERY__}!g" mempool-config.json sed -i "s!__MEMPOOL_DISK_CACHE_BLOCK_INTERVAL__!${__MEMPOOL_DISK_CACHE_BLOCK_INTERVAL__}!g" mempool-config.json +sed -i "s!__MEMPOOL_MAX_PUSH_TX_SIZE_WEIGHT__!${__MEMPOOL_MAX_PUSH_TX_SIZE_WEIGHT__}!g" mempool-config.json +sed -i "s!__MEMPOOL_ALLOW_UNREACHABLE__!${__MEMPOOL_ALLOW_UNREACHABLE__}!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 diff --git a/production/mempool-config.mainnet.json b/production/mempool-config.mainnet.json index 8630f1fcd..a76053913 100644 --- a/production/mempool-config.mainnet.json +++ b/production/mempool-config.mainnet.json @@ -16,7 +16,9 @@ "ADVANCED_GBT_MEMPOOL": true, "RUST_GBT": true, "USE_SECOND_NODE_FOR_MINFEE": true, - "DISK_CACHE_BLOCK_INTERVAL": 1 + "DISK_CACHE_BLOCK_INTERVAL": 1, + "MAX_PUSH_TX_SIZE_WEIGHT": 4000000, + "ALLOW_UNREACHABLE": true }, "SYSLOG" : { "MIN_PRIORITY": "debug" diff --git a/production/mempool-config.signet.json b/production/mempool-config.signet.json index e216ed216..957b36101 100644 --- a/production/mempool-config.signet.json +++ b/production/mempool-config.signet.json @@ -12,7 +12,9 @@ "ADVANCED_GBT_MEMPOOL": true, "RUST_GBT": true, "POLL_RATE_MS": 1000, - "DISK_CACHE_BLOCK_INTERVAL": 1 + "DISK_CACHE_BLOCK_INTERVAL": 1, + "MAX_PUSH_TX_SIZE_WEIGHT": 4000000, + "ALLOW_UNREACHABLE": true }, "SYSLOG" : { "MIN_PRIORITY": "debug" diff --git a/production/mempool-config.testnet.json b/production/mempool-config.testnet.json index 02bf892c1..8943e987f 100644 --- a/production/mempool-config.testnet.json +++ b/production/mempool-config.testnet.json @@ -12,7 +12,9 @@ "ADVANCED_GBT_MEMPOOL": true, "RUST_GBT": true, "POLL_RATE_MS": 1000, - "DISK_CACHE_BLOCK_INTERVAL": 1 + "DISK_CACHE_BLOCK_INTERVAL": 1, + "MAX_PUSH_TX_SIZE_WEIGHT": 4000000, + "ALLOW_UNREACHABLE": true }, "SYSLOG" : { "MIN_PRIORITY": "debug" From 15e58035e544a62a3f002ae49baddf35c099d112 Mon Sep 17 00:00:00 2001 From: wiz Date: Thu, 13 Jul 2023 15:06:00 +0900 Subject: [PATCH 28/28] ops: Remove 2 electrs patches from prod installer --- production/install | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/production/install b/production/install index 0297d4b4f..1121f5b4f 100755 --- a/production/install +++ b/production/install @@ -1240,8 +1240,8 @@ if [ "${BITCOIN_ELECTRS_INSTALL}" = ON ];then FreeBSD) echo "[*] Patching Bitcoin Electrs code for FreeBSD" osSudo "${BITCOIN_USER}" sh -c "cd \"${BITCOIN_HOME}/.cargo/registry/src/index.crates.io-6f17d22bba15001f/sysconf-0.3.4\" && patch -p1 < \"${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/freebsd/sysconf.patch\"" - osSudo "${BITCOIN_USER}" sh -c "cd \"${BITCOIN_ELECTRS_HOME}/src/new_index/\" && sed -i.bak -e s/Snappy/None/ db.rs && rm db.rs.bak" - osSudo "${BITCOIN_USER}" sh -c "cd \"${BITCOIN_ELECTRS_HOME}/src/bin/\" && sed -i.bak -e 's/from_secs(5)/from_secs(1)/' electrs.rs && rm electrs.rs.bak" + #osSudo "${BITCOIN_USER}" sh -c "cd \"${BITCOIN_ELECTRS_HOME}/src/new_index/\" && sed -i.bak -e s/Snappy/None/ db.rs && rm db.rs.bak" + #osSudo "${BITCOIN_USER}" sh -c "cd \"${BITCOIN_ELECTRS_HOME}/src/bin/\" && sed -i.bak -e 's/from_secs(5)/from_secs(1)/' electrs.rs && rm electrs.rs.bak" ;; Debian) ;;