From a671bfc226e32bdd296f7706aa95f30d48404f19 Mon Sep 17 00:00:00 2001 From: nymkappa Date: Thu, 1 Dec 2022 12:05:23 +0100 Subject: [PATCH 01/67] Make sure exchange API response format is valid before using it --- backend/src/tasks/price-feeds/bitfinex-api.ts | 6 +++++- backend/src/tasks/price-feeds/bitflyer-api.ts | 6 +++++- backend/src/tasks/price-feeds/coinbase-api.ts | 6 +++++- backend/src/tasks/price-feeds/gemini-api.ts | 6 +++++- backend/src/tasks/price-feeds/kraken-api.ts | 9 ++++++++- backend/src/tasks/price-updater.ts | 6 +++++- 6 files changed, 33 insertions(+), 6 deletions(-) diff --git a/backend/src/tasks/price-feeds/bitfinex-api.ts b/backend/src/tasks/price-feeds/bitfinex-api.ts index 04bd47732..0e06c3af7 100644 --- a/backend/src/tasks/price-feeds/bitfinex-api.ts +++ b/backend/src/tasks/price-feeds/bitfinex-api.ts @@ -13,7 +13,11 @@ class BitfinexApi implements PriceFeed { public async $fetchPrice(currency): Promise { const response = await query(this.url + currency); - return response ? parseInt(response['last_price'], 10) : -1; + if (response && response['last_price']) { + return parseInt(response['last_price'], 10); + } else { + return -1; + } } public async $fetchRecentPrice(currencies: string[], type: 'hour' | 'day'): Promise { diff --git a/backend/src/tasks/price-feeds/bitflyer-api.ts b/backend/src/tasks/price-feeds/bitflyer-api.ts index 143fbe8d9..72b2e6adf 100644 --- a/backend/src/tasks/price-feeds/bitflyer-api.ts +++ b/backend/src/tasks/price-feeds/bitflyer-api.ts @@ -13,7 +13,11 @@ class BitflyerApi implements PriceFeed { public async $fetchPrice(currency): Promise { const response = await query(this.url + currency); - return response ? parseInt(response['ltp'], 10) : -1; + if (response && response['ltp']) { + return parseInt(response['ltp'], 10); + } else { + return -1; + } } public async $fetchRecentPrice(currencies: string[], type: 'hour' | 'day'): Promise { diff --git a/backend/src/tasks/price-feeds/coinbase-api.ts b/backend/src/tasks/price-feeds/coinbase-api.ts index ef28b0d80..424ac8867 100644 --- a/backend/src/tasks/price-feeds/coinbase-api.ts +++ b/backend/src/tasks/price-feeds/coinbase-api.ts @@ -13,7 +13,11 @@ class CoinbaseApi implements PriceFeed { public async $fetchPrice(currency): Promise { const response = await query(this.url + currency); - return response ? parseInt(response['data']['amount'], 10) : -1; + if (response && response['data'] && response['data']['amount']) { + return parseInt(response['data']['amount'], 10); + } else { + return -1; + } } public async $fetchRecentPrice(currencies: string[], type: 'hour' | 'day'): Promise { diff --git a/backend/src/tasks/price-feeds/gemini-api.ts b/backend/src/tasks/price-feeds/gemini-api.ts index abd8e0939..fc86dc0a3 100644 --- a/backend/src/tasks/price-feeds/gemini-api.ts +++ b/backend/src/tasks/price-feeds/gemini-api.ts @@ -13,7 +13,11 @@ class GeminiApi implements PriceFeed { public async $fetchPrice(currency): Promise { const response = await query(this.url + currency); - return response ? parseInt(response['last'], 10) : -1; + if (response && response['last']) { + return parseInt(response['last'], 10); + } else { + return -1; + } } public async $fetchRecentPrice(currencies: string[], type: 'hour' | 'day'): Promise { diff --git a/backend/src/tasks/price-feeds/kraken-api.ts b/backend/src/tasks/price-feeds/kraken-api.ts index ddb3c4f65..cc8fa132c 100644 --- a/backend/src/tasks/price-feeds/kraken-api.ts +++ b/backend/src/tasks/price-feeds/kraken-api.ts @@ -23,7 +23,14 @@ class KrakenApi implements PriceFeed { public async $fetchPrice(currency): Promise { const response = await query(this.url + currency); - return response ? parseInt(response['result'][this.getTicker(currency)]['c'][0], 10) : -1; + const ticker = this.getTicker(currency); + if (response && response['result'] && response['result'][ticker] && + response['result'][ticker]['c'] && response['result'][ticker]['c'].length > 0 + ) { + return parseInt(response['result'][ticker]['c'][0], 10); + } else { + return -1; + } } public async $fetchRecentPrice(currencies: string[], type: 'hour' | 'day'): Promise { diff --git a/backend/src/tasks/price-updater.ts b/backend/src/tasks/price-updater.ts index e069e4db4..e2d00ec7b 100644 --- a/backend/src/tasks/price-updater.ts +++ b/backend/src/tasks/price-updater.ts @@ -127,7 +127,11 @@ class PriceUpdater { // Compute average price, non weighted prices = prices.filter(price => price > 0); - this.latestPrices[currency] = Math.round((prices.reduce((partialSum, a) => partialSum + a, 0)) / prices.length); + if (prices.length === 0) { + this.latestPrices[currency] = -1; + } else { + this.latestPrices[currency] = Math.round((prices.reduce((partialSum, a) => partialSum + a, 0)) / prices.length); + } } logger.info(`Latest BTC fiat averaged price: ${JSON.stringify(this.latestPrices)}`); From f9026b8f3536c48ca4253146e4a3cbde0c6fba42 Mon Sep 17 00:00:00 2001 From: softsimon Date: Sun, 29 Jan 2023 16:23:35 +0400 Subject: [PATCH 02/67] Credit nepalese translator --- frontend/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/README.md b/frontend/README.md index 4bfca4fe8..68fa12e48 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -132,3 +132,4 @@ https://www.transifex.com/mempool/mempool/dashboard/ * Russian @TonyCrusoe @Bitconan * Romanian @mirceavesa * Macedonian @SkechBoy +* Nepalese @kebinm From e8c32735413d616dd39ef832cbe9ef0a756d2c80 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 30 Jan 2023 16:26:37 -0600 Subject: [PATCH 03/67] fix drift in next block viz with mixed template algos --- backend/src/api/mempool-blocks.ts | 39 +++++++++++++++++----------- backend/src/api/websocket-handler.ts | 20 +++++++------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/backend/src/api/mempool-blocks.ts b/backend/src/api/mempool-blocks.ts index d94ed77bd..0df125d55 100644 --- a/backend/src/api/mempool-blocks.ts +++ b/backend/src/api/mempool-blocks.ts @@ -33,7 +33,7 @@ class MempoolBlocks { return this.mempoolBlockDeltas; } - public updateMempoolBlocks(memPool: { [txid: string]: TransactionExtended }): void { + public updateMempoolBlocks(memPool: { [txid: string]: TransactionExtended }, saveResults: boolean = false): MempoolBlockWithTransactions[] { const latestMempool = memPool; const memPoolArray: TransactionExtended[] = []; for (const i in latestMempool) { @@ -75,10 +75,14 @@ class MempoolBlocks { logger.debug('Mempool blocks calculated in ' + time / 1000 + ' seconds'); const blocks = this.calculateMempoolBlocks(memPoolArray, this.mempoolBlocks); - const deltas = this.calculateMempoolDeltas(this.mempoolBlocks, blocks); - this.mempoolBlocks = blocks; - this.mempoolBlockDeltas = deltas; + if (saveResults) { + const deltas = this.calculateMempoolDeltas(this.mempoolBlocks, blocks); + this.mempoolBlocks = blocks; + this.mempoolBlockDeltas = deltas; + } + + return blocks; } private calculateMempoolBlocks(transactionsSorted: TransactionExtended[], prevBlocks: MempoolBlockWithTransactions[]): MempoolBlockWithTransactions[] { @@ -143,7 +147,7 @@ class MempoolBlocks { return mempoolBlockDeltas; } - public async makeBlockTemplates(newMempool: { [txid: string]: TransactionExtended }): Promise { + public async makeBlockTemplates(newMempool: { [txid: string]: TransactionExtended }, saveResults: boolean = false): Promise { // prepare a stripped down version of the mempool with only the minimum necessary data // to reduce the overhead of passing this data to the worker thread const strippedMempool: { [txid: string]: ThreadTransaction } = {}; @@ -184,19 +188,21 @@ class MempoolBlocks { this.txSelectionWorker.postMessage({ type: 'set', mempool: strippedMempool }); const { blocks, clusters } = await workerResultPromise; - this.processBlockTemplates(newMempool, blocks, clusters); - // clean up thread error listener this.txSelectionWorker?.removeListener('error', threadErrorListener); + + return this.processBlockTemplates(newMempool, blocks, clusters, saveResults); } catch (e) { logger.err('makeBlockTemplates failed. ' + (e instanceof Error ? e.message : e)); } + return this.mempoolBlocks; } - public async updateBlockTemplates(newMempool: { [txid: string]: TransactionExtended }, added: TransactionExtended[], removed: string[]): Promise { + public async updateBlockTemplates(newMempool: { [txid: string]: TransactionExtended }, added: TransactionExtended[], removed: string[], saveResults: boolean = false): Promise { if (!this.txSelectionWorker) { // need to reset the worker - return this.makeBlockTemplates(newMempool); + this.makeBlockTemplates(newMempool, saveResults); + return; } // prepare a stripped down version of the mempool with only the minimum necessary data // to reduce the overhead of passing this data to the worker thread @@ -224,16 +230,16 @@ class MempoolBlocks { this.txSelectionWorker.postMessage({ type: 'update', added: addedStripped, removed }); const { blocks, clusters } = await workerResultPromise; - this.processBlockTemplates(newMempool, blocks, clusters); - // clean up thread error listener this.txSelectionWorker?.removeListener('error', threadErrorListener); + + this.processBlockTemplates(newMempool, blocks, clusters, saveResults); } catch (e) { logger.err('updateBlockTemplates failed. ' + (e instanceof Error ? e.message : e)); } } - private processBlockTemplates(mempool, blocks, clusters): void { + private processBlockTemplates(mempool, blocks, clusters, saveResults): MempoolBlockWithTransactions[] { // update this thread's mempool with the results blocks.forEach(block => { block.forEach(tx => { @@ -278,10 +284,13 @@ class MempoolBlocks { }).filter(tx => !!tx), undefined, undefined, blockIndex); }); - const deltas = this.calculateMempoolDeltas(this.mempoolBlocks, mempoolBlocks); + if (saveResults) { + const deltas = this.calculateMempoolDeltas(this.mempoolBlocks, mempoolBlocks); + this.mempoolBlocks = mempoolBlocks; + this.mempoolBlockDeltas = deltas; + } - this.mempoolBlocks = mempoolBlocks; - this.mempoolBlockDeltas = deltas; + return mempoolBlocks; } private dataToMempoolBlocks(transactions: TransactionExtended[], diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index 3ca49293d..cffbea346 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -251,9 +251,9 @@ class WebsocketHandler { } if (config.MEMPOOL.ADVANCED_GBT_MEMPOOL) { - await mempoolBlocks.updateBlockTemplates(newMempool, newTransactions, deletedTransactions.map(tx => tx.txid)); + await mempoolBlocks.updateBlockTemplates(newMempool, newTransactions, deletedTransactions.map(tx => tx.txid), true); } else { - mempoolBlocks.updateMempoolBlocks(newMempool); + mempoolBlocks.updateMempoolBlocks(newMempool, true); } const mBlocks = mempoolBlocks.getMempoolBlocks(); @@ -418,16 +418,18 @@ class WebsocketHandler { const _memPool = memPool.getMempool(); + let projectedBlocks; + // template calculation functions have mempool side effects, so calculate audits using + // a cloned copy of the mempool if we're running a different algorithm for mempool updates + const auditMempool = (config.MEMPOOL.ADVANCED_GBT_AUDIT === config.MEMPOOL.ADVANCED_GBT_MEMPOOL) ? _memPool : JSON.parse(JSON.stringify(_memPool)); if (config.MEMPOOL.ADVANCED_GBT_AUDIT) { - await mempoolBlocks.makeBlockTemplates(_memPool); + projectedBlocks = await mempoolBlocks.makeBlockTemplates(auditMempool, false); } else { - mempoolBlocks.updateMempoolBlocks(_memPool); + projectedBlocks = mempoolBlocks.updateMempoolBlocks(auditMempool, false); } if (Common.indexingEnabled() && memPool.isInSync()) { - const projectedBlocks = mempoolBlocks.getMempoolBlocksWithTransactions(); - - const { censored, added, fresh, score } = Audit.auditBlock(transactions, projectedBlocks, _memPool); + const { censored, added, fresh, score } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); const matchRate = Math.round(score * 100 * 100) / 100; const stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions.map((tx) => { @@ -471,9 +473,9 @@ class WebsocketHandler { } if (config.MEMPOOL.ADVANCED_GBT_MEMPOOL) { - await mempoolBlocks.updateBlockTemplates(_memPool, [], removed); + await mempoolBlocks.updateBlockTemplates(_memPool, [], removed, true); } else { - mempoolBlocks.updateMempoolBlocks(_memPool); + mempoolBlocks.updateMempoolBlocks(_memPool, true); } const mBlocks = mempoolBlocks.getMempoolBlocks(); const mBlockDeltas = mempoolBlocks.getMempoolBlockDeltas(); From 573fe3515a14e35c319c01cc276b0e9f723defdb Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Tue, 31 Jan 2023 00:27:06 -0500 Subject: [PATCH 04/67] Add block health faq --- frontend/src/app/docs/api-docs/api-docs-data.ts | 7 +++++++ .../src/app/docs/api-docs/api-docs.component.html | 13 +++++++++++++ .../src/app/docs/api-docs/api-docs.component.scss | 6 ++++++ 3 files changed, 26 insertions(+) diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts index 8cbf03dfb..f4b4602ad 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -8667,6 +8667,13 @@ export const faqData = [ fragment: "why-dont-fee-ranges-match", title: "Why doesn't the fee range shown for a block match the feerates of transactions within the block?", }, + { + type: "endpoint", + category: "advanced", + showConditions: bitcoinNetworks, + fragment: "what-is-block-health", + title: "What is block health?", + }, { type: "category", category: "self-hosting", diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html index c343d24c8..9f0b9fc2d 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -218,6 +218,19 @@

For unconfirmed CPFP transactions, Mempool will show the effective feerate (along with descendent & ancestor transaction information) on the transaction page. For confirmed transactions, CPFP relationships are not stored, so this additional information is not shown.

+ +

Block health indicates the extent of potential censorship in a block. This is determined by counting how many expected transactions a block is missing—a block that is not missing any expected transactions will have 100% health, while a block missing 1 or more expected transactions will have sub-100% health.

+

How does this work? Let sexpected be the set of all transactions Mempool expected to be in a block and let sactual be the set of all transactions actually in a block. Let n be the number of all transactions in both sexpected and sactual.

+

Then let r be the number of all transactions expected to be in sactual but not actually in it (excluding those that have been recently broadcast; see below).

+

Block health is calculated as n / ( n + r ).

+

Transactions appearing in both sexpected and sactual are used (instead of a block's full transaction count) in order to minimize chances that block health is impacted by missing transactions that don't imply censorship:

+
    +
  • recently-broadcast transactions, since the miner may simply not have received them
  • +
  • certain low-feerate transactions, since the miner may have opted to replace them with more profitable out-of-band transactions
  • +
+

Mempool uses a re-implementation of Bitcoin Core's transaction selection algorithm to determine the transactions it expects to see in the next block.

+
+ The official mempool.space website is operated by The Mempool Open Source Project. See more information on our About page. There are also many unofficial instances of this website operated by individual members of the Bitcoin community. diff --git a/frontend/src/app/docs/api-docs/api-docs.component.scss b/frontend/src/app/docs/api-docs/api-docs.component.scss index 7392d1f55..db6d51cf2 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.scss +++ b/frontend/src/app/docs/api-docs/api-docs.component.scss @@ -21,6 +21,12 @@ code { font-family: Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New; } +.math { + font-family: monospace; + margin-left: 4px; + margin-right: 4px; +} + tr { white-space: inherit; } From ff3af3a159d60aea661f6948912bd7eb76ce0c16 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Tue, 31 Jan 2023 01:06:33 -0500 Subject: [PATCH 05/67] Add link to faq next to block health metric --- frontend/src/app/components/block/block.component.html | 4 ++-- frontend/src/app/components/block/block.component.scss | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 828493736..c3b58c4ee 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -53,7 +53,7 @@ - Health + Health - +

diff --git a/frontend/src/app/components/block/block.component.scss b/frontend/src/app/components/block/block.component.scss index 97b5a24f3..e5ac03727 100644 --- a/frontend/src/app/components/block/block.component.scss +++ b/frontend/src/app/components/block/block.component.scss @@ -34,6 +34,10 @@ text-align: left; } } + .info-link { + color: rgba(255, 255, 255, 0.4); + margin-left: 5px; + } } } @@ -227,4 +231,4 @@ h1 { .ng-fa-icon { margin-right: 1em; } -} \ No newline at end of file +} From e3c1343a24e5c014c71953df2d63773f5bbf908a Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Tue, 31 Jan 2023 10:30:16 -0500 Subject: [PATCH 06/67] Remove merge conflict cruft --- frontend/src/app/components/block/block.component.scss | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/src/app/components/block/block.component.scss b/frontend/src/app/components/block/block.component.scss index 3d96495e6..71d5fa6fb 100644 --- a/frontend/src/app/components/block/block.component.scss +++ b/frontend/src/app/components/block/block.component.scss @@ -232,8 +232,6 @@ h1 { margin-right: 1em; } } -<<<<<<< hunicus/block-health-faq -======= .beta { font-size: 10px; @@ -243,4 +241,3 @@ h1 { top: 11px; margin-left: 10px; } ->>>>>>> master From 3f0cbcab636bdeed98538b15d07b65851eeb8f65 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Tue, 31 Jan 2023 10:50:16 -0500 Subject: [PATCH 07/67] Add 'removed' so meaning of r is more clear --- frontend/src/app/docs/api-docs/api-docs.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html index 9f0b9fc2d..48e9b48eb 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -221,7 +221,7 @@

Block health indicates the extent of potential censorship in a block. This is determined by counting how many expected transactions a block is missing—a block that is not missing any expected transactions will have 100% health, while a block missing 1 or more expected transactions will have sub-100% health.

How does this work? Let sexpected be the set of all transactions Mempool expected to be in a block and let sactual be the set of all transactions actually in a block. Let n be the number of all transactions in both sexpected and sactual.

-

Then let r be the number of all transactions expected to be in sactual but not actually in it (excluding those that have been recently broadcast; see below).

+

Then let r be the number of removed transactions—all transactions expected to be in sactual but not actually in it (excluding those that have been recently broadcast; see below).

Block health is calculated as n / ( n + r ).

Transactions appearing in both sexpected and sactual are used (instead of a block's full transaction count) in order to minimize chances that block health is impacted by missing transactions that don't imply censorship:

    From aa8a3e60c2e8a8b5ee4e4763eff1177738e2ffdd Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 3 Jan 2023 11:54:42 -0600 Subject: [PATCH 08/67] expose other currencies in charts APIs --- backend/src/repositories/BlocksRepository.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/repositories/BlocksRepository.ts b/backend/src/repositories/BlocksRepository.ts index df98719b9..355187e21 100644 --- a/backend/src/repositories/BlocksRepository.ts +++ b/backend/src/repositories/BlocksRepository.ts @@ -521,7 +521,7 @@ class BlocksRepository { CAST(AVG(blocks.height) as INT) as avgHeight, CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, CAST(AVG(fees) as INT) as avgFees, - prices.USD + prices.* FROM blocks JOIN blocks_prices on blocks_prices.height = blocks.height JOIN prices on prices.id = blocks_prices.price_id @@ -550,7 +550,7 @@ class BlocksRepository { CAST(AVG(blocks.height) as INT) as avgHeight, CAST(AVG(UNIX_TIMESTAMP(blockTimestamp)) as INT) as timestamp, CAST(AVG(reward) as INT) as avgRewards, - prices.USD + prices.* FROM blocks JOIN blocks_prices on blocks_prices.height = blocks.height JOIN prices on prices.id = blocks_prices.price_id From 02655d757e014aa77e2bc2d9d302ac5f8dc77582 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 3 Jan 2023 11:56:54 -0600 Subject: [PATCH 09/67] Add currency preference to dashboard --- frontend/src/app/app.constants.ts | 94 +++++++++++++++++++ .../fiat-selector.component.html | 5 + .../fiat-selector.component.scss | 0 .../fiat-selector/fiat-selector.component.ts | 38 ++++++++ .../language-selector.component.html | 2 +- .../app/dashboard/dashboard.component.html | 9 +- .../app/dashboard/dashboard.component.scss | 15 +++ frontend/src/app/services/state.service.ts | 4 + frontend/src/app/shared/shared.module.ts | 3 + 9 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 frontend/src/app/components/fiat-selector/fiat-selector.component.html create mode 100644 frontend/src/app/components/fiat-selector/fiat-selector.component.scss create mode 100644 frontend/src/app/components/fiat-selector/fiat-selector.component.ts diff --git a/frontend/src/app/app.constants.ts b/frontend/src/app/app.constants.ts index 47e12cfcd..0456f2647 100644 --- a/frontend/src/app/app.constants.ts +++ b/frontend/src/app/app.constants.ts @@ -157,3 +157,97 @@ export const specialBlocks = { labelEventCompleted: 'Block Subsidy has halved to 3.125 BTC per block', } }; + +export const fiatCurrencies = { + AED: { + name: 'UAE Dirham', + code: 'AED' + }, + AUD: { + name: 'Australian Dollar', + code: 'AUD', + indexed: true, + }, + BRL: { + name: 'Brazilian Real', + code: 'BRL' + }, + CAD: { + name: 'Canadian Dollar', + code: 'CAD', + indexed: true, + }, + CHF: { + name: 'Swiss Franc', + code: 'CHF', + indexed: true, + }, + EUR: { + name: 'Euro', + code: 'EUR', + indexed: true, + }, + GBP: { + name: 'Pound Sterling', + code: 'GBP', + indexed: true, + }, + HKD: { + name: 'Hong Kong Dollar', + code: 'HKD' + }, + IDR: { + name: 'Indonesian Rupiah', + code: 'IDR' + }, + JPY: { + name: 'Japanese Yen', + code: 'JPY', + indexed: true, + }, + KRW: { + name: 'Korean Won', + code: 'KRW' + }, + MYR: { + name: 'Malaysian Ringgit', + code: 'MYR' + }, + NGN: { + name: 'Nigerian Naira', + code: 'NGN' + }, + NZD: { + name: 'New Zealand Dollar', + code: 'NZD' + }, + PLN: { + name: 'Polish Złoty', + code: 'PLN' + }, + RUB: { + name: 'Russian Ruble', + code: 'RUB' + }, + SGD: { + name: 'Singapore Dollar', + code: 'SGD' + }, + TRY: { + name: 'Turkish Lira', + code: 'TRY' + }, + UAH: { + name: 'Ukrainian Hryvnia', + code: 'UAH' + }, + USD: { + name: 'US Dollar', + code: 'USD', + indexed: true, + }, + ZAR: { + name: 'South African Rand', + code: 'ZAR' + }, +}; \ No newline at end of file diff --git a/frontend/src/app/components/fiat-selector/fiat-selector.component.html b/frontend/src/app/components/fiat-selector/fiat-selector.component.html new file mode 100644 index 000000000..7fa8c0d00 --- /dev/null +++ b/frontend/src/app/components/fiat-selector/fiat-selector.component.html @@ -0,0 +1,5 @@ +
    + +
    diff --git a/frontend/src/app/components/fiat-selector/fiat-selector.component.scss b/frontend/src/app/components/fiat-selector/fiat-selector.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/components/fiat-selector/fiat-selector.component.ts b/frontend/src/app/components/fiat-selector/fiat-selector.component.ts new file mode 100644 index 000000000..f967b7d77 --- /dev/null +++ b/frontend/src/app/components/fiat-selector/fiat-selector.component.ts @@ -0,0 +1,38 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { StorageService } from '../../services/storage.service'; +import { fiatCurrencies } from '../../app.constants'; +import { StateService } from '../../services/state.service'; + +@Component({ + selector: 'app-fiat-selector', + templateUrl: './fiat-selector.component.html', + styleUrls: ['./fiat-selector.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class FiatSelectorComponent implements OnInit { + fiatForm: UntypedFormGroup; + currencies = fiatCurrencies; + currencyList = Object.keys(fiatCurrencies).sort(); + + constructor( + private formBuilder: UntypedFormBuilder, + private stateService: StateService, + private storageService: StorageService, + ) { } + + ngOnInit() { + this.fiatForm = this.formBuilder.group({ + fiat: ['USD'] + }); + this.stateService.fiatCurrency$.subscribe((fiat) => { + this.fiatForm.get('fiat')?.setValue(fiat); + }); + } + + changeFiat() { + const newFiat = this.fiatForm.get('fiat')?.value; + this.storageService.setValue('fiat-preference', newFiat); + this.stateService.fiatCurrency$.next(newFiat); + } +} diff --git a/frontend/src/app/components/language-selector/language-selector.component.html b/frontend/src/app/components/language-selector/language-selector.component.html index dee1e3acb..b23b2b7b7 100644 --- a/frontend/src/app/components/language-selector/language-selector.component.html +++ b/frontend/src/app/components/language-selector/language-selector.component.html @@ -1,5 +1,5 @@
    -
    diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 397021048..1df7baabc 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -144,7 +144,14 @@
- +
+
+ +
+
+ +
+
Terms of Service diff --git a/frontend/src/app/dashboard/dashboard.component.scss b/frontend/src/app/dashboard/dashboard.component.scss index a7e21ba94..4a844eb52 100644 --- a/frontend/src/app/dashboard/dashboard.component.scss +++ b/frontend/src/app/dashboard/dashboard.component.scss @@ -323,4 +323,19 @@ margin-bottom: 10px; text-decoration: none; color: inherit; +} + +.pref-selectors { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + + .selector { + margin-left: .5em; + margin-bottom: .5em; + &:first { + margin-left: 0; + } + } } \ No newline at end of file diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 091490715..02b160fe9 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -119,6 +119,7 @@ export class StateService { timeLtr: BehaviorSubject; hideFlow: BehaviorSubject; hideAudit: BehaviorSubject; + fiatCurrency$: BehaviorSubject; constructor( @Inject(PLATFORM_ID) private platformId: any, @@ -184,6 +185,9 @@ export class StateService { this.hideAudit.subscribe((hide) => { this.storageService.setValue('audit-preference', hide ? 'hide' : 'show'); }); + + const fiatPreference = this.storageService.getValue('fiat-preference'); + this.fiatCurrency$ = new BehaviorSubject(fiatPreference || 'USD'); } setNetworkBasedonUrl(url: string) { diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index d82f03493..e87107fe5 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -34,6 +34,7 @@ import { TxFeaturesComponent } from '../components/tx-features/tx-features.compo import { TxFeeRatingComponent } from '../components/tx-fee-rating/tx-fee-rating.component'; import { ReactiveFormsModule } from '@angular/forms'; import { LanguageSelectorComponent } from '../components/language-selector/language-selector.component'; +import { FiatSelectorComponent } from '../components/fiat-selector/fiat-selector.component'; import { ColoredPriceDirective } from './directives/colored-price.directive'; import { NoSanitizePipe } from './pipes/no-sanitize.pipe'; import { MempoolBlocksComponent } from '../components/mempool-blocks/mempool-blocks.component'; @@ -93,6 +94,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati TxFeaturesComponent, TxFeeRatingComponent, LanguageSelectorComponent, + FiatSelectorComponent, ScriptpubkeyTypePipe, RelativeUrlPipe, NoSanitizePipe, @@ -199,6 +201,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati TxFeaturesComponent, TxFeeRatingComponent, LanguageSelectorComponent, + FiatSelectorComponent, ScriptpubkeyTypePipe, RelativeUrlPipe, Hex2asciiPipe, From c2ff6a996ab447dae6a971205ecaa4e091724964 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 3 Jan 2023 11:58:09 -0600 Subject: [PATCH 10/67] Multi-currency fiat formatting pipes & components --- frontend/src/app/app.module.ts | 2 ++ frontend/src/app/fiat/fiat.component.html | 2 +- frontend/src/app/fiat/fiat.component.ts | 20 ++++++++++--- .../app/shared/pipes/fiat-currency.pipe.ts | 28 +++++++++++++++++++ .../app/shared/pipes/fiat-shortener.pipe.ts | 22 +++++++++++---- frontend/src/app/shared/shared.module.ts | 3 ++ 6 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 frontend/src/app/shared/pipes/fiat-currency.pipe.ts diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index b7bd1526f..f26b4a924 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -17,6 +17,7 @@ import { StorageService } from './services/storage.service'; import { HttpCacheInterceptor } from './services/http-cache.interceptor'; import { LanguageService } from './services/language.service'; import { FiatShortenerPipe } from './shared/pipes/fiat-shortener.pipe'; +import { FiatCurrencyPipe } from './shared/pipes/fiat-currency.pipe'; import { ShortenStringPipe } from './shared/pipes/shorten-string-pipe/shorten-string.pipe'; import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe'; import { AppPreloadingStrategy } from './app.preloading-strategy'; @@ -34,6 +35,7 @@ const providers = [ LanguageService, ShortenStringPipe, FiatShortenerPipe, + FiatCurrencyPipe, CapAddressPipe, AppPreloadingStrategy, { provide: HTTP_INTERCEPTORS, useClass: HttpCacheInterceptor, multi: true } diff --git a/frontend/src/app/fiat/fiat.component.html b/frontend/src/app/fiat/fiat.component.html index 19a5c8aa0..1adde8120 100644 --- a/frontend/src/app/fiat/fiat.component.html +++ b/frontend/src/app/fiat/fiat.component.html @@ -1 +1 @@ -{{ (conversions$ | async)?.USD * value / 100000000 | currency:'USD':'symbol':digitsInfo }} \ No newline at end of file +{{ (conversions$ | async)[currency] * value / 100000000 | fiatCurrency : digitsInfo : currency }} \ No newline at end of file diff --git a/frontend/src/app/fiat/fiat.component.ts b/frontend/src/app/fiat/fiat.component.ts index 9d22355d8..bc0f6a0de 100644 --- a/frontend/src/app/fiat/fiat.component.ts +++ b/frontend/src/app/fiat/fiat.component.ts @@ -1,5 +1,5 @@ -import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef, OnDestroy } from '@angular/core'; +import { Observable, Subscription } from 'rxjs'; import { StateService } from '../services/state.service'; @Component({ @@ -8,18 +8,30 @@ import { StateService } from '../services/state.service'; styleUrls: ['./fiat.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class FiatComponent implements OnInit { +export class FiatComponent implements OnInit, OnDestroy { conversions$: Observable; + currencySubscription: Subscription; + currency: string; @Input() value: number; @Input() digitsInfo = '1.2-2'; constructor( private stateService: StateService, - ) { } + private cd: ChangeDetectorRef, + ) { + this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => { + this.currency = fiat; + this.cd.markForCheck(); + }); + } ngOnInit(): void { this.conversions$ = this.stateService.conversions$.asObservable(); } + ngOnDestroy(): void { + this.currencySubscription.unsubscribe(); + } + } diff --git a/frontend/src/app/shared/pipes/fiat-currency.pipe.ts b/frontend/src/app/shared/pipes/fiat-currency.pipe.ts new file mode 100644 index 000000000..3cd825291 --- /dev/null +++ b/frontend/src/app/shared/pipes/fiat-currency.pipe.ts @@ -0,0 +1,28 @@ +import { formatCurrency, getCurrencySymbol } from '@angular/common'; +import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { StateService } from '../../services/state.service'; + +@Pipe({ + name: 'fiatCurrency' +}) +export class FiatCurrencyPipe implements PipeTransform { + fiatSubscription: Subscription; + currency: string; + + constructor( + @Inject(LOCALE_ID) public locale: string, + private stateService: StateService, + ) { + this.fiatSubscription = this.stateService.fiatCurrency$.subscribe((fiat) => { + this.currency = fiat; + }); + } + + transform(num: number, ...args: any[]): unknown { + const digits = args[0] || 1; + const currency = args[1] || this.currency || 'USD'; + + return new Intl.NumberFormat(this.locale, { style: 'currency', currency }).format(num); + } +} \ No newline at end of file diff --git a/frontend/src/app/shared/pipes/fiat-shortener.pipe.ts b/frontend/src/app/shared/pipes/fiat-shortener.pipe.ts index 8c534f93f..93ab5cf8f 100644 --- a/frontend/src/app/shared/pipes/fiat-shortener.pipe.ts +++ b/frontend/src/app/shared/pipes/fiat-shortener.pipe.ts @@ -1,20 +1,30 @@ import { formatCurrency, getCurrencySymbol } from '@angular/common'; import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { StateService } from '../../services/state.service'; @Pipe({ name: 'fiatShortener' }) export class FiatShortenerPipe implements PipeTransform { + fiatSubscription: Subscription; + currency: string; + constructor( - @Inject(LOCALE_ID) public locale: string - ) {} + @Inject(LOCALE_ID) public locale: string, + private stateService: StateService, + ) { + this.fiatSubscription = this.stateService.fiatCurrency$.subscribe((fiat) => { + this.currency = fiat; + }); + } transform(num: number, ...args: any[]): unknown { const digits = args[0] || 1; - const unit = args[1] || undefined; + const currency = args[1] || this.currency || 'USD'; if (num < 1000) { - return num.toFixed(digits); + return new Intl.NumberFormat(this.locale, { style: 'currency', currency, maximumFractionDigits: 1 }).format(num); } const lookup = [ @@ -30,8 +40,8 @@ export class FiatShortenerPipe implements PipeTransform { const item = lookup.slice().reverse().find((item) => num >= item.value); let result = item ? (num / item.value).toFixed(digits).replace(rx, '$1') : '0'; - result = formatCurrency(parseInt(result, 10), this.locale, getCurrencySymbol('USD', 'narrow'), 'USD', '1.0-0'); - + result = new Intl.NumberFormat(this.locale, { style: 'currency', currency, maximumFractionDigits: 0 }).format(item ? num / item.value : 0); + return result + item.symbol; } } \ No newline at end of file diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index e87107fe5..fd257db85 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -23,6 +23,7 @@ import { RelativeUrlPipe } from './pipes/relative-url/relative-url.pipe'; import { ScriptpubkeyTypePipe } from './pipes/scriptpubkey-type-pipe/scriptpubkey-type.pipe'; import { BytesPipe } from './pipes/bytes-pipe/bytes.pipe'; import { WuBytesPipe } from './pipes/bytes-pipe/wubytes.pipe'; +import { FiatCurrencyPipe } from './pipes/fiat-currency.pipe'; import { BlockchainComponent } from '../components/blockchain/blockchain.component'; import { TimeSinceComponent } from '../components/time-since/time-since.component'; import { TimeUntilComponent } from '../components/time-until/time-until.component'; @@ -109,6 +110,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati CapAddressPipe, Decimal2HexPipe, FeeRoundingPipe, + FiatCurrencyPipe, ColoredPriceDirective, BlockchainComponent, MempoolBlocksComponent, @@ -210,6 +212,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati BytesPipe, VbytesPipe, WuBytesPipe, + FiatCurrencyPipe, CeilPipe, ShortenStringPipe, CapAddressPipe, From d06dcdccb496ba12f58792a9adc48492881c9150 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 3 Jan 2023 11:58:51 -0600 Subject: [PATCH 11/67] Display fiat mining graphs in selected currency --- .../block-fees-graph.component.ts | 30 +++++++++++++----- .../block-rewards-graph.component.ts | 31 ++++++++++++++----- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts index 0c2f228b8..42667126f 100644 --- a/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts +++ b/frontend/src/app/components/block-fees-graph/block-fees-graph.component.ts @@ -1,16 +1,19 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; import { EChartsOption, graphic } from 'echarts'; -import { Observable } from 'rxjs'; +import { Observable, Subscription } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; import { SeoService } from '../../services/seo.service'; import { formatCurrency, formatNumber, getCurrencySymbol } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from '../../shared/graphs.utils'; +import { StateService } from '../../services/state.service'; import { StorageService } from '../../services/storage.service'; import { MiningService } from '../../services/mining.service'; import { ActivatedRoute } from '@angular/router'; import { FiatShortenerPipe } from '../../shared/pipes/fiat-shortener.pipe'; +import { FiatCurrencyPipe } from '../../shared/pipes/fiat-currency.pipe'; +import { fiatCurrencies } from '../../app.constants'; @Component({ selector: 'app-block-fees-graph', @@ -44,6 +47,9 @@ export class BlockFeesGraphComponent implements OnInit { timespan = ''; chartInstance: any = undefined; + currencySubscription: Subscription; + currency: string; + constructor( @Inject(LOCALE_ID) public locale: string, private seoService: SeoService, @@ -51,11 +57,21 @@ export class BlockFeesGraphComponent implements OnInit { private formBuilder: UntypedFormBuilder, private storageService: StorageService, private miningService: MiningService, + private stateService: StateService, private route: ActivatedRoute, private fiatShortenerPipe: FiatShortenerPipe, + private fiatCurrencyPipe: FiatCurrencyPipe, ) { this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' }); this.radioGroupForm.controls.dateSpan.setValue('1y'); + + this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => { + if (fiat && fiatCurrencies[fiat]?.indexed) { + this.currency = fiat; + } else { + this.currency = 'USD'; + } + }); } ngOnInit(): void { @@ -84,7 +100,7 @@ export class BlockFeesGraphComponent implements OnInit { tap((response) => { this.prepareChartOptions({ blockFees: response.body.map(val => [val.timestamp * 1000, val.avgFees / 100000000, val.avgHeight]), - blockFeesUSD: response.body.filter(val => val.USD > 0).map(val => [val.timestamp * 1000, val.avgFees / 100000000 * val.USD, val.avgHeight]), + blockFeesFiat: response.body.filter(val => val[this.currency] > 0).map(val => [val.timestamp * 1000, val.avgFees / 100000000 * val[this.currency], val.avgHeight]), }); this.isLoading = false; }), @@ -157,7 +173,7 @@ export class BlockFeesGraphComponent implements OnInit { if (tick.seriesIndex === 0) { tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.3-3')} BTC
`; } else if (tick.seriesIndex === 1) { - tooltip += `${tick.marker} ${tick.seriesName}: ${formatCurrency(tick.data[1], this.locale, getCurrencySymbol('USD', 'narrow'), 'USD', '1.0-0')}
`; + tooltip += `${tick.marker} ${tick.seriesName}: ${this.fiatCurrencyPipe.transform(tick.data[1], null, this.currency) }
`; } } @@ -184,7 +200,7 @@ export class BlockFeesGraphComponent implements OnInit { icon: 'roundRect', }, { - name: 'Fees USD', + name: 'Fees ' + this.currency, inactiveColor: 'rgb(110, 112, 121)', textStyle: { color: 'white', @@ -216,7 +232,7 @@ export class BlockFeesGraphComponent implements OnInit { axisLabel: { color: 'rgb(110, 112, 121)', formatter: function(val) { - return this.fiatShortenerPipe.transform(val); + return this.fiatShortenerPipe.transform(val, null, this.currency); }.bind(this) }, splitLine: { @@ -243,8 +259,8 @@ export class BlockFeesGraphComponent implements OnInit { legendHoverLink: false, zlevel: 1, yAxisIndex: 1, - name: 'Fees USD', - data: data.blockFeesUSD, + name: 'Fees ' + this.currency, + data: data.blockFeesFiat, type: 'line', smooth: 0.25, symbol: 'none', diff --git a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts index fe80b0c97..0e5e339fa 100644 --- a/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts +++ b/frontend/src/app/components/block-rewards-graph/block-rewards-graph.component.ts @@ -1,16 +1,19 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; import { EChartsOption, graphic } from 'echarts'; -import { Observable } from 'rxjs'; +import { Observable, Subscription } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; import { SeoService } from '../../services/seo.service'; -import { formatCurrency, formatNumber, getCurrencySymbol } from '@angular/common'; +import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from '../../shared/graphs.utils'; import { MiningService } from '../../services/mining.service'; +import { StateService } from '../../services/state.service'; import { StorageService } from '../../services/storage.service'; import { ActivatedRoute } from '@angular/router'; import { FiatShortenerPipe } from '../../shared/pipes/fiat-shortener.pipe'; +import { FiatCurrencyPipe } from '../../shared/pipes/fiat-currency.pipe'; +import { fiatCurrencies } from '../../app.constants'; @Component({ selector: 'app-block-rewards-graph', @@ -44,16 +47,28 @@ export class BlockRewardsGraphComponent implements OnInit { timespan = ''; chartInstance: any = undefined; + currencySubscription: Subscription; + currency: string; + constructor( @Inject(LOCALE_ID) public locale: string, private seoService: SeoService, private apiService: ApiService, private formBuilder: UntypedFormBuilder, private miningService: MiningService, + private stateService: StateService, private storageService: StorageService, private route: ActivatedRoute, private fiatShortenerPipe: FiatShortenerPipe, + private fiatCurrencyPipe: FiatCurrencyPipe, ) { + this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => { + if (fiat && fiatCurrencies[fiat]?.indexed) { + this.currency = fiat; + } else { + this.currency = 'USD'; + } + }); } ngOnInit(): void { @@ -82,7 +97,7 @@ export class BlockRewardsGraphComponent implements OnInit { tap((response) => { this.prepareChartOptions({ blockRewards: response.body.map(val => [val.timestamp * 1000, val.avgRewards / 100000000, val.avgHeight]), - blockRewardsUSD: response.body.filter(val => val.USD > 0).map(val => [val.timestamp * 1000, val.avgRewards / 100000000 * val.USD, val.avgHeight]), + blockRewardsFiat: response.body.filter(val => val[this.currency] > 0).map(val => [val.timestamp * 1000, val.avgRewards / 100000000 * val[this.currency], val.avgHeight]), }); this.isLoading = false; }), @@ -157,7 +172,7 @@ export class BlockRewardsGraphComponent implements OnInit { if (tick.seriesIndex === 0) { tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.3-3')} BTC
`; } else if (tick.seriesIndex === 1) { - tooltip += `${tick.marker} ${tick.seriesName}: ${formatCurrency(tick.data[1], this.locale, getCurrencySymbol('USD', 'narrow'), 'USD', '1.0-0')}
`; + tooltip += `${tick.marker} ${tick.seriesName}: ${this.fiatCurrencyPipe.transform(tick.data[1], null, this.currency)}
`; } } @@ -184,7 +199,7 @@ export class BlockRewardsGraphComponent implements OnInit { icon: 'roundRect', }, { - name: 'Rewards USD', + name: 'Rewards ' + this.currency, inactiveColor: 'rgb(110, 112, 121)', textStyle: { color: 'white', @@ -228,7 +243,7 @@ export class BlockRewardsGraphComponent implements OnInit { axisLabel: { color: 'rgb(110, 112, 121)', formatter: function(val) { - return this.fiatShortenerPipe.transform(val); + return this.fiatShortenerPipe.transform(val, null, this.currency); }.bind(this) }, splitLine: { @@ -251,8 +266,8 @@ export class BlockRewardsGraphComponent implements OnInit { legendHoverLink: false, zlevel: 1, yAxisIndex: 1, - name: 'Rewards USD', - data: data.blockRewardsUSD, + name: 'Rewards ' + this.currency, + data: data.blockRewardsFiat, type: 'line', smooth: 0.25, symbol: 'none', From 368041e7d477dc66904314c2d9d66f0024d576a3 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 16 Jan 2023 11:05:30 -0600 Subject: [PATCH 12/67] Use selected currency in app-amount component --- .../src/app/components/amount/amount.component.html | 2 +- .../src/app/components/amount/amount.component.ts | 13 +++++++++++-- frontend/src/app/fiat/fiat.component.html | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/components/amount/amount.component.html b/frontend/src/app/components/amount/amount.component.html index c4946ddf8..3526b554b 100644 --- a/frontend/src/app/components/amount/amount.component.html +++ b/frontend/src/app/components/amount/amount.component.html @@ -1,5 +1,5 @@ - {{ addPlus && satoshis >= 0 ? '+' : '' }}{{ conversions.USD * (satoshis / 100000000) | currency:'USD':'symbol':'1.2-2' }} + {{ addPlus && satoshis >= 0 ? '+' : '' }}{{ (conversions ? conversions[currency] : 0) * satoshis / 100000000 | fiatCurrency : digitsInfo : currency }} diff --git a/frontend/src/app/components/amount/amount.component.ts b/frontend/src/app/components/amount/amount.component.ts index f9bba4318..cfdc50468 100644 --- a/frontend/src/app/components/amount/amount.component.ts +++ b/frontend/src/app/components/amount/amount.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, OnDestroy, Input, ChangeDetectionStrategy } from '@angular/core'; +import { Component, OnInit, OnDestroy, Input, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; import { StateService } from '../../services/state.service'; import { Observable, Subscription } from 'rxjs'; @@ -10,10 +10,12 @@ import { Observable, Subscription } from 'rxjs'; }) export class AmountComponent implements OnInit, OnDestroy { conversions$: Observable; + currency: string; viewFiat$: Observable; network = ''; stateSubscription: Subscription; + currencySubscription: Subscription; @Input() satoshis: number; @Input() digitsInfo = '1.8-8'; @@ -22,7 +24,13 @@ export class AmountComponent implements OnInit, OnDestroy { constructor( private stateService: StateService, - ) { } + private cd: ChangeDetectorRef, + ) { + this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => { + this.currency = fiat; + this.cd.markForCheck(); + }); + } ngOnInit() { this.viewFiat$ = this.stateService.viewFiat$.asObservable(); @@ -34,6 +42,7 @@ export class AmountComponent implements OnInit, OnDestroy { if (this.stateSubscription) { this.stateSubscription.unsubscribe(); } + this.currencySubscription.unsubscribe(); } } diff --git a/frontend/src/app/fiat/fiat.component.html b/frontend/src/app/fiat/fiat.component.html index 1adde8120..99a177cc0 100644 --- a/frontend/src/app/fiat/fiat.component.html +++ b/frontend/src/app/fiat/fiat.component.html @@ -1 +1 @@ -{{ (conversions$ | async)[currency] * value / 100000000 | fiatCurrency : digitsInfo : currency }} \ No newline at end of file +{{ (conversions ? conversions[currency] : 0) * value / 100000000 | fiatCurrency : digitsInfo : currency }} \ No newline at end of file From b269e99cfb7a48d5ccb32e2f177f3fae6656454b Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Thu, 2 Feb 2023 14:23:08 -0500 Subject: [PATCH 13/67] Update cpfp faq for stored relationships --- frontend/src/app/docs/api-docs/api-docs.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html index 48e9b48eb..4e99ce469 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -214,8 +214,8 @@

Mempool aims to show you the effective feerate range for blocks—how much would you actually need to pay to get a transaction included in a block.

-

A transaction's effective feerate is not always the same as the feerate explicitly set for it. For example, if you see a 1 s/vb transaction in a block with a displayed feerate range of 5 s/vb to 72 s/vb, chances are that 1 s/vb transaction had a high-feerate child transaction that boosted its effective feerate to 5 s/vb or higher (this is how CPFP fee-bumping works). In such a case, it would be misleading to use 1 s/vb as the lower bound of the block's feerate range because it actually required more than 1 s/vb to confirm that transaction in that block.

-

For unconfirmed CPFP transactions, Mempool will show the effective feerate (along with descendent & ancestor transaction information) on the transaction page. For confirmed transactions, CPFP relationships are not stored, so this additional information is not shown.

+

A transaction's effective feerate is not always the same as the feerate explicitly set for it. For example, if you see a 1 s/vb transaction in a block with a displayed feerate range of 5 s/vb to 72 s/vb, chances are that 1 s/vb transaction had a high-feerate child transaction that boosted its effective feerate to 5 s/vb or higher (this is how CPFP fee-bumping works). In such a case, it would be misleading to use 1 s/vb as the lower bound of the block's feerate range since it actually required more than 1 s/vb to confirm that transaction in that block.

+

You can find a transaction's feerate on its transaction details page. If the transaction has any CPFP relationships, the page will also show the transaction's effective feerate along with links to descendent and/or ancestor transactions.

From 900e66aef7aee9329c7bccc60a5d7eb1907d4176 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 2 Feb 2023 17:37:32 -0600 Subject: [PATCH 14/67] More robust error checking & handling in CPFP repositories --- backend/src/repositories/CpfpRepository.ts | 46 +++++++++++-------- .../src/repositories/TransactionRepository.ts | 34 +++++++------- 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/backend/src/repositories/CpfpRepository.ts b/backend/src/repositories/CpfpRepository.ts index ce7432d5b..9d8b2fe75 100644 --- a/backend/src/repositories/CpfpRepository.ts +++ b/backend/src/repositories/CpfpRepository.ts @@ -111,7 +111,7 @@ class CpfpRepository { } } - public async $getCluster(clusterRoot: string): Promise { + public async $getCluster(clusterRoot: string): Promise { const [clusterRows]: any = await DB.query( ` SELECT * @@ -121,8 +121,11 @@ class CpfpRepository { [clusterRoot] ); const cluster = clusterRows[0]; - cluster.txs = this.unpack(cluster.txs); - return cluster; + if (cluster?.txs) { + cluster.txs = this.unpack(cluster.txs); + return cluster; + } + return; } public async $deleteClustersFrom(height: number): Promise { @@ -136,9 +139,9 @@ class CpfpRepository { [height] ) as RowDataPacket[][]; if (rows?.length) { - for (let clusterToDelete of rows) { - const txs = this.unpack(clusterToDelete.txs); - for (let tx of txs) { + for (const clusterToDelete of rows) { + const txs = this.unpack(clusterToDelete?.txs); + for (const tx of txs) { await transactionRepository.$removeTransaction(tx.txid); } } @@ -204,20 +207,25 @@ class CpfpRepository { return []; } - const arrayBuffer = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); - const txs: Ancestor[] = []; - const view = new DataView(arrayBuffer); - for (let offset = 0; offset < arrayBuffer.byteLength; offset += 44) { - const txid = Array.from(new Uint8Array(arrayBuffer, offset, 32)).reverse().map(b => b.toString(16).padStart(2, '0')).join(''); - const weight = view.getUint32(offset + 32); - const fee = Number(view.getBigUint64(offset + 36)); - txs.push({ - txid, - weight, - fee - }); + try { + const arrayBuffer = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); + const txs: Ancestor[] = []; + const view = new DataView(arrayBuffer); + for (let offset = 0; offset < arrayBuffer.byteLength; offset += 44) { + const txid = Array.from(new Uint8Array(arrayBuffer, offset, 32)).reverse().map(b => b.toString(16).padStart(2, '0')).join(''); + const weight = view.getUint32(offset + 32); + const fee = Number(view.getBigUint64(offset + 36)); + txs.push({ + txid, + weight, + fee + }); + } + return txs; + } catch (e) { + logger.warn(`Failed to unpack CPFP cluster. Reason: ` + (e instanceof Error ? e.message : e)); + return []; } - return txs; } } diff --git a/backend/src/repositories/TransactionRepository.ts b/backend/src/repositories/TransactionRepository.ts index 061617451..279a2bdad 100644 --- a/backend/src/repositories/TransactionRepository.ts +++ b/backend/src/repositories/TransactionRepository.ts @@ -3,15 +3,6 @@ import logger from '../logger'; import { Ancestor, CpfpInfo } from '../mempool.interfaces'; import cpfpRepository from './CpfpRepository'; -interface CpfpSummary { - txid: string; - cluster: string; - root: string; - txs: Ancestor[]; - height: number; - fee_rate: number; -} - class TransactionRepository { public async $setCluster(txid: string, clusterRoot: string): Promise { try { @@ -72,7 +63,9 @@ class TransactionRepository { const txid = txRows[0].id.toLowerCase(); const clusterId = txRows[0].root.toLowerCase(); const cluster = await cpfpRepository.$getCluster(clusterId); - return this.convertCpfp(txid, cluster); + if (cluster) { + return this.convertCpfp(txid, cluster); + } } } catch (e) { logger.err('Cannot get transaction cpfp info from db. Reason: ' + (e instanceof Error ? e.message : e)); @@ -81,13 +74,18 @@ class TransactionRepository { } public async $removeTransaction(txid: string): Promise { - await DB.query( - ` - DELETE FROM compact_transactions - WHERE txid = UNHEX(?) - `, - [txid] - ); + try { + await DB.query( + ` + DELETE FROM compact_transactions + WHERE txid = UNHEX(?) + `, + [txid] + ); + } catch (e) { + logger.warn('Cannot delete transaction cpfp info from db. Reason: ' + (e instanceof Error ? e.message : e)); + throw e; + } } private convertCpfp(txid, cluster): CpfpInfo { @@ -95,7 +93,7 @@ class TransactionRepository { const ancestors: Ancestor[] = []; let matched = false; - for (const tx of cluster.txs) { + for (const tx of (cluster?.txs || [])) { if (tx.txid === txid) { matched = true; } else if (!matched) { From a67a074eaf6a761c8874e92e581228293d267090 Mon Sep 17 00:00:00 2001 From: Aroooba Date: Tue, 7 Feb 2023 05:12:44 +0900 Subject: [PATCH 15/67] Add contributor license agreement --- contributors/Arooba-git.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 contributors/Arooba-git.txt diff --git a/contributors/Arooba-git.txt b/contributors/Arooba-git.txt new file mode 100644 index 000000000..833d78ff4 --- /dev/null +++ b/contributors/Arooba-git.txt @@ -0,0 +1,3 @@ +I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of January 25, 2022. + +Signed: Arooba-git \ No newline at end of file From d66921938c672493996f0da1232a74686121a71e Mon Sep 17 00:00:00 2001 From: Aroooba Date: Tue, 7 Feb 2023 05:13:19 +0900 Subject: [PATCH 16/67] Unsubscribe subscription in component destructor to avoid memory leak --- frontend/src/app/docs/api-docs/api-docs.component.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/docs/api-docs/api-docs.component.ts b/frontend/src/app/docs/api-docs/api-docs.component.ts index 7b78d187b..0bffd5b2a 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.ts +++ b/frontend/src/app/docs/api-docs/api-docs.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, Input, QueryList, AfterViewInit, ViewChildren } from '@angular/core'; import { Env, StateService } from '../../services/state.service'; -import { Observable, merge, of } from 'rxjs'; -import { tap } from 'rxjs/operators'; +import { Observable, merge, of, Subject } from 'rxjs'; +import { tap, takeUntil } from 'rxjs/operators'; import { ActivatedRoute } from "@angular/router"; import { faqData, restApiDocsData, wsApiDocsData } from './api-docs-data'; import { FaqTemplateDirective } from '../faq-template/faq-template.component'; @@ -12,6 +12,7 @@ import { FaqTemplateDirective } from '../faq-template/faq-template.component'; styleUrls: ['./api-docs.component.scss'] }) export class ApiDocsComponent implements OnInit, AfterViewInit { + private destroy$: Subject = new Subject(); plainHostname = document.location.hostname; electrsPort = 0; hostname = document.location.hostname; @@ -82,7 +83,7 @@ export class ApiDocsComponent implements OnInit, AfterViewInit { this.restDocs = restApiDocsData; this.wsDocs = wsApiDocsData; - this.network$.subscribe((network) => { + this.network$.pipe(takeUntil(this.destroy$)).subscribe((network) => { this.active = (network === 'liquid' || network === 'liquidtestnet') ? 2 : 0; switch( network ) { case "": @@ -102,6 +103,8 @@ export class ApiDocsComponent implements OnInit, AfterViewInit { } ngOnDestroy(): void { + this.destroy$.next(true); + this.destroy$.complete(); window.removeEventListener('scroll', this.onDocScroll); } From 133044ab8ed100abb8f334ef759242eb9ce4cfe4 Mon Sep 17 00:00:00 2001 From: Alex Lloyd Date: Thu, 9 Feb 2023 16:27:36 +0100 Subject: [PATCH 17/67] Create AlexLloyd0.txt Contributing in https://github.com/mempool/mempool.js/pull/79 --- contributors/AlexLloyd0.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 contributors/AlexLloyd0.txt diff --git a/contributors/AlexLloyd0.txt b/contributors/AlexLloyd0.txt new file mode 100644 index 000000000..065dbfe11 --- /dev/null +++ b/contributors/AlexLloyd0.txt @@ -0,0 +1,3 @@ +I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of January 25, 2022. + +Signed: AlexLloyd0 From 8f3b8276c5cc58f8bf2b11bec9ad264c6b58224f Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 10 Feb 2023 07:26:49 -0600 Subject: [PATCH 18/67] safer mempool cloning for different GBT algorithms --- backend/src/api/websocket-handler.ts | 3 ++- backend/src/utils/clone.ts | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 backend/src/utils/clone.ts diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index cffbea346..599c068a6 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -19,6 +19,7 @@ import feeApi from './fee-api'; import BlocksAuditsRepository from '../repositories/BlocksAuditsRepository'; import BlocksSummariesRepository from '../repositories/BlocksSummariesRepository'; import Audit from './audit'; +import { deepClone } from '../utils/clone'; class WebsocketHandler { private wss: WebSocket.Server | undefined; @@ -421,7 +422,7 @@ class WebsocketHandler { let projectedBlocks; // template calculation functions have mempool side effects, so calculate audits using // a cloned copy of the mempool if we're running a different algorithm for mempool updates - const auditMempool = (config.MEMPOOL.ADVANCED_GBT_AUDIT === config.MEMPOOL.ADVANCED_GBT_MEMPOOL) ? _memPool : JSON.parse(JSON.stringify(_memPool)); + const auditMempool = (config.MEMPOOL.ADVANCED_GBT_AUDIT === config.MEMPOOL.ADVANCED_GBT_MEMPOOL) ? _memPool : deepClone(_memPool); if (config.MEMPOOL.ADVANCED_GBT_AUDIT) { projectedBlocks = await mempoolBlocks.makeBlockTemplates(auditMempool, false); } else { diff --git a/backend/src/utils/clone.ts b/backend/src/utils/clone.ts new file mode 100644 index 000000000..38e36096f --- /dev/null +++ b/backend/src/utils/clone.ts @@ -0,0 +1,14 @@ +// simple recursive deep clone for literal-type objects +// does not preserve Dates, Maps, Sets etc +// does not support recursive objects +// properties deeper than maxDepth will be shallow cloned +export function deepClone(obj: any, maxDepth: number = 50, depth: number = 0): any { + let cloned = obj; + if (depth < maxDepth && typeof obj === 'object') { + cloned = Array.isArray(obj) ? [] : {}; + for (const key in obj) { + cloned[key] = deepClone(obj[key], maxDepth, depth + 1); + } + } + return cloned; +} \ No newline at end of file From c85d8cd29d6ff2e5b2da4be753eacbb363f40ca9 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 12 Feb 2023 21:42:33 -0600 Subject: [PATCH 19/67] Fix firefox blockchain scroll reset bug --- frontend/src/app/components/start/start.component.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/app/components/start/start.component.ts b/frontend/src/app/components/start/start.component.ts index d29372d97..ff66e7b97 100644 --- a/frontend/src/app/components/start/start.component.ts +++ b/frontend/src/app/components/start/start.component.ts @@ -298,6 +298,10 @@ export class StartComponent implements OnInit, OnDestroy { } ngOnDestroy() { + if (this.blockchainContainer?.nativeElement) { + // clean up scroll position to prevent caching wrong scroll in Firefox + this.blockchainContainer.nativeElement.scrollLeft = 0; + } this.timeLtrSubscription.unsubscribe(); this.chainTipSubscription.unsubscribe(); this.markBlockSubscription.unsubscribe(); From 0176afa394b52c6a9b6b01272a4c882c95cc6ac2 Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Mon, 13 Feb 2023 14:58:35 +0900 Subject: [PATCH 20/67] Add `--reindex` doc to backend README --- backend/README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/backend/README.md b/backend/README.md index 3d7c23eaa..d00dc1812 100644 --- a/backend/README.md +++ b/backend/README.md @@ -218,3 +218,21 @@ Generate block at regular interval (every 10 seconds in this example): ``` watch -n 10 "./src/bitcoin-cli -regtest -rpcport=8332 generatetoaddress 1 $address" ``` + +### Re-index tables + +You can manually force the nodejs backend to drop all data from a specified set of tables for future re-index. This is mostly useful for the mining dashboard and the lightning explorer. + +Use the `--reindex` command to specify a list of comma separated table which will be truncated at start. Note that a 5 seconds delay will be observed before truncating tables in order to give you a chance to cancel (CTRL+C) in case of misuse of the command. + +Usage: +``` +npm run start --reindex=blocks,hashrates +``` +Example output: +``` +Feb 13 14:55:27 [63246] WARN: Indexed data for "hashrates" tables will be erased in 5 seconds (using '--reindex') +Feb 13 14:55:32 [63246] NOTICE: Table hashrates has been truncated +``` + +Reference: https://github.com/mempool/mempool/pull/1269 \ No newline at end of file From cf720c4beff98b8619816eccebd788976f5be513 Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Mon, 13 Feb 2023 15:50:22 +0900 Subject: [PATCH 21/67] Fix missing fee range in blocks api when querying non indexed blocks --- backend/src/utils/blocks-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/utils/blocks-utils.ts b/backend/src/utils/blocks-utils.ts index b933d6ae7..43a2fc964 100644 --- a/backend/src/utils/blocks-utils.ts +++ b/backend/src/utils/blocks-utils.ts @@ -17,7 +17,7 @@ export function prepareBlock(block: any): BlockExtended { extras: { coinbaseRaw: block.coinbase_raw ?? block.extras?.coinbaseRaw, medianFee: block.medianFee ?? block.median_fee ?? block.extras?.medianFee, - feeRange: block.feeRange ?? block.fee_span, + feeRange: block.feeRange ?? block?.extras?.feeRange ?? block.fee_span, reward: block.reward ?? block?.extras?.reward, totalFees: block.totalFees ?? block?.fees ?? block?.extras?.totalFees, avgFee: block?.extras?.avgFee ?? block.avg_fee, From 198f85af2d8d1b10635079ef0a45aab9537475d0 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Mon, 13 Feb 2023 04:08:12 -0500 Subject: [PATCH 22/67] Add block audit faq --- .../src/app/docs/api-docs/api-docs-data.ts | 7 ++++ .../app/docs/api-docs/api-docs.component.html | 34 ++++++++++++--- .../app/docs/api-docs/api-docs.component.scss | 42 +++++++++++++++++++ 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts index f4b4602ad..069dabacf 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -8667,6 +8667,13 @@ export const faqData = [ fragment: "why-dont-fee-ranges-match", title: "Why doesn't the fee range shown for a block match the feerates of transactions within the block?", }, + { + type: "endpoint", + category: "advanced", + showConditions: bitcoinNetworks, + fragment: "how-do-block-audits-work", + title: "How do block audits work?", + }, { type: "endpoint", category: "advanced", diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html index 48e9b48eb..445b2c102 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -218,17 +218,41 @@

For unconfirmed CPFP transactions, Mempool will show the effective feerate (along with descendent & ancestor transaction information) on the transaction page. For confirmed transactions, CPFP relationships are not stored, so this additional information is not shown.

+ +

A block audit visually compares Mempool's expected block to the actual block for a particular block height.

+

How is the expected block determined? Mempool monitors its view of the mempool and runs a re-implementation of Bitcoin Core's transaction selection algorithm to determine the transactions it expects to see in upcoming blocks (source code here). Since there is a continual flow of new transactions, this algorithm runs every 2 seconds, and as a result, you will see the transactions projected to be in upcoming blocks change in near real-time.

+

At the moment a new block is mined, Mempool saves a snapshot of its projected block template for the next block. We call this snapshot the expected block for the block height in question, and it serves as the basis for the block audit.

+

When details for an expected block and actual block are available, we can compare them. The purpose of block audits is to deduce when miners intentionally include or exclude transactions from blocks they mine. Since this information cannot be precisely known, Mempool uses a handful of heuristics to accomplish this.

+

Block audits highlight transactions in different colors to convey these heuristics:

+
    +
  • Added

    A transaction is highlighted blue if it is not present in the expected block, present in the actual block, and also either:

    +
      +
    • far out of the expected feerate range, meaning the miner may have intentionally prioritized the transaction
    • +
    • not in the mempool at all, meaning the miner may have accepted the transaction out-of-band
    • +
    +

    Added transactions do not negatively affect block health.

  • +
  • Recently broadcasted

    A transaction is highlighted dark pink if it is present in the expected block, not present in the actual block, and was first seen by Mempool's Bitcoin node within 3 minutes of the block being mined.

    Due to network latency and other factors, it can take time for a miner's Bitcoin nodes to receive a transaction, so we do not assume a miner has intentionally excluded such a transaction from a block.

    Recently-broadcasted transactions do not negatively affect block health.

  • +
  • Marginal fee +

    A transaction is darkened if it is in the low end of the expected feerate range and missing in either the expected block or the actual block.

    Such a transaction may have been displaced by an added transaction, or it may have been displaced by another transaction from the mempool that was also at the low end of the expected feerate range for the block. In either case, the deviation is not considered notable.

    +

    Marginal fee transactions do not negatively affect block health.

  • +
  • Removed

    A transaction is highlighted bright pink if it is present in the expected block, not present in the actual block, and qualifies as neither recently-broadcasted nor marginal-fee. In other words, it has been in the mempool long enough to be widely propagated and has a feerate that is well within the range expected for the block. There is a chance such a transaction may have been intentionally excluded from the block.

    Removed transactions do negatively affect block health.

  • +
+

See how results of the block audit are used to devise the block health score below.

+
+ -

Block health indicates the extent of potential censorship in a block. This is determined by counting how many expected transactions a block is missing—a block that is not missing any expected transactions will have 100% health, while a block missing 1 or more expected transactions will have sub-100% health.

-

How does this work? Let sexpected be the set of all transactions Mempool expected to be in a block and let sactual be the set of all transactions actually in a block. Let n be the number of all transactions in both sexpected and sactual.

-

Then let r be the number of removed transactions—all transactions expected to be in sactual but not actually in it (excluding those that have been recently broadcast; see below).

+

Block health is a measure of how many transactions appear to be intentionally excluded from a block—a block without any transactions that appear intentionally excluded will have 100% health, while a block with 1 or more transactions that appear intentionally excluded will have sub-100% health.

+

As a result, block health is not intended to be a measure of how closely an expected block resembles an actual block.

+

How is it calculated? Let sexpected be the set of all transactions in Mempool's expected block and let sactual be the set of all transactions in the actual block. Then let n be the number of all transactions in both sexpected and sactual.

+

Furthermore, let r be the number of transactions Mempool deduces were intentionally excluded from sactual.

Block health is calculated as n / ( n + r ).

-

Transactions appearing in both sexpected and sactual are used (instead of a block's full transaction count) in order to minimize chances that block health is impacted by missing transactions that don't imply censorship:

+

The number of transactions appearing in both sexpected and sactual is used (instead of a block's full transaction count) in order to minimize chances that block health is inadvertently impacted by transactions that were most likely not intentionally excluded:

  • recently-broadcast transactions, since the miner may simply not have received them
  • certain low-feerate transactions, since the miner may have opted to replace them with more profitable out-of-band transactions
-

Mempool uses a re-implementation of Bitcoin Core's transaction selection algorithm to determine the transactions it expects to see in the next block.

+

As a result, block health is not intended to be a measure of how closely an expected block resembles an actual block. The actual block can be vastly different from the expected block, but if no transactions appear to be intentionally excluded, it will have a high health rating (extreme example).

+

See more context in our FAQ on block audits.

diff --git a/frontend/src/app/docs/api-docs/api-docs.component.scss b/frontend/src/app/docs/api-docs/api-docs.component.scss index db6d51cf2..92e78bc55 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.scss +++ b/frontend/src/app/docs/api-docs/api-docs.component.scss @@ -42,6 +42,48 @@ li.nav-item { } } +ul.no-bull { + list-style: none; +} + +ul.no-bull.block-audit li code { + text-transform: uppercase; +} + +ul.no-bull.block-audit li span { + margin-right: 10px; +} + +ul.no-bull.block-audit li span.block-audit-highlight-color.added { + color: #0099ff; +} + +ul.no-bull.block-audit li span.block-audit-highlight-color.removed { + color: #f344df; +} + +ul.no-bull.block-audit li span.block-audit-highlight-color.recent { + color: #8a3480; +} + +ul.no-bull.block-audit li span.block-audit-highlight-color.marginal { + color: #414127; +} + +ul.no-bull.block-audit li p { + margin-left: 25px; + margin-top: 5px; +} + +ul.no-bull.block-audit li ul { + margin-left: 15px; + margin-bottom: 15px; +} + +ul.no-bull.block-audit code{ + background-color: inherit; +} + .doc-welcome-note { margin-bottom: 0; } From 610597b687215be9857c4a4a38aede843f54ac94 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Mon, 13 Feb 2023 18:34:09 -0500 Subject: [PATCH 23/67] Change 'projected' to 'expected' for block audit --- frontend/src/app/components/block/block.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 059b9b708..98ee1ad67 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -206,13 +206,13 @@
-

Projected Block beta

+

Expected Block beta

Date: Mon, 13 Feb 2023 18:45:29 -0500 Subject: [PATCH 24/67] Remove repeated line --- frontend/src/app/docs/api-docs/api-docs.component.html | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html index 445b2c102..161e17ce8 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -242,7 +242,6 @@

Block health is a measure of how many transactions appear to be intentionally excluded from a block—a block without any transactions that appear intentionally excluded will have 100% health, while a block with 1 or more transactions that appear intentionally excluded will have sub-100% health.

-

As a result, block health is not intended to be a measure of how closely an expected block resembles an actual block.

How is it calculated? Let sexpected be the set of all transactions in Mempool's expected block and let sactual be the set of all transactions in the actual block. Then let n be the number of all transactions in both sexpected and sactual.

Furthermore, let r be the number of transactions Mempool deduces were intentionally excluded from sactual.

Block health is calculated as n / ( n + r ).

From 38ce8b8dc1835ea8642ff83d434c88d44c0d3ed9 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Mon, 13 Feb 2023 19:39:46 -0500 Subject: [PATCH 25/67] Add note about audit/health availability Not available on non-official instances. --- frontend/src/app/docs/api-docs/api-docs.component.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html index 161e17ce8..7a7b9b416 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -238,6 +238,7 @@
  • Removed

    A transaction is highlighted bright pink if it is present in the expected block, not present in the actual block, and qualifies as neither recently-broadcasted nor marginal-fee. In other words, it has been in the mempool long enough to be widely propagated and has a feerate that is well within the range expected for the block. There is a chance such a transaction may have been intentionally excluded from the block.

    Removed transactions do negatively affect block health.

  • See how results of the block audit are used to devise the block health score below.

    +

    Because of this feature's resource usage and availability requirements, it is only supported on official mempool.space instances.

    @@ -252,6 +253,7 @@

    As a result, block health is not intended to be a measure of how closely an expected block resembles an actual block. The actual block can be vastly different from the expected block, but if no transactions appear to be intentionally excluded, it will have a high health rating (extreme example).

    See more context in our FAQ on block audits.

    +

    Because of this feature's resource usage and availability requirements, it is only supported on official mempool.space instances.

    From 771825f2242b8ce70f1c9a2bec3ed745cdadd149 Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn <100320+knorrium@users.noreply.github.com> Date: Mon, 13 Feb 2023 16:57:39 -0800 Subject: [PATCH 26/67] Add offset to blockchain blocks classes and locators --- .../blockchain-blocks.component.html | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html index 29df378a4..17c5709fd 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html @@ -1,64 +1,86 @@ -
    +
    -
    +
     
    -
    - ~{{ block?.extras?.medianFee | number:feeRounding }} sat/vB +
    + ~{{ block?.extras?.medianFee | number:feeRounding }} sat/vB
    -
    - {{ block?.extras?.feeRange?.[1] | number:feeRounding }} - {{ block?.extras?.feeRange[block?.extras?.feeRange?.length - 1] | number:feeRounding }} sat/vB +
    + {{ block?.extras?.feeRange?.[1] | number:feeRounding }} - {{ + block?.extras?.feeRange[block?.extras?.feeRange?.length - 1] | number:feeRounding }} sat/vB
    -
    +
     
    -
    +
    -
    +
    - - {{ i }} transaction - {{ i }} transactions + + {{ i }} + transaction + {{ i }} + transactions
    -
    +
    +
    -
    - +
    +
    -
    +
    -
    +
    -
    -
    +
    +
    - From 635fadd13f39b83deba8953fe7cdf58b958645d0 Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn <100320+knorrium@users.noreply.github.com> Date: Mon, 13 Feb 2023 16:58:25 -0800 Subject: [PATCH 27/67] Update mainnet tests: increase blocks to 22, update locators and skip a test --- frontend/cypress/e2e/mainnet/mainnet.spec.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/cypress/e2e/mainnet/mainnet.spec.ts b/frontend/cypress/e2e/mainnet/mainnet.spec.ts index d6fe94dac..5ab3f9ce9 100644 --- a/frontend/cypress/e2e/mainnet/mainnet.spec.ts +++ b/frontend/cypress/e2e/mainnet/mainnet.spec.ts @@ -64,7 +64,7 @@ describe('Mainnet', () => { it('loads the status screen', () => { cy.visit('/status'); cy.get('#mempool-block-0').should('be.visible'); - cy.get('[id^="bitcoin-block-"]').should('have.length', 8); + cy.get('[id^="bitcoin-block-"]').should('have.length', 22); cy.get('.footer').should('be.visible'); cy.get('.row > :nth-child(1)').invoke('text').then((text) => { expect(text).to.match(/Incoming transactions.* vB\/s/); @@ -219,11 +219,11 @@ describe('Mainnet', () => { describe('blocks navigation', () => { describe('keyboard events', () => { - it('loads first blockchain blocks visible and keypress arrow right', () => { + it('loads first blockchain block visible and keypress arrow right', () => { cy.viewport('macbook-16'); cy.visit('/'); cy.waitForSkeletonGone(); - cy.get('.blockchain-blocks-0 > a').click().then(() => { + cy.get('[data-cy="bitcoin-block-offset-0-index-0"]').click().then(() => { cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('not.exist'); cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible'); cy.waitForPageIdle(); @@ -233,11 +233,11 @@ describe('Mainnet', () => { }); }); - it('loads first blockchain blocks visible and keypress arrow left', () => { + it('loads first blockchain block visible and keypress arrow left', () => { cy.viewport('macbook-16'); cy.visit('/'); cy.waitForSkeletonGone(); - cy.get('.blockchain-blocks-0 > a').click().then(() => { + cy.get('[data-cy="bitcoin-block-offset-0-index-0"]').click().then(() => { cy.waitForPageIdle(); cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('not.exist'); cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible'); @@ -246,11 +246,11 @@ describe('Mainnet', () => { }); }); - it('loads last blockchain blocks and keypress arrow right', () => { + it.skip('loads last blockchain block and keypress arrow right', () => { //Skip for now as "last" doesn't really work with infinite scrolling cy.viewport('macbook-16'); cy.visit('/'); cy.waitForSkeletonGone(); - cy.get('.blockchain-blocks-4 > a').click().then(() => { + cy.get('bitcoin-block-offset-0-index-7').click().then(() => { cy.waitForPageIdle(); // block 6 @@ -309,7 +309,7 @@ describe('Mainnet', () => { cy.viewport('macbook-16'); cy.visit('/'); cy.waitForSkeletonGone(); - cy.get('.blockchain-blocks-0 > a').click().then(() => { + cy.get('[data-cy="bitcoin-block-offset-0-index-0"]').click().then(() => { cy.waitForPageIdle(); cy.get('[ngbtooltip="Next Block"] > .ng-fa-icon > .svg-inline--fa').should('not.exist'); cy.get('[ngbtooltip="Previous Block"] > .ng-fa-icon > .svg-inline--fa').should('be.visible'); From d8ebc5a92c68724d56c1b4fb49431af9803f6f3e Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Mon, 13 Feb 2023 20:12:31 -0500 Subject: [PATCH 28/67] Only show audit and health faqs on official --- frontend/src/app/docs/api-docs/api-docs-data.ts | 2 ++ .../app/docs/api-docs/api-docs-nav.component.html | 2 +- .../src/app/docs/api-docs/api-docs-nav.component.ts | 9 ++++++++- .../src/app/docs/api-docs/api-docs.component.html | 12 +++++++----- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts index 069dabacf..5cc5ca3aa 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -8671,6 +8671,7 @@ export const faqData = [ type: "endpoint", category: "advanced", showConditions: bitcoinNetworks, + options: { officialOnly: true }, fragment: "how-do-block-audits-work", title: "How do block audits work?", }, @@ -8678,6 +8679,7 @@ export const faqData = [ type: "endpoint", category: "advanced", showConditions: bitcoinNetworks, + options: { officialOnly: true }, fragment: "what-is-block-health", title: "What is block health?", }, diff --git a/frontend/src/app/docs/api-docs/api-docs-nav.component.html b/frontend/src/app/docs/api-docs/api-docs-nav.component.html index c8460ada4..cbfe8a170 100644 --- a/frontend/src/app/docs/api-docs/api-docs-nav.component.html +++ b/frontend/src/app/docs/api-docs/api-docs-nav.component.html @@ -1,4 +1,4 @@

    {{ item.title }}

    - {{ item.title }} + {{ item.title }}
    diff --git a/frontend/src/app/docs/api-docs/api-docs-nav.component.ts b/frontend/src/app/docs/api-docs/api-docs-nav.component.ts index df73030c9..ad9d0b9a5 100644 --- a/frontend/src/app/docs/api-docs/api-docs-nav.component.ts +++ b/frontend/src/app/docs/api-docs/api-docs-nav.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { Env, StateService } from '../../services/state.service'; import { restApiDocsData } from './api-docs-data'; import { faqData } from './api-docs-data'; @@ -12,11 +13,17 @@ export class ApiDocsNavComponent implements OnInit { @Input() network: any; @Input() whichTab: string; @Output() navLinkClickEvent: EventEmitter = new EventEmitter(); + env: Env; tabData: any[]; + officialMempoolInstance: boolean; - constructor() { } + constructor( + private stateService: StateService + ) { } ngOnInit(): void { + this.env = this.stateService.env; + this.officialMempoolInstance = this.env.OFFICIAL_MEMPOOL_SPACE; if (this.whichTab === 'rest') { this.tabData = restApiDocsData; } else if (this.whichTab === 'faq') { diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html index 7a7b9b416..be3fd2f42 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -15,11 +15,13 @@
    -

    {{ item.title }}

    -
    -
    {{ item.title }}{{ item.category }}
    -
    - +
    +

    {{ item.title }}

    +
    From b817ee9e5cef0cc43dda4bab910bbc9b84309005 Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn <100320+knorrium@users.noreply.github.com> Date: Mon, 13 Feb 2023 18:22:53 -0800 Subject: [PATCH 29/67] Update staging hosts to fra --- frontend/proxy.conf.staging.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/proxy.conf.staging.js b/frontend/proxy.conf.staging.js index 0281b66ce..0cf366ca7 100644 --- a/frontend/proxy.conf.staging.js +++ b/frontend/proxy.conf.staging.js @@ -3,8 +3,8 @@ const fs = require('fs'); let PROXY_CONFIG = require('./proxy.conf'); PROXY_CONFIG.forEach(entry => { - entry.target = entry.target.replace("mempool.space", "mempool-staging.tk7.mempool.space"); - entry.target = entry.target.replace("liquid.network", "liquid-staging.tk7.mempool.space"); + entry.target = entry.target.replace("mempool.space", "mempool-staging.fra.mempool.space"); + entry.target = entry.target.replace("liquid.network", "liquid-staging.fra.mempool.space"); entry.target = entry.target.replace("bisq.markets", "bisq-staging.fra.mempool.space"); }); From 33d7a0af60d34d71f540d8d20b17270709a5980e Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Tue, 14 Feb 2023 02:07:44 -0500 Subject: [PATCH 30/67] Edit i18n attributes --- frontend/src/app/components/block/block.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 98ee1ad67..23a826bae 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -206,13 +206,13 @@
    -

    Expected Block beta

    +

    Expected Block beta

    Date: Tue, 14 Feb 2023 03:01:37 -0500 Subject: [PATCH 31/67] Fix error in logic for official_mempool_space --- frontend/src/app/docs/api-docs/api-docs-nav.component.html | 2 +- frontend/src/app/docs/api-docs/api-docs.component.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/docs/api-docs/api-docs-nav.component.html b/frontend/src/app/docs/api-docs/api-docs-nav.component.html index cbfe8a170..3abdc91be 100644 --- a/frontend/src/app/docs/api-docs/api-docs-nav.component.html +++ b/frontend/src/app/docs/api-docs/api-docs-nav.component.html @@ -1,4 +1,4 @@

    {{ item.title }}

    - {{ item.title }} + {{ item.title }}
    diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html index be3fd2f42..c984c2b77 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -15,7 +15,7 @@
    -
    +

    {{ item.title }}

    {{ item.title }}{{ item.category }}
    From 491f2c628064f86ec2e59c72a1c9dd34e0f12983 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Tue, 14 Feb 2023 03:40:00 -0500 Subject: [PATCH 32/67] Add info icon on audit linking to audit faq --- frontend/src/app/components/block/block.component.html | 2 +- frontend/src/app/components/block/block.component.scss | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 059b9b708..62bb67755 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -221,7 +221,7 @@
    -

    Actual Block

    +

    Actual Block

    Date: Tue, 14 Feb 2023 04:32:15 -0500 Subject: [PATCH 33/67] Make audit info icon look better --- frontend/src/app/components/block/block.component.html | 2 +- frontend/src/app/components/block/block.component.scss | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 62bb67755..0b9cded08 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -221,7 +221,7 @@
    -

    Actual Block

    +

    Actual Block

    Date: Tue, 14 Feb 2023 17:20:01 +0700 Subject: [PATCH 34/67] Extracting i18n --- frontend/src/locale/messages.xlf | 91 ++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/frontend/src/locale/messages.xlf b/frontend/src/locale/messages.xlf index c94eaae9a..eeea07658 100644 --- a/frontend/src/locale/messages.xlf +++ b/frontend/src/locale/messages.xlf @@ -546,7 +546,7 @@ src/app/components/master-page/master-page.component.html - 49,51 + 48,50 src/app/components/pool-ranking/pool-ranking.component.html @@ -1420,7 +1420,7 @@ src/app/components/master-page/master-page.component.html - 58,61 + 57,60 @@ -2453,19 +2453,11 @@ Previous Block - - Health + + Health src/app/components/block/block.component.html - 56,59 - - - src/app/components/blocks-list/blocks-list.component.html - 18,19 - - - src/app/components/blocks-list/blocks-list.component.html - 18,19 + 56,57 latest-blocks.health @@ -2550,13 +2542,25 @@ Total subsidy and fees in a block block.subsidy-and-fees - - Projected + + Expected src/app/components/block/block.component.html - 209,211 + 209 - block.projected + block.expected + + + beta + + src/app/components/block/block.component.html + 209,210 + + + src/app/components/block/block.component.html + 215,217 + + beta Actual @@ -2566,19 +2570,20 @@ block.actual - - Projected Block + + Expected Block src/app/components/block/block.component.html - 215,217 + 215 - block.projected-block + block.expected-block - - Actual Block + + Actual Block src/app/components/block/block.component.html - 224,226 + 224,225 block.actual-block @@ -2752,6 +2757,18 @@ latest-blocks.mined + + Health + + src/app/components/blocks-list/blocks-list.component.html + 18,19 + + + src/app/components/blocks-list/blocks-list.component.html + 18,19 + + latest-blocks.health + Reward @@ -3308,7 +3325,7 @@ src/app/components/master-page/master-page.component.html - 52,54 + 51,53 src/app/components/statistics/statistics.component.ts @@ -3332,7 +3349,7 @@ Lightning Explorer src/app/components/master-page/master-page.component.html - 44,45 + 44,47 src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts @@ -3340,19 +3357,11 @@ master-page.lightning - - beta - - src/app/components/master-page/master-page.component.html - 45,48 - - beta - Documentation src/app/components/master-page/master-page.component.html - 55,57 + 54,56 src/app/docs/docs/docs.component.html @@ -4760,7 +4769,7 @@ REST API service src/app/docs/api-docs/api-docs.component.html - 39,40 + 41,42 api-docs.title @@ -4768,11 +4777,11 @@ Endpoint src/app/docs/api-docs/api-docs.component.html - 48,49 + 50,51 src/app/docs/api-docs/api-docs.component.html - 102,105 + 104,107 Api docs endpoint @@ -4780,18 +4789,18 @@ Description src/app/docs/api-docs/api-docs.component.html - 67,68 + 69,70 src/app/docs/api-docs/api-docs.component.html - 106,107 + 108,109 Default push: action: 'want', data: ['blocks', ...] to express what you want pushed. Available: blocks, mempool-blocks, live-2h-chart, and stats.Push transactions related to address: 'track-address': '3PbJ...bF9B' to receive all new transactions containing that address as input or output. Returns an array of transactions. address-transactions for new mempool transactions, and block-transactions for new block confirmed transactions. src/app/docs/api-docs/api-docs.component.html - 107,108 + 109,110 api-docs.websocket.websocket From 014113bb33eb741efcd3c4da86b04606ccc006d5 Mon Sep 17 00:00:00 2001 From: softsimon Date: Tue, 14 Feb 2023 17:24:48 +0700 Subject: [PATCH 35/67] Updating a few i18n strings --- .../app/components/block/block.component.html | 4 +- .../app/components/start/start.component.html | 2 +- frontend/src/locale/messages.xlf | 41 +++++++------------ 3 files changed, 17 insertions(+), 30 deletions(-) diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 01ea9ffed..7e30d4bbf 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -53,7 +53,7 @@ - Health + Health
    -

    Actual Block

    +

    Actual Block

    - +
    diff --git a/frontend/src/locale/messages.xlf b/frontend/src/locale/messages.xlf index eeea07658..a4154cbcd 100644 --- a/frontend/src/locale/messages.xlf +++ b/frontend/src/locale/messages.xlf @@ -2453,11 +2453,19 @@ Previous Block - - Health + + Health src/app/components/block/block.component.html - 56,57 + 56 + + + src/app/components/blocks-list/blocks-list.component.html + 18,19 + + + src/app/components/blocks-list/blocks-list.component.html + 18,19 latest-blocks.health @@ -2578,12 +2586,11 @@ block.expected-block - - Actual Block + + Actual Block src/app/components/block/block.component.html - 224,225 + 224 block.actual-block @@ -2757,18 +2764,6 @@ latest-blocks.mined - - Health - - src/app/components/blocks-list/blocks-list.component.html - 18,19 - - - src/app/components/blocks-list/blocks-list.component.html - 18,19 - - latest-blocks.health - Reward @@ -3910,14 +3905,6 @@ search-form.search-title - - Return to tip - - src/app/components/start/start.component.html - 20 - - blocks.return-to-tip - Mempool by vBytes (sat/vByte) From 56dad33fce4aa6c887be5ac9e9b9d3328ae89d7b Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Tue, 14 Feb 2023 22:12:43 +0900 Subject: [PATCH 36/67] Always return fully extended block in blocks API even if indexing is disabled --- backend/src/api/blocks.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 83de897ca..1d45d6c58 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -609,7 +609,9 @@ class Blocks { const transactions = await this.$getTransactionsExtended(blockHash, block.height, true); const blockExtended = await this.$getBlockExtended(block, transactions); - await blocksRepository.$saveBlockInDatabase(blockExtended); + if (Common.indexingEnabled()) { + await blocksRepository.$saveBlockInDatabase(blockExtended); + } return prepareBlock(blockExtended); } @@ -713,7 +715,7 @@ class Blocks { block = await this.$indexBlock(currentHeight); returnBlocks.push(block); } else if (nextHash != null) { - block = prepareBlock(await bitcoinClient.getBlock(nextHash)); + block = await this.$indexBlock(currentHeight); nextHash = block.previousblockhash; returnBlocks.push(block); } From 8f2255a7a2f97b146cc6c79a7f57b3c80a846b01 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 12 Feb 2023 21:43:12 -0600 Subject: [PATCH 37/67] Add audit / block health config feature flag --- backend/mempool-config.sample.json | 1 + .../__fixtures__/mempool-config.template.json | 1 + backend/src/__tests__/config.test.ts | 1 + backend/src/api/blocks.ts | 8 +- backend/src/api/websocket-handler.ts | 80 ++++++++++--------- backend/src/config.ts | 2 + docker/backend/mempool-config.json | 1 + docker/backend/start.sh | 2 + docker/frontend/entrypoint.sh | 2 + frontend/mempool-frontend-config.sample.json | 1 + .../app/components/block/block.component.ts | 26 +++--- .../blocks-list/blocks-list.component.html | 6 +- .../blocks-list/blocks-list.component.ts | 4 +- .../components/graphs/graphs.component.html | 2 +- frontend/src/app/services/state.service.ts | 2 + production/mempool-config.mainnet.json | 1 + production/mempool-config.signet.json | 1 + production/mempool-config.testnet.json | 1 + .../mempool-frontend-config.mainnet.json | 3 +- 19 files changed, 88 insertions(+), 57 deletions(-) diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index 1f64214ce..f8417f0e7 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -25,6 +25,7 @@ "AUTOMATIC_BLOCK_REINDEXING": false, "POOLS_JSON_URL": "https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json", "POOLS_JSON_TREE_URL": "https://api.github.com/repos/mempool/mining-pools/git/trees/master", + "AUDIT": false, "ADVANCED_GBT_AUDIT": false, "ADVANCED_GBT_MEMPOOL": false, "CPFP_INDEXING": false diff --git a/backend/src/__fixtures__/mempool-config.template.json b/backend/src/__fixtures__/mempool-config.template.json index e699c9458..f15d9c328 100644 --- a/backend/src/__fixtures__/mempool-config.template.json +++ b/backend/src/__fixtures__/mempool-config.template.json @@ -26,6 +26,7 @@ "INDEXING_BLOCKS_AMOUNT": 14, "POOLS_JSON_TREE_URL": "__POOLS_JSON_TREE_URL__", "POOLS_JSON_URL": "__POOLS_JSON_URL__", + "AUDIT": "__MEMPOOL_AUDIT__", "ADVANCED_GBT_AUDIT": "__MEMPOOL_ADVANCED_GBT_AUDIT__", "ADVANCED_GBT_MEMPOOL": "__MEMPOOL_ADVANCED_GBT_MEMPOOL__", "CPFP_INDEXING": "__MEMPOOL_CPFP_INDEXING__" diff --git a/backend/src/__tests__/config.test.ts b/backend/src/__tests__/config.test.ts index 4158d3df1..88083f479 100644 --- a/backend/src/__tests__/config.test.ts +++ b/backend/src/__tests__/config.test.ts @@ -38,6 +38,7 @@ describe('Mempool Backend Config', () => { STDOUT_LOG_MIN_PRIORITY: 'debug', POOLS_JSON_TREE_URL: 'https://api.github.com/repos/mempool/mining-pools/git/trees/master', POOLS_JSON_URL: 'https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json', + AUDIT: false, ADVANCED_GBT_AUDIT: false, ADVANCED_GBT_MEMPOOL: false, CPFP_INDEXING: false, diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 83de897ca..0f9e6b7f6 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -212,9 +212,11 @@ class Blocks { }; } - const auditScore = await BlocksAuditsRepository.$getBlockAuditScore(block.id); - if (auditScore != null) { - blockExtended.extras.matchRate = auditScore.matchRate; + if (config.MEMPOOL.AUDIT) { + const auditScore = await BlocksAuditsRepository.$getBlockAuditScore(block.id); + if (auditScore != null) { + blockExtended.extras.matchRate = auditScore.matchRate; + } } } diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index 599c068a6..9334f3717 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -419,49 +419,51 @@ class WebsocketHandler { const _memPool = memPool.getMempool(); - let projectedBlocks; - // template calculation functions have mempool side effects, so calculate audits using - // a cloned copy of the mempool if we're running a different algorithm for mempool updates - const auditMempool = (config.MEMPOOL.ADVANCED_GBT_AUDIT === config.MEMPOOL.ADVANCED_GBT_MEMPOOL) ? _memPool : deepClone(_memPool); - if (config.MEMPOOL.ADVANCED_GBT_AUDIT) { - projectedBlocks = await mempoolBlocks.makeBlockTemplates(auditMempool, false); - } else { - projectedBlocks = mempoolBlocks.updateMempoolBlocks(auditMempool, false); - } + if (config.MEMPOOL.AUDIT) { + let projectedBlocks; + // template calculation functions have mempool side effects, so calculate audits using + // a cloned copy of the mempool if we're running a different algorithm for mempool updates + const auditMempool = (config.MEMPOOL.ADVANCED_GBT_AUDIT === config.MEMPOOL.ADVANCED_GBT_MEMPOOL) ? _memPool : deepClone(_memPool); + if (config.MEMPOOL.ADVANCED_GBT_AUDIT) { + projectedBlocks = await mempoolBlocks.makeBlockTemplates(auditMempool, false); + } else { + projectedBlocks = mempoolBlocks.updateMempoolBlocks(auditMempool, false); + } - if (Common.indexingEnabled() && memPool.isInSync()) { - const { censored, added, fresh, score } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); - const matchRate = Math.round(score * 100 * 100) / 100; + if (Common.indexingEnabled() && memPool.isInSync()) { + const { censored, added, fresh, score } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); + const matchRate = Math.round(score * 100 * 100) / 100; - const stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions.map((tx) => { - return { - txid: tx.txid, - vsize: tx.vsize, - fee: tx.fee ? Math.round(tx.fee) : 0, - value: tx.value, - }; - }) : []; + const stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions.map((tx) => { + return { + txid: tx.txid, + vsize: tx.vsize, + fee: tx.fee ? Math.round(tx.fee) : 0, + value: tx.value, + }; + }) : []; - BlocksSummariesRepository.$saveTemplate({ - height: block.height, - template: { - id: block.id, - transactions: stripped + BlocksSummariesRepository.$saveTemplate({ + height: block.height, + template: { + id: block.id, + transactions: stripped + } + }); + + BlocksAuditsRepository.$saveAudit({ + time: block.timestamp, + height: block.height, + hash: block.id, + addedTxs: added, + missingTxs: censored, + freshTxs: fresh, + matchRate: matchRate, + }); + + if (block.extras) { + block.extras.matchRate = matchRate; } - }); - - BlocksAuditsRepository.$saveAudit({ - time: block.timestamp, - height: block.height, - hash: block.id, - addedTxs: added, - missingTxs: censored, - freshTxs: fresh, - matchRate: matchRate, - }); - - if (block.extras) { - block.extras.matchRate = matchRate; } } diff --git a/backend/src/config.ts b/backend/src/config.ts index fb06c84fb..a5736996f 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -29,6 +29,7 @@ interface IConfig { AUTOMATIC_BLOCK_REINDEXING: boolean; POOLS_JSON_URL: string, POOLS_JSON_TREE_URL: string, + AUDIT: boolean; ADVANCED_GBT_AUDIT: boolean; ADVANCED_GBT_MEMPOOL: boolean; CPFP_INDEXING: boolean; @@ -150,6 +151,7 @@ const defaults: IConfig = { 'AUTOMATIC_BLOCK_REINDEXING': false, 'POOLS_JSON_URL': 'https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json', 'POOLS_JSON_TREE_URL': 'https://api.github.com/repos/mempool/mining-pools/git/trees/master', + 'AUDIT': false, 'ADVANCED_GBT_AUDIT': false, 'ADVANCED_GBT_MEMPOOL': false, 'CPFP_INDEXING': false, diff --git a/docker/backend/mempool-config.json b/docker/backend/mempool-config.json index 2e3826f1d..17901acc4 100644 --- a/docker/backend/mempool-config.json +++ b/docker/backend/mempool-config.json @@ -23,6 +23,7 @@ "INDEXING_BLOCKS_AMOUNT": __MEMPOOL_INDEXING_BLOCKS_AMOUNT__, "BLOCKS_SUMMARIES_INDEXING": __MEMPOOL_BLOCKS_SUMMARIES_INDEXING__, "AUTOMATIC_BLOCK_REINDEXING": __MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__, + "AUDIT": __MEMPOOL_AUDIT__, "ADVANCED_GBT_AUDIT": __MEMPOOL_ADVANCED_GBT_AUDIT__, "ADVANCED_GBT_MEMPOOL": __MEMPOOL_ADVANCED_GBT_MEMPOOL__, "CPFP_INDEXING": __MEMPOOL_CPFP_INDEXING__ diff --git a/docker/backend/start.sh b/docker/backend/start.sh index 5f33df107..e02706bce 100755 --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -27,6 +27,7 @@ __MEMPOOL_INDEXING_BLOCKS_AMOUNT__=${MEMPOOL_INDEXING_BLOCKS_AMOUNT:=false} __MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__=${MEMPOOL_AUTOMATIC_BLOCK_REINDEXING:=false} __MEMPOOL_POOLS_JSON_URL__=${MEMPOOL_POOLS_JSON_URL:=https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json} __MEMPOOL_POOLS_JSON_TREE_URL__=${MEMPOOL_POOLS_JSON_TREE_URL:=https://api.github.com/repos/mempool/mining-pools/git/trees/master} +__MEMPOOL_AUDIT__=${MEMPOOL_AUDIT:=false} __MEMPOOL_ADVANCED_GBT_AUDIT__=${MEMPOOL_ADVANCED_GBT_AUDIT:=false} __MEMPOOL_ADVANCED_GBT_MEMPOOL__=${MEMPOOL_ADVANCED_GBT_MEMPOOL:=false} __MEMPOOL_CPFP_INDEXING__=${MEMPOOL_CPFP_INDEXING:=false} @@ -139,6 +140,7 @@ sed -i "s/__MEMPOOL_INDEXING_BLOCKS_AMOUNT__/${__MEMPOOL_INDEXING_BLOCKS_AMOUNT_ sed -i "s/__MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__/${__MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__}/g" mempool-config.json sed -i "s!__MEMPOOL_POOLS_JSON_URL__!${__MEMPOOL_POOLS_JSON_URL__}!g" mempool-config.json sed -i "s!__MEMPOOL_POOLS_JSON_TREE_URL__!${__MEMPOOL_POOLS_JSON_TREE_URL__}!g" mempool-config.json +sed -i "s!__MEMPOOL_AUDIT__!${__MEMPOOL_AUDIT__}!g" mempool-config.json sed -i "s!__MEMPOOL_ADVANCED_GBT_MEMPOOL__!${__MEMPOOL_ADVANCED_GBT_MEMPOOL__}!g" mempool-config.json sed -i "s!__MEMPOOL_ADVANCED_GBT_AUDIT__!${__MEMPOOL_ADVANCED_GBT_AUDIT__}!g" mempool-config.json sed -i "s!__MEMPOOL_CPFP_INDEXING__!${__MEMPOOL_CPFP_INDEXING__}!g" mempool-config.json diff --git a/docker/frontend/entrypoint.sh b/docker/frontend/entrypoint.sh index 3e2210360..18cb782e9 100644 --- a/docker/frontend/entrypoint.sh +++ b/docker/frontend/entrypoint.sh @@ -31,6 +31,7 @@ __LIQUID_WEBSITE_URL__=${LIQUID_WEBSITE_URL:=https://liquid.network} __BISQ_WEBSITE_URL__=${BISQ_WEBSITE_URL:=https://bisq.markets} __MINING_DASHBOARD__=${MINING_DASHBOARD:=true} __LIGHTNING__=${LIGHTNING:=false} +__AUDIT__=${AUDIT:=false} __MAINNET_BLOCK_AUDIT_START_HEIGHT__=${MAINNET_BLOCK_AUDIT_START_HEIGHT:=0} __TESTNET_BLOCK_AUDIT_START_HEIGHT__=${TESTNET_BLOCK_AUDIT_START_HEIGHT:=0} __SIGNET_BLOCK_AUDIT_START_HEIGHT__=${SIGNET_BLOCK_AUDIT_START_HEIGHT:=0} @@ -55,6 +56,7 @@ export __LIQUID_WEBSITE_URL__ export __BISQ_WEBSITE_URL__ export __MINING_DASHBOARD__ export __LIGHTNING__ +export __AUDIT__ export __MAINNET_BLOCK_AUDIT_START_HEIGHT__ export __TESTNET_BLOCK_AUDIT_START_HEIGHT__ export __SIGNET_BLOCK_AUDIT_START_HEIGHT__ diff --git a/frontend/mempool-frontend-config.sample.json b/frontend/mempool-frontend-config.sample.json index 5c0f92acf..9035315a4 100644 --- a/frontend/mempool-frontend-config.sample.json +++ b/frontend/mempool-frontend-config.sample.json @@ -17,6 +17,7 @@ "LIQUID_WEBSITE_URL": "https://liquid.network", "BISQ_WEBSITE_URL": "https://bisq.markets", "MINING_DASHBOARD": true, + "AUDIT": false, "MAINNET_BLOCK_AUDIT_START_HEIGHT": 0, "TESTNET_BLOCK_AUDIT_START_HEIGHT": 0, "SIGNET_BLOCK_AUDIT_START_HEIGHT": 0, diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index f1ebfed6a..668d0af48 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -58,7 +58,7 @@ export class BlockComponent implements OnInit, OnDestroy { overviewError: any = null; webGlEnabled = true; indexingAvailable = false; - auditModeEnabled: boolean = !this.stateService.hideAudit.value; + auditModeEnabled: boolean = this.stateService.env.AUDIT && !this.stateService.hideAudit.value; auditAvailable = true; showAudit: boolean; isMobile = window.innerWidth <= 767.98; @@ -110,12 +110,15 @@ export class BlockComponent implements OnInit, OnDestroy { }); this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true); - this.setAuditAvailable(this.indexingAvailable); - this.auditPrefSubscription = this.stateService.hideAudit.subscribe((hide) => { - this.auditModeEnabled = !hide; - this.showAudit = this.auditAvailable && this.auditModeEnabled; - }); + this.setAuditAvailable(this.stateService.env.AUDIT && this.indexingAvailable); + + if (this.stateService.env.AUDIT) { + this.auditPrefSubscription = this.stateService.hideAudit.subscribe((hide) => { + this.auditModeEnabled = !hide; + this.showAudit = this.auditAvailable && this.auditModeEnabled; + }); + } this.txsLoadingStatus$ = this.route.paramMap .pipe( @@ -221,7 +224,9 @@ export class BlockComponent implements OnInit, OnDestroy { setTimeout(() => { this.nextBlockSubscription = this.apiService.getBlock$(block.previousblockhash).subscribe(); this.nextBlockTxListSubscription = this.electrsApiService.getBlockTransactions$(block.previousblockhash).subscribe(); - this.apiService.getBlockAudit$(block.previousblockhash); + if (this.stateService.env.AUDIT) { + this.apiService.getBlockAudit$(block.previousblockhash); + } }, 100); } this.updateAuditAvailableFromBlockHeight(block.height); @@ -269,7 +274,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.isLoadingOverview = false; }); - if (!this.indexingAvailable) { + if (!this.indexingAvailable || !this.stateService.env.AUDIT) { this.overviewSubscription = block$.pipe( startWith(null), pairwise(), @@ -300,7 +305,7 @@ export class BlockComponent implements OnInit, OnDestroy { }); } - if (this.indexingAvailable) { + if (this.indexingAvailable && this.stateService.env.AUDIT) { this.auditSubscription = block$.pipe( startWith(null), pairwise(), @@ -613,6 +618,9 @@ export class BlockComponent implements OnInit, OnDestroy { } updateAuditAvailableFromBlockHeight(blockHeight: number): void { + if (!this.stateService.env.AUDIT) { + this.setAuditAvailable(false); + } switch (this.stateService.network) { case 'testnet': if (blockHeight < this.stateService.env.TESTNET_BLOCK_AUDIT_START_HEIGHT) { diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.html b/frontend/src/app/components/blocks-list/blocks-list.component.html index 424ea2ec4..46da3fa91 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -14,7 +14,7 @@ i18n-ngbTooltip="mining.pool-name" ngbTooltip="Pool" placement="bottom" #miningpool [disableTooltip]="!isEllipsisActive(miningpool)">Pool Timestamp Mined - Health Reward @@ -45,7 +45,7 @@ - + - + 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 93f7814cf..1cc428be3 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -22,6 +22,7 @@ export class BlocksList implements OnInit, OnDestroy { latestScoreSubscription: Subscription; indexingAvailable = false; + auditAvailable = false; isLoading = true; loadingScores = true; fromBlockHeight = undefined; @@ -44,6 +45,7 @@ export class BlocksList implements OnInit, OnDestroy { ngOnInit(): void { this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true); + this.auditAvailable = this.stateService.env.AUDIT; if (!this.widget) { this.websocketService.want(['blocks']); @@ -111,7 +113,7 @@ export class BlocksList implements OnInit, OnDestroy { }, []) ); - if (this.indexingAvailable) { + if (this.indexingAvailable && this.auditAvailable) { this.auditScoreSubscription = this.fromHeightSubject.pipe( switchMap((fromBlockHeight) => { this.loadingScores = true; diff --git a/frontend/src/app/components/graphs/graphs.component.html b/frontend/src/app/components/graphs/graphs.component.html index dd47a4ac7..af5136a38 100644 --- a/frontend/src/app/components/graphs/graphs.component.html +++ b/frontend/src/app/components/graphs/graphs.component.html @@ -22,7 +22,7 @@ i18n="mining.block-rewards">Block Rewards Block Sizes and Weights - Block Prediction Accuracy
    diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 091490715..5761e0a55 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -39,6 +39,7 @@ export interface Env { BISQ_WEBSITE_URL: string; MINING_DASHBOARD: boolean; LIGHTNING: boolean; + AUDIT: boolean; MAINNET_BLOCK_AUDIT_START_HEIGHT: number; TESTNET_BLOCK_AUDIT_START_HEIGHT: number; SIGNET_BLOCK_AUDIT_START_HEIGHT: number; @@ -67,6 +68,7 @@ const defaultEnv: Env = { 'BISQ_WEBSITE_URL': 'https://bisq.markets', 'MINING_DASHBOARD': true, 'LIGHTNING': false, + 'AUDIT': false, 'MAINNET_BLOCK_AUDIT_START_HEIGHT': 0, 'TESTNET_BLOCK_AUDIT_START_HEIGHT': 0, 'SIGNET_BLOCK_AUDIT_START_HEIGHT': 0, diff --git a/production/mempool-config.mainnet.json b/production/mempool-config.mainnet.json index ab2fa69c1..1258e62fb 100644 --- a/production/mempool-config.mainnet.json +++ b/production/mempool-config.mainnet.json @@ -10,6 +10,7 @@ "POLL_RATE_MS": 1000, "INDEXING_BLOCKS_AMOUNT": -1, "BLOCKS_SUMMARIES_INDEXING": true, + "AUDIT": true, "ADVANCED_GBT_AUDIT": true, "ADVANCED_GBT_MEMPOOL": false, "USE_SECOND_NODE_FOR_MINFEE": true diff --git a/production/mempool-config.signet.json b/production/mempool-config.signet.json index 313a09679..3c661c39f 100644 --- a/production/mempool-config.signet.json +++ b/production/mempool-config.signet.json @@ -7,6 +7,7 @@ "SPAWN_CLUSTER_PROCS": 0, "API_URL_PREFIX": "/api/v1/", "INDEXING_BLOCKS_AMOUNT": -1, + "AUDIT": true, "ADVANCED_GBT_AUDIT": true, "ADVANCED_GBT_MEMPOOL": false, "POLL_RATE_MS": 1000 diff --git a/production/mempool-config.testnet.json b/production/mempool-config.testnet.json index 908df7886..352529c6e 100644 --- a/production/mempool-config.testnet.json +++ b/production/mempool-config.testnet.json @@ -7,6 +7,7 @@ "SPAWN_CLUSTER_PROCS": 0, "API_URL_PREFIX": "/api/v1/", "INDEXING_BLOCKS_AMOUNT": -1, + "AUDIT": true, "ADVANCED_GBT_AUDIT": true, "ADVANCED_GBT_MEMPOOL": false, "POLL_RATE_MS": 1000 diff --git a/production/mempool-frontend-config.mainnet.json b/production/mempool-frontend-config.mainnet.json index e612c0440..5eb3d0a7d 100644 --- a/production/mempool-frontend-config.mainnet.json +++ b/production/mempool-frontend-config.mainnet.json @@ -10,5 +10,6 @@ "LIQUID_WEBSITE_URL": "https://liquid.network", "BISQ_WEBSITE_URL": "https://bisq.markets", "ITEMS_PER_PAGE": 25, - "LIGHTNING": true + "LIGHTNING": true, + "AUDIT": true } From b4c30cad5c22a812c5cd1fcdd47ff0482aa7201f Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 14 Feb 2023 12:23:26 -0600 Subject: [PATCH 38/67] simplify audit availability logic --- .../app/components/block/block.component.ts | 20 +++++++++---------- .../blocks-list/blocks-list.component.html | 6 +++--- .../blocks-list/blocks-list.component.ts | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 668d0af48..ba5dd8cf7 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -57,8 +57,8 @@ export class BlockComponent implements OnInit, OnDestroy { transactionsError: any = null; overviewError: any = null; webGlEnabled = true; - indexingAvailable = false; - auditModeEnabled: boolean = this.stateService.env.AUDIT && !this.stateService.hideAudit.value; + auditSupported: boolean = this.stateService.env.AUDIT && this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true; + auditModeEnabled: boolean = !this.stateService.hideAudit.value; auditAvailable = true; showAudit: boolean; isMobile = window.innerWidth <= 767.98; @@ -109,11 +109,9 @@ export class BlockComponent implements OnInit, OnDestroy { this.timeLtr = !!ltr; }); - this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true); + this.setAuditAvailable(this.auditSupported); - this.setAuditAvailable(this.stateService.env.AUDIT && this.indexingAvailable); - - if (this.stateService.env.AUDIT) { + if (this.auditSupported) { this.auditPrefSubscription = this.stateService.hideAudit.subscribe((hide) => { this.auditModeEnabled = !hide; this.showAudit = this.auditAvailable && this.auditModeEnabled; @@ -224,7 +222,7 @@ export class BlockComponent implements OnInit, OnDestroy { setTimeout(() => { this.nextBlockSubscription = this.apiService.getBlock$(block.previousblockhash).subscribe(); this.nextBlockTxListSubscription = this.electrsApiService.getBlockTransactions$(block.previousblockhash).subscribe(); - if (this.stateService.env.AUDIT) { + if (this.auditSupported) { this.apiService.getBlockAudit$(block.previousblockhash); } }, 100); @@ -274,7 +272,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.isLoadingOverview = false; }); - if (!this.indexingAvailable || !this.stateService.env.AUDIT) { + if (!this.auditSupported) { this.overviewSubscription = block$.pipe( startWith(null), pairwise(), @@ -305,7 +303,7 @@ export class BlockComponent implements OnInit, OnDestroy { }); } - if (this.indexingAvailable && this.stateService.env.AUDIT) { + if (this.auditSupported) { this.auditSubscription = block$.pipe( startWith(null), pairwise(), @@ -610,7 +608,7 @@ export class BlockComponent implements OnInit, OnDestroy { setAuditAvailable(available: boolean): void { this.auditAvailable = available; - this.showAudit = this.auditAvailable && this.auditModeEnabled; + this.showAudit = this.auditAvailable && this.auditModeEnabled && this.auditSupported; } toggleAuditMode(): void { @@ -618,7 +616,7 @@ export class BlockComponent implements OnInit, OnDestroy { } updateAuditAvailableFromBlockHeight(blockHeight: number): void { - if (!this.stateService.env.AUDIT) { + if (!this.auditSupported) { this.setAuditAvailable(false); } switch (this.stateService.network) { diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.html b/frontend/src/app/components/blocks-list/blocks-list.component.html index 46da3fa91..ffb5f5f88 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -14,7 +14,7 @@ i18n-ngbTooltip="mining.pool-name" ngbTooltip="Pool" placement="bottom" #miningpool [disableTooltip]="!isEllipsisActive(miningpool)">Pool Timestamp Mined - Health Reward @@ -45,7 +45,7 @@ - + - + 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 1cc428be3..160e0c882 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -45,7 +45,7 @@ export class BlocksList implements OnInit, OnDestroy { ngOnInit(): void { this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true); - this.auditAvailable = this.stateService.env.AUDIT; + this.auditAvailable = this.indexingAvailable && this.stateService.env.AUDIT; if (!this.widget) { this.websocketService.want(['blocks']); From 9f1a6ad31d7df6538d09f1d41536fc08f8402f34 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Wed, 15 Feb 2023 00:56:02 -0500 Subject: [PATCH 39/67] Remove colon from block overview table --- frontend/src/app/components/block/block.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 7e30d4bbf..0fda470d6 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -143,7 +143,7 @@ - Subsidy + fees: + Subsidy + fees @@ -158,7 +158,7 @@ - Subsidy + fees: + Subsidy + fees From fddbf510841f74653e02d516eb43f10bc5626d3e Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Wed, 15 Feb 2023 15:01:07 +0900 Subject: [PATCH 40/67] Only show supported currencies - Tweak UI --- frontend/src/app/app.constants.ts | 56 ------------------- .../fiat-selector.component.html | 4 +- .../fiat-selector/fiat-selector.component.ts | 11 +++- .../language-selector.component.html | 2 +- 4 files changed, 12 insertions(+), 61 deletions(-) diff --git a/frontend/src/app/app.constants.ts b/frontend/src/app/app.constants.ts index 0456f2647..5cc446dbf 100644 --- a/frontend/src/app/app.constants.ts +++ b/frontend/src/app/app.constants.ts @@ -159,19 +159,11 @@ export const specialBlocks = { }; export const fiatCurrencies = { - AED: { - name: 'UAE Dirham', - code: 'AED' - }, AUD: { name: 'Australian Dollar', code: 'AUD', indexed: true, }, - BRL: { - name: 'Brazilian Real', - code: 'BRL' - }, CAD: { name: 'Canadian Dollar', code: 'CAD', @@ -192,62 +184,14 @@ export const fiatCurrencies = { code: 'GBP', indexed: true, }, - HKD: { - name: 'Hong Kong Dollar', - code: 'HKD' - }, - IDR: { - name: 'Indonesian Rupiah', - code: 'IDR' - }, JPY: { name: 'Japanese Yen', code: 'JPY', indexed: true, }, - KRW: { - name: 'Korean Won', - code: 'KRW' - }, - MYR: { - name: 'Malaysian Ringgit', - code: 'MYR' - }, - NGN: { - name: 'Nigerian Naira', - code: 'NGN' - }, - NZD: { - name: 'New Zealand Dollar', - code: 'NZD' - }, - PLN: { - name: 'Polish Złoty', - code: 'PLN' - }, - RUB: { - name: 'Russian Ruble', - code: 'RUB' - }, - SGD: { - name: 'Singapore Dollar', - code: 'SGD' - }, - TRY: { - name: 'Turkish Lira', - code: 'TRY' - }, - UAH: { - name: 'Ukrainian Hryvnia', - code: 'UAH' - }, USD: { name: 'US Dollar', code: 'USD', indexed: true, }, - ZAR: { - name: 'South African Rand', - code: 'ZAR' - }, }; \ No newline at end of file diff --git a/frontend/src/app/components/fiat-selector/fiat-selector.component.html b/frontend/src/app/components/fiat-selector/fiat-selector.component.html index 7fa8c0d00..dd32b1815 100644 --- a/frontend/src/app/components/fiat-selector/fiat-selector.component.html +++ b/frontend/src/app/components/fiat-selector/fiat-selector.component.html @@ -1,5 +1,5 @@
    - +
    diff --git a/frontend/src/app/components/fiat-selector/fiat-selector.component.ts b/frontend/src/app/components/fiat-selector/fiat-selector.component.ts index f967b7d77..337ef11f3 100644 --- a/frontend/src/app/components/fiat-selector/fiat-selector.component.ts +++ b/frontend/src/app/components/fiat-selector/fiat-selector.component.ts @@ -12,8 +12,15 @@ import { StateService } from '../../services/state.service'; }) export class FiatSelectorComponent implements OnInit { fiatForm: UntypedFormGroup; - currencies = fiatCurrencies; - currencyList = Object.keys(fiatCurrencies).sort(); + currencies = Object.entries(fiatCurrencies).sort((a: any, b: any) => { + if (a[1].name < b[1].name) { + return -1; + } + if (a[1].name > b[1].name) { + return 1; + } + return 0; + }); constructor( private formBuilder: UntypedFormBuilder, diff --git a/frontend/src/app/components/language-selector/language-selector.component.html b/frontend/src/app/components/language-selector/language-selector.component.html index b23b2b7b7..22839441c 100644 --- a/frontend/src/app/components/language-selector/language-selector.component.html +++ b/frontend/src/app/components/language-selector/language-selector.component.html @@ -1,5 +1,5 @@
    -
    From 27a962287573cc08cb96b3889874e72f9fcbd765 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Wed, 15 Feb 2023 01:27:43 -0500 Subject: [PATCH 41/67] Switch audit faq conditions to env.audit From OFFICIAL_MEMPOOL_SPACE. --- frontend/src/app/docs/api-docs/api-docs-data.ts | 4 ++-- frontend/src/app/docs/api-docs/api-docs-nav.component.html | 2 +- frontend/src/app/docs/api-docs/api-docs-nav.component.ts | 4 ++-- frontend/src/app/docs/api-docs/api-docs.component.html | 2 +- frontend/src/app/docs/api-docs/api-docs.component.ts | 2 ++ 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts index 5cc5ca3aa..a6e8e418f 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -8671,7 +8671,7 @@ export const faqData = [ type: "endpoint", category: "advanced", showConditions: bitcoinNetworks, - options: { officialOnly: true }, + options: { auditOnly: true }, fragment: "how-do-block-audits-work", title: "How do block audits work?", }, @@ -8679,7 +8679,7 @@ export const faqData = [ type: "endpoint", category: "advanced", showConditions: bitcoinNetworks, - options: { officialOnly: true }, + options: { auditOnly: true }, fragment: "what-is-block-health", title: "What is block health?", }, diff --git a/frontend/src/app/docs/api-docs/api-docs-nav.component.html b/frontend/src/app/docs/api-docs/api-docs-nav.component.html index 3abdc91be..0353d5fb0 100644 --- a/frontend/src/app/docs/api-docs/api-docs-nav.component.html +++ b/frontend/src/app/docs/api-docs/api-docs-nav.component.html @@ -1,4 +1,4 @@
    diff --git a/frontend/src/app/docs/api-docs/api-docs-nav.component.ts b/frontend/src/app/docs/api-docs/api-docs-nav.component.ts index ad9d0b9a5..439f20339 100644 --- a/frontend/src/app/docs/api-docs/api-docs-nav.component.ts +++ b/frontend/src/app/docs/api-docs/api-docs-nav.component.ts @@ -15,7 +15,7 @@ export class ApiDocsNavComponent implements OnInit { @Output() navLinkClickEvent: EventEmitter = new EventEmitter(); env: Env; tabData: any[]; - officialMempoolInstance: boolean; + auditEnabled: boolean; constructor( private stateService: StateService @@ -23,7 +23,7 @@ export class ApiDocsNavComponent implements OnInit { ngOnInit(): void { this.env = this.stateService.env; - this.officialMempoolInstance = this.env.OFFICIAL_MEMPOOL_SPACE; + this.auditEnabled = this.env.AUDIT; if (this.whichTab === 'rest') { this.tabData = restApiDocsData; } else if (this.whichTab === 'faq') { diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html index c984c2b77..e9a905fab 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -15,7 +15,7 @@
    -
    +

    {{ item.title }}

    {{ item.title }}{{ item.category }}
    diff --git a/frontend/src/app/docs/api-docs/api-docs.component.ts b/frontend/src/app/docs/api-docs/api-docs.component.ts index 0bffd5b2a..1d5eec453 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.ts +++ b/frontend/src/app/docs/api-docs/api-docs.component.ts @@ -28,6 +28,7 @@ export class ApiDocsComponent implements OnInit, AfterViewInit { wsDocs: any; screenWidth: number; officialMempoolInstance: boolean; + auditEnabled: boolean; @ViewChildren(FaqTemplateDirective) faqTemplates: QueryList; dict = {}; @@ -60,6 +61,7 @@ export class ApiDocsComponent implements OnInit, AfterViewInit { ngOnInit(): void { this.env = this.stateService.env; this.officialMempoolInstance = this.env.OFFICIAL_MEMPOOL_SPACE; + this.auditEnabled = this.env.AUDIT; this.network$ = merge(of(''), this.stateService.networkChanged$).pipe( tap((network: string) => { if (this.env.BASE_MODULE === 'mempool' && network !== '') { From 6f650e936d1f3a9b3edf8a5cfd85c74558ff7723 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Wed, 15 Feb 2023 01:49:11 -0500 Subject: [PATCH 42/67] Shrink og avators to make group more square --- frontend/src/app/components/about/about.component.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/about/about.component.scss b/frontend/src/app/components/about/about.component.scss index 910ff922b..42ecded1c 100644 --- a/frontend/src/app/components/about/about.component.scss +++ b/frontend/src/app/components/about/about.component.scss @@ -68,8 +68,8 @@ .community-sponsor { img { - width: 67px; - height: 67px; + width: 57px; + height: 57px; } } From 32aa7aaff11e4bfa25e2959738e0180f1cccd8f8 Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Wed, 15 Feb 2023 16:05:14 +0900 Subject: [PATCH 43/67] Remove bisq price fetch and replace it with our in house price index --- backend/src/api/blocks.ts | 3 +- backend/src/api/fiat-conversion.ts | 123 ------------------- backend/src/api/websocket-handler.ts | 4 +- backend/src/index.ts | 8 +- backend/src/repositories/PricesRepository.ts | 32 +++-- backend/src/tasks/price-updater.ts | 59 +++++---- 6 files changed, 64 insertions(+), 165 deletions(-) delete mode 100644 backend/src/api/fiat-conversion.ts diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 83de897ca..902f1f13b 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -17,7 +17,6 @@ import { prepareBlock } from '../utils/blocks-utils'; import BlocksRepository from '../repositories/BlocksRepository'; import HashratesRepository from '../repositories/HashratesRepository'; import indexer from '../indexer'; -import fiatConversion from './fiat-conversion'; import poolsParser from './pools-parser'; import BlocksSummariesRepository from '../repositories/BlocksSummariesRepository'; import BlocksAuditsRepository from '../repositories/BlocksAuditsRepository'; @@ -170,7 +169,7 @@ class Blocks { blockExtended.extras.reward = transactions[0].vout.reduce((acc, curr) => acc + curr.value, 0); blockExtended.extras.coinbaseTx = transactionUtils.stripCoinbaseTransaction(transactions[0]); blockExtended.extras.coinbaseRaw = blockExtended.extras.coinbaseTx.vin[0].scriptsig; - blockExtended.extras.usd = fiatConversion.getConversionRates().USD; + blockExtended.extras.usd = priceUpdater.latestPrices.USD; if (block.height === 0) { blockExtended.extras.medianFee = 0; // 50th percentiles diff --git a/backend/src/api/fiat-conversion.ts b/backend/src/api/fiat-conversion.ts deleted file mode 100644 index ffbe6a758..000000000 --- a/backend/src/api/fiat-conversion.ts +++ /dev/null @@ -1,123 +0,0 @@ -import logger from '../logger'; -import * as http from 'http'; -import * as https from 'https'; -import axios, { AxiosResponse } from 'axios'; -import { IConversionRates } from '../mempool.interfaces'; -import config from '../config'; -import backendInfo from './backend-info'; -import { SocksProxyAgent } from 'socks-proxy-agent'; - -class FiatConversion { - private debasingFiatCurrencies = ['AED', 'AUD', 'BDT', 'BHD', 'BMD', 'BRL', 'CAD', 'CHF', 'CLP', - 'CNY', 'CZK', 'DKK', 'EUR', 'GBP', 'HKD', 'HUF', 'IDR', 'ILS', 'INR', 'JPY', 'KRW', 'KWD', - 'LKR', 'MMK', 'MXN', 'MYR', 'NGN', 'NOK', 'NZD', 'PHP', 'PKR', 'PLN', 'RUB', 'SAR', 'SEK', - 'SGD', 'THB', 'TRY', 'TWD', 'UAH', 'USD', 'VND', 'ZAR']; - private conversionRates: IConversionRates = {}; - private ratesChangedCallback: ((rates: IConversionRates) => void) | undefined; - public ratesInitialized = false; // If true, it means rates are ready for use - - constructor() { - for (const fiat of this.debasingFiatCurrencies) { - this.conversionRates[fiat] = 0; - } - } - - public setProgressChangedCallback(fn: (rates: IConversionRates) => void) { - this.ratesChangedCallback = fn; - } - - public startService() { - const fiatConversionUrl = (config.SOCKS5PROXY.ENABLED === true) && (config.SOCKS5PROXY.USE_ONION === true) ? config.PRICE_DATA_SERVER.TOR_URL : config.PRICE_DATA_SERVER.CLEARNET_URL; - logger.info('Starting currency rates service'); - if (config.SOCKS5PROXY.ENABLED) { - logger.info(`Currency rates service will be queried over the Tor network using ${fiatConversionUrl}`); - } else { - logger.info(`Currency rates service will be queried over clearnet using ${config.PRICE_DATA_SERVER.CLEARNET_URL}`); - } - setInterval(this.updateCurrency.bind(this), 1000 * config.MEMPOOL.PRICE_FEED_UPDATE_INTERVAL); - this.updateCurrency(); - } - - public getConversionRates() { - return this.conversionRates; - } - - private async updateCurrency(): Promise { - type axiosOptions = { - headers: { - 'User-Agent': string - }; - timeout: number; - httpAgent?: http.Agent; - httpsAgent?: https.Agent; - } - const setDelay = (secs: number = 1): Promise => new Promise(resolve => setTimeout(() => resolve(), secs * 1000)); - const fiatConversionUrl = (config.SOCKS5PROXY.ENABLED === true) && (config.SOCKS5PROXY.USE_ONION === true) ? config.PRICE_DATA_SERVER.TOR_URL : config.PRICE_DATA_SERVER.CLEARNET_URL; - const isHTTP = (new URL(fiatConversionUrl).protocol.split(':')[0] === 'http') ? true : false; - const axiosOptions: axiosOptions = { - headers: { - 'User-Agent': (config.MEMPOOL.USER_AGENT === 'mempool') ? `mempool/v${backendInfo.getBackendInfo().version}` : `${config.MEMPOOL.USER_AGENT}` - }, - timeout: config.SOCKS5PROXY.ENABLED ? 30000 : 10000 - }; - - let retry = 0; - - while(retry < config.MEMPOOL.EXTERNAL_MAX_RETRY) { - try { - if (config.SOCKS5PROXY.ENABLED) { - let socksOptions: any = { - agentOptions: { - keepAlive: true, - }, - hostname: config.SOCKS5PROXY.HOST, - port: config.SOCKS5PROXY.PORT - }; - - if (config.SOCKS5PROXY.USERNAME && config.SOCKS5PROXY.PASSWORD) { - socksOptions.username = config.SOCKS5PROXY.USERNAME; - socksOptions.password = config.SOCKS5PROXY.PASSWORD; - } else { - // Retry with different tor circuits https://stackoverflow.com/a/64960234 - socksOptions.username = `circuit${retry}`; - } - - // Handle proxy agent for onion addresses - if (isHTTP) { - axiosOptions.httpAgent = new SocksProxyAgent(socksOptions); - } else { - axiosOptions.httpsAgent = new SocksProxyAgent(socksOptions); - } - } - - logger.debug('Querying currency rates service...'); - - const response: AxiosResponse = await axios.get(`${fiatConversionUrl}`, axiosOptions); - - if (response.statusText === 'error' || !response.data) { - throw new Error(`Could not fetch data from ${fiatConversionUrl}, Error: ${response.status}`); - } - - for (const rate of response.data.data) { - if (this.debasingFiatCurrencies.includes(rate.currencyCode) && rate.provider === 'Bisq-Aggregate') { - this.conversionRates[rate.currencyCode] = Math.round(100 * rate.price) / 100; - } - } - - this.ratesInitialized = true; - logger.debug(`USD Conversion Rate: ${this.conversionRates.USD}`); - - if (this.ratesChangedCallback) { - this.ratesChangedCallback(this.conversionRates); - } - break; - } catch (e) { - logger.err('Error updating fiat conversion rates: ' + (e instanceof Error ? e.message : e)); - await setDelay(config.MEMPOOL.EXTERNAL_RETRY_INTERVAL); - retry++; - } - } - } -} - -export default new FiatConversion(); diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index 599c068a6..3b6c62513 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -8,7 +8,6 @@ import blocks from './blocks'; import memPool from './mempool'; import backendInfo from './backend-info'; import mempoolBlocks from './mempool-blocks'; -import fiatConversion from './fiat-conversion'; import { Common } from './common'; import loadingIndicators from './loading-indicators'; import config from '../config'; @@ -20,6 +19,7 @@ import BlocksAuditsRepository from '../repositories/BlocksAuditsRepository'; import BlocksSummariesRepository from '../repositories/BlocksSummariesRepository'; import Audit from './audit'; import { deepClone } from '../utils/clone'; +import priceUpdater from '../tasks/price-updater'; class WebsocketHandler { private wss: WebSocket.Server | undefined; @@ -214,7 +214,7 @@ class WebsocketHandler { 'mempoolInfo': memPool.getMempoolInfo(), 'vBytesPerSecond': memPool.getVBytesPerSecond(), 'blocks': _blocks, - 'conversions': fiatConversion.getConversionRates(), + 'conversions': priceUpdater.latestPrices, 'mempool-blocks': mempoolBlocks.getMempoolBlocks(), 'transactions': memPool.getLatestTransactions(), 'backendInfo': backendInfo.getBackendInfo(), diff --git a/backend/src/index.ts b/backend/src/index.ts index 8371e927f..7c0d7ee48 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -10,7 +10,6 @@ import memPool from './api/mempool'; import diskCache from './api/disk-cache'; import statistics from './api/statistics/statistics'; import websocketHandler from './api/websocket-handler'; -import fiatConversion from './api/fiat-conversion'; import bisq from './api/bisq/bisq'; import bisqMarkets from './api/bisq/markets'; import logger from './logger'; @@ -36,6 +35,7 @@ import liquidRoutes from './api/liquid/liquid.routes'; import bitcoinRoutes from './api/bitcoin/bitcoin.routes'; import fundingTxFetcher from './tasks/lightning/sync-tasks/funding-tx-fetcher'; import forensicsService from './tasks/lightning/forensics.service'; +import priceUpdater from './tasks/price-updater'; class Server { private wss: WebSocket.Server | undefined; @@ -87,6 +87,8 @@ class Server { .use(express.text({ type: ['text/plain', 'application/base64'] })) ; + await priceUpdater.$initializeLatestPriceWithDb(); + this.server = http.createServer(this.app); this.wss = new WebSocket.Server({ server: this.server }); @@ -127,7 +129,7 @@ class Server { } } - fiatConversion.startService(); + priceUpdater.$run(); this.setUpHttpApiRoutes(); @@ -221,7 +223,7 @@ class Server { memPool.setAsyncMempoolChangedCallback(websocketHandler.handleMempoolChange.bind(websocketHandler)); blocks.setNewAsyncBlockCallback(websocketHandler.handleNewBlock.bind(websocketHandler)); } - fiatConversion.setProgressChangedCallback(websocketHandler.handleNewConversionRates.bind(websocketHandler)); + priceUpdater.setRatesChangedCallback(websocketHandler.handleNewConversionRates.bind(websocketHandler)); loadingIndicators.setProgressChangedCallback(websocketHandler.handleLoadingChanged.bind(websocketHandler)); } diff --git a/backend/src/repositories/PricesRepository.ts b/backend/src/repositories/PricesRepository.ts index cc79ff2a6..bc606e68b 100644 --- a/backend/src/repositories/PricesRepository.ts +++ b/backend/src/repositories/PricesRepository.ts @@ -1,12 +1,13 @@ import DB from '../database'; import logger from '../logger'; -import { Prices } from '../tasks/price-updater'; +import { IConversionRates } from '../mempool.interfaces'; +import priceUpdater from '../tasks/price-updater'; class PricesRepository { - public async $savePrices(time: number, prices: Prices): Promise { - if (prices.USD === -1) { - // Some historical price entries have not USD prices, so we just ignore them to avoid future UX issues - // As of today there are only 4 (on 2013-09-05, 2013-09-19, 2013-09-12 and 2013-09-26) so that's fine + public async $savePrices(time: number, prices: IConversionRates): Promise { + if (prices.USD === 0) { + // Some historical price entries have no USD prices, so we just ignore them to avoid future UX issues + // As of today there are only 4 (on 2013-09-05, 2013-0909, 2013-09-12 and 2013-09-26) so that's fine return; } @@ -23,22 +24,22 @@ class PricesRepository { } public async $getOldestPriceTime(): Promise { - const [oldestRow] = await DB.query(`SELECT UNIX_TIMESTAMP(time) as time from prices WHERE USD != -1 ORDER BY time LIMIT 1`); + const [oldestRow] = await DB.query(`SELECT UNIX_TIMESTAMP(time) as time from prices WHERE USD != 0 ORDER BY time LIMIT 1`); return oldestRow[0] ? oldestRow[0].time : 0; } public async $getLatestPriceId(): Promise { - const [oldestRow] = await DB.query(`SELECT id from prices WHERE USD != -1 ORDER BY time DESC LIMIT 1`); + const [oldestRow] = await DB.query(`SELECT id from prices WHERE USD != 0 ORDER BY time DESC LIMIT 1`); return oldestRow[0] ? oldestRow[0].id : null; } public async $getLatestPriceTime(): Promise { - const [oldestRow] = await DB.query(`SELECT UNIX_TIMESTAMP(time) as time from prices WHERE USD != -1 ORDER BY time DESC LIMIT 1`); + const [oldestRow] = await DB.query(`SELECT UNIX_TIMESTAMP(time) as time from prices WHERE USD != 0 ORDER BY time DESC LIMIT 1`); return oldestRow[0] ? oldestRow[0].time : 0; } public async $getPricesTimes(): Promise { - const [times]: any[] = await DB.query(`SELECT UNIX_TIMESTAMP(time) as time from prices WHERE USD != -1 ORDER BY time`); + const [times]: any[] = await DB.query(`SELECT UNIX_TIMESTAMP(time) as time from prices WHERE USD != 0 ORDER BY time`); return times.map(time => time.time); } @@ -46,6 +47,19 @@ class PricesRepository { const [times]: any[] = await DB.query(`SELECT UNIX_TIMESTAMP(time) as time, id, USD from prices ORDER BY time`); return times; } + + public async $getLatestConversionRates(): Promise { + const [rates]: any[] = await DB.query(` + SELECT USD, EUR, GBP, CAD, CHF, AUD, JPY + FROM prices + ORDER BY time DESC + LIMIT 1` + ); + if (!rates || rates.length === 0) { + return priceUpdater.getEmptyPricesObj(); + } + return rates[0]; + } } export default new PricesRepository(); diff --git a/backend/src/tasks/price-updater.ts b/backend/src/tasks/price-updater.ts index 9e7e5910a..e42c5887a 100644 --- a/backend/src/tasks/price-updater.ts +++ b/backend/src/tasks/price-updater.ts @@ -1,7 +1,8 @@ import * as fs from 'fs'; -import path from "path"; +import path from 'path'; import config from '../config'; import logger from '../logger'; +import { IConversionRates } from '../mempool.interfaces'; import PricesRepository from '../repositories/PricesRepository'; import BitfinexApi from './price-feeds/bitfinex-api'; import BitflyerApi from './price-feeds/bitflyer-api'; @@ -20,17 +21,7 @@ export interface PriceFeed { } export interface PriceHistory { - [timestamp: number]: Prices; -} - -export interface Prices { - USD: number; - EUR: number; - GBP: number; - CAD: number; - CHF: number; - AUD: number; - JPY: number; + [timestamp: number]: IConversionRates; } class PriceUpdater { @@ -40,7 +31,8 @@ class PriceUpdater { running = false; feeds: PriceFeed[] = []; currencies: string[] = ['USD', 'EUR', 'GBP', 'CAD', 'CHF', 'AUD', 'JPY']; - latestPrices: Prices; + latestPrices: IConversionRates; + private ratesChangedCallback: ((rates: IConversionRates) => void) | undefined; constructor() { this.latestPrices = this.getEmptyPricesObj(); @@ -52,18 +44,30 @@ class PriceUpdater { this.feeds.push(new GeminiApi()); } - public getEmptyPricesObj(): Prices { + public getEmptyPricesObj(): IConversionRates { return { - USD: -1, - EUR: -1, - GBP: -1, - CAD: -1, - CHF: -1, - AUD: -1, - JPY: -1, + USD: 0, + EUR: 0, + GBP: 0, + CAD: 0, + CHF: 0, + AUD: 0, + JPY: 0, }; } + public setRatesChangedCallback(fn: (rates: IConversionRates) => void) { + this.ratesChangedCallback = fn; + } + + /** + * We execute this function before the websocket initialization since + * the websocket init is not done asyncronously + */ + public async $initializeLatestPriceWithDb(): Promise { + this.latestPrices = await PricesRepository.$getLatestConversionRates(); + } + public async $run(): Promise { if (this.running === true) { return; @@ -76,10 +80,9 @@ class PriceUpdater { } try { + await this.$updatePrice(); if (this.historyInserted === false && config.DATABASE.ENABLED === true) { await this.$insertHistoricalPrices(); - } else { - await this.$updatePrice(); } } catch (e) { logger.err(`Cannot save BTC prices in db. Reason: ${e instanceof Error ? e.message : e}`, logger.tags.mining); @@ -144,6 +147,10 @@ class PriceUpdater { } } + if (this.ratesChangedCallback) { + this.ratesChangedCallback(this.latestPrices); + } + this.lastRun = new Date().getTime() / 1000; } @@ -213,7 +220,7 @@ class PriceUpdater { // Group them by timestamp and currency, for example // grouped[123456789]['USD'] = [1, 2, 3, 4]; - const grouped: Object = {}; + const grouped: any = {}; for (const historicalEntry of historicalPrices) { for (const time in historicalEntry) { if (existingPriceTimes.includes(parseInt(time, 10))) { @@ -229,7 +236,7 @@ class PriceUpdater { for (const currency of this.currencies) { const price = historicalEntry[time][currency]; if (price > 0) { - grouped[time][currency].push(parseInt(price, 10)); + grouped[time][currency].push(typeof price === 'string' ? parseInt(price, 10) : price); } } } @@ -238,7 +245,7 @@ class PriceUpdater { // Average prices and insert everything into the db let totalInserted = 0; for (const time in grouped) { - const prices: Prices = this.getEmptyPricesObj(); + const prices: IConversionRates = this.getEmptyPricesObj(); for (const currency in grouped[time]) { if (grouped[time][currency].length === 0) { continue; From 6c0dc34dd6249c1df69fc2b39e3e6c519122ec76 Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Wed, 15 Feb 2023 16:13:10 +0900 Subject: [PATCH 44/67] Run database migration before running any business logic --- backend/src/index.ts | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index 7c0d7ee48..cad675e27 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -78,6 +78,24 @@ class Server { async startServer(worker = false): Promise { logger.notice(`Starting Mempool Server${worker ? ' (worker)' : ''}... (${backendInfo.getShortCommitHash()})`); + if (config.DATABASE.ENABLED) { + await DB.checkDbConnection(); + try { + if (process.env.npm_config_reindex !== undefined) { // Re-index requests + const tables = process.env.npm_config_reindex.split(','); + logger.warn(`Indexed data for "${process.env.npm_config_reindex}" tables will be erased in 5 seconds (using '--reindex')`); + await Common.sleep$(5000); + await databaseMigration.$truncateIndexedData(tables); + } + await databaseMigration.$initializeOrMigrateDatabase(); + if (Common.indexingEnabled()) { + await indexer.$resetHashratesIndexingState(); + } + } catch (e) { + throw new Error(e instanceof Error ? e.message : 'Error'); + } + } + this.app .use((req: Request, res: Response, next: NextFunction) => { res.setHeader('Access-Control-Allow-Origin', '*'); @@ -99,24 +117,6 @@ class Server { diskCache.loadMempoolCache(); } - if (config.DATABASE.ENABLED) { - await DB.checkDbConnection(); - try { - if (process.env.npm_config_reindex !== undefined) { // Re-index requests - const tables = process.env.npm_config_reindex.split(','); - logger.warn(`Indexed data for "${process.env.npm_config_reindex}" tables will be erased in 5 seconds (using '--reindex')`); - await Common.sleep$(5000); - await databaseMigration.$truncateIndexedData(tables); - } - await databaseMigration.$initializeOrMigrateDatabase(); - if (Common.indexingEnabled()) { - await indexer.$resetHashratesIndexingState(); - } - } catch (e) { - throw new Error(e instanceof Error ? e.message : 'Error'); - } - } - if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED && cluster.isPrimary) { statistics.startStatistics(); } From 28bd813fb8322e9b765d54203c01069fbcda0b0c Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Wed, 15 Feb 2023 16:22:06 +0900 Subject: [PATCH 45/67] Show correct currency label in 'Latest transactions' widget --- .../app/dashboard/dashboard.component.html | 2 +- .../src/app/dashboard/dashboard.component.ts | 21 ++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 1df7baabc..f3d96e187 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -122,7 +122,7 @@ TXID Amount - USD + {{ currency }} Fee diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index 45b402314..1eb305220 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -1,5 +1,5 @@ -import { ChangeDetectionStrategy, Component, Inject, LOCALE_ID, OnInit } from '@angular/core'; -import { combineLatest, merge, Observable, of } from 'rxjs'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; +import { combineLatest, merge, Observable, of, Subscription } from 'rxjs'; import { filter, map, scan, share, switchMap, tap } from 'rxjs/operators'; import { BlockExtended, OptimizedMempoolStats } from '../interfaces/node-api.interface'; import { MempoolInfo, TransactionStripped } from '../interfaces/websocket.interface'; @@ -7,7 +7,6 @@ import { ApiService } from '../services/api.service'; import { StateService } from '../services/state.service'; import { WebsocketService } from '../services/websocket.service'; import { SeoService } from '../services/seo.service'; -import { StorageService } from '../services/storage.service'; interface MempoolBlocksData { blocks: number; @@ -32,7 +31,7 @@ interface MempoolStatsData { styleUrls: ['./dashboard.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class DashboardComponent implements OnInit { +export class DashboardComponent implements OnInit, OnDestroy { featuredAssets$: Observable; network$: Observable; mempoolBlocksData$: Observable; @@ -47,16 +46,20 @@ export class DashboardComponent implements OnInit { transactionsWeightPerSecondOptions: any; isLoadingWebSocket$: Observable; liquidPegsMonth$: Observable; + currencySubscription: Subscription; + currency: string; constructor( - @Inject(LOCALE_ID) private locale: string, public stateService: StateService, private apiService: ApiService, private websocketService: WebsocketService, - private seoService: SeoService, - private storageService: StorageService, + private seoService: SeoService ) { } + ngOnDestroy(): void { + this.currencySubscription.unsubscribe(); + } + ngOnInit(): void { this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$; this.seoService.resetTitle(); @@ -213,6 +216,10 @@ export class DashboardComponent implements OnInit { share(), ); } + + this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => { + this.currency = fiat; + }); } handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) { From 23636313263798c86537a993ca8ffb10edd3123e Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 12 Feb 2023 21:43:12 -0600 Subject: [PATCH 46/67] Add audit / block health config feature flag --- backend/mempool-config.sample.json | 1 + .../__fixtures__/mempool-config.template.json | 1 + backend/src/__tests__/config.test.ts | 1 + backend/src/api/blocks.ts | 8 +- backend/src/api/websocket-handler.ts | 80 ++++++++++--------- backend/src/config.ts | 2 + docker/backend/mempool-config.json | 1 + docker/backend/start.sh | 2 + docker/frontend/entrypoint.sh | 2 + frontend/mempool-frontend-config.sample.json | 1 + .../app/components/block/block.component.ts | 26 +++--- .../blocks-list/blocks-list.component.html | 6 +- .../blocks-list/blocks-list.component.ts | 4 +- .../components/graphs/graphs.component.html | 2 +- frontend/src/app/services/state.service.ts | 2 + production/mempool-config.mainnet.json | 1 + production/mempool-config.signet.json | 1 + production/mempool-config.testnet.json | 1 + .../mempool-frontend-config.mainnet.json | 3 +- 19 files changed, 88 insertions(+), 57 deletions(-) diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index 1f64214ce..f8417f0e7 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -25,6 +25,7 @@ "AUTOMATIC_BLOCK_REINDEXING": false, "POOLS_JSON_URL": "https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json", "POOLS_JSON_TREE_URL": "https://api.github.com/repos/mempool/mining-pools/git/trees/master", + "AUDIT": false, "ADVANCED_GBT_AUDIT": false, "ADVANCED_GBT_MEMPOOL": false, "CPFP_INDEXING": false diff --git a/backend/src/__fixtures__/mempool-config.template.json b/backend/src/__fixtures__/mempool-config.template.json index e699c9458..f15d9c328 100644 --- a/backend/src/__fixtures__/mempool-config.template.json +++ b/backend/src/__fixtures__/mempool-config.template.json @@ -26,6 +26,7 @@ "INDEXING_BLOCKS_AMOUNT": 14, "POOLS_JSON_TREE_URL": "__POOLS_JSON_TREE_URL__", "POOLS_JSON_URL": "__POOLS_JSON_URL__", + "AUDIT": "__MEMPOOL_AUDIT__", "ADVANCED_GBT_AUDIT": "__MEMPOOL_ADVANCED_GBT_AUDIT__", "ADVANCED_GBT_MEMPOOL": "__MEMPOOL_ADVANCED_GBT_MEMPOOL__", "CPFP_INDEXING": "__MEMPOOL_CPFP_INDEXING__" diff --git a/backend/src/__tests__/config.test.ts b/backend/src/__tests__/config.test.ts index 4158d3df1..88083f479 100644 --- a/backend/src/__tests__/config.test.ts +++ b/backend/src/__tests__/config.test.ts @@ -38,6 +38,7 @@ describe('Mempool Backend Config', () => { STDOUT_LOG_MIN_PRIORITY: 'debug', POOLS_JSON_TREE_URL: 'https://api.github.com/repos/mempool/mining-pools/git/trees/master', POOLS_JSON_URL: 'https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json', + AUDIT: false, ADVANCED_GBT_AUDIT: false, ADVANCED_GBT_MEMPOOL: false, CPFP_INDEXING: false, diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 902f1f13b..84c919e5d 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -211,9 +211,11 @@ class Blocks { }; } - const auditScore = await BlocksAuditsRepository.$getBlockAuditScore(block.id); - if (auditScore != null) { - blockExtended.extras.matchRate = auditScore.matchRate; + if (config.MEMPOOL.AUDIT) { + const auditScore = await BlocksAuditsRepository.$getBlockAuditScore(block.id); + if (auditScore != null) { + blockExtended.extras.matchRate = auditScore.matchRate; + } } } diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index 3b6c62513..c1c3b3995 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -419,49 +419,51 @@ class WebsocketHandler { const _memPool = memPool.getMempool(); - let projectedBlocks; - // template calculation functions have mempool side effects, so calculate audits using - // a cloned copy of the mempool if we're running a different algorithm for mempool updates - const auditMempool = (config.MEMPOOL.ADVANCED_GBT_AUDIT === config.MEMPOOL.ADVANCED_GBT_MEMPOOL) ? _memPool : deepClone(_memPool); - if (config.MEMPOOL.ADVANCED_GBT_AUDIT) { - projectedBlocks = await mempoolBlocks.makeBlockTemplates(auditMempool, false); - } else { - projectedBlocks = mempoolBlocks.updateMempoolBlocks(auditMempool, false); - } + if (config.MEMPOOL.AUDIT) { + let projectedBlocks; + // template calculation functions have mempool side effects, so calculate audits using + // a cloned copy of the mempool if we're running a different algorithm for mempool updates + const auditMempool = (config.MEMPOOL.ADVANCED_GBT_AUDIT === config.MEMPOOL.ADVANCED_GBT_MEMPOOL) ? _memPool : deepClone(_memPool); + if (config.MEMPOOL.ADVANCED_GBT_AUDIT) { + projectedBlocks = await mempoolBlocks.makeBlockTemplates(auditMempool, false); + } else { + projectedBlocks = mempoolBlocks.updateMempoolBlocks(auditMempool, false); + } - if (Common.indexingEnabled() && memPool.isInSync()) { - const { censored, added, fresh, score } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); - const matchRate = Math.round(score * 100 * 100) / 100; + if (Common.indexingEnabled() && memPool.isInSync()) { + const { censored, added, fresh, score } = Audit.auditBlock(transactions, projectedBlocks, auditMempool); + const matchRate = Math.round(score * 100 * 100) / 100; - const stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions.map((tx) => { - return { - txid: tx.txid, - vsize: tx.vsize, - fee: tx.fee ? Math.round(tx.fee) : 0, - value: tx.value, - }; - }) : []; + const stripped = projectedBlocks[0]?.transactions ? projectedBlocks[0].transactions.map((tx) => { + return { + txid: tx.txid, + vsize: tx.vsize, + fee: tx.fee ? Math.round(tx.fee) : 0, + value: tx.value, + }; + }) : []; - BlocksSummariesRepository.$saveTemplate({ - height: block.height, - template: { - id: block.id, - transactions: stripped + BlocksSummariesRepository.$saveTemplate({ + height: block.height, + template: { + id: block.id, + transactions: stripped + } + }); + + BlocksAuditsRepository.$saveAudit({ + time: block.timestamp, + height: block.height, + hash: block.id, + addedTxs: added, + missingTxs: censored, + freshTxs: fresh, + matchRate: matchRate, + }); + + if (block.extras) { + block.extras.matchRate = matchRate; } - }); - - BlocksAuditsRepository.$saveAudit({ - time: block.timestamp, - height: block.height, - hash: block.id, - addedTxs: added, - missingTxs: censored, - freshTxs: fresh, - matchRate: matchRate, - }); - - if (block.extras) { - block.extras.matchRate = matchRate; } } diff --git a/backend/src/config.ts b/backend/src/config.ts index fb06c84fb..a5736996f 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -29,6 +29,7 @@ interface IConfig { AUTOMATIC_BLOCK_REINDEXING: boolean; POOLS_JSON_URL: string, POOLS_JSON_TREE_URL: string, + AUDIT: boolean; ADVANCED_GBT_AUDIT: boolean; ADVANCED_GBT_MEMPOOL: boolean; CPFP_INDEXING: boolean; @@ -150,6 +151,7 @@ const defaults: IConfig = { 'AUTOMATIC_BLOCK_REINDEXING': false, 'POOLS_JSON_URL': 'https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json', 'POOLS_JSON_TREE_URL': 'https://api.github.com/repos/mempool/mining-pools/git/trees/master', + 'AUDIT': false, 'ADVANCED_GBT_AUDIT': false, 'ADVANCED_GBT_MEMPOOL': false, 'CPFP_INDEXING': false, diff --git a/docker/backend/mempool-config.json b/docker/backend/mempool-config.json index 2e3826f1d..17901acc4 100644 --- a/docker/backend/mempool-config.json +++ b/docker/backend/mempool-config.json @@ -23,6 +23,7 @@ "INDEXING_BLOCKS_AMOUNT": __MEMPOOL_INDEXING_BLOCKS_AMOUNT__, "BLOCKS_SUMMARIES_INDEXING": __MEMPOOL_BLOCKS_SUMMARIES_INDEXING__, "AUTOMATIC_BLOCK_REINDEXING": __MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__, + "AUDIT": __MEMPOOL_AUDIT__, "ADVANCED_GBT_AUDIT": __MEMPOOL_ADVANCED_GBT_AUDIT__, "ADVANCED_GBT_MEMPOOL": __MEMPOOL_ADVANCED_GBT_MEMPOOL__, "CPFP_INDEXING": __MEMPOOL_CPFP_INDEXING__ diff --git a/docker/backend/start.sh b/docker/backend/start.sh index 5f33df107..e02706bce 100755 --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -27,6 +27,7 @@ __MEMPOOL_INDEXING_BLOCKS_AMOUNT__=${MEMPOOL_INDEXING_BLOCKS_AMOUNT:=false} __MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__=${MEMPOOL_AUTOMATIC_BLOCK_REINDEXING:=false} __MEMPOOL_POOLS_JSON_URL__=${MEMPOOL_POOLS_JSON_URL:=https://raw.githubusercontent.com/mempool/mining-pools/master/pools.json} __MEMPOOL_POOLS_JSON_TREE_URL__=${MEMPOOL_POOLS_JSON_TREE_URL:=https://api.github.com/repos/mempool/mining-pools/git/trees/master} +__MEMPOOL_AUDIT__=${MEMPOOL_AUDIT:=false} __MEMPOOL_ADVANCED_GBT_AUDIT__=${MEMPOOL_ADVANCED_GBT_AUDIT:=false} __MEMPOOL_ADVANCED_GBT_MEMPOOL__=${MEMPOOL_ADVANCED_GBT_MEMPOOL:=false} __MEMPOOL_CPFP_INDEXING__=${MEMPOOL_CPFP_INDEXING:=false} @@ -139,6 +140,7 @@ sed -i "s/__MEMPOOL_INDEXING_BLOCKS_AMOUNT__/${__MEMPOOL_INDEXING_BLOCKS_AMOUNT_ sed -i "s/__MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__/${__MEMPOOL_AUTOMATIC_BLOCK_REINDEXING__}/g" mempool-config.json sed -i "s!__MEMPOOL_POOLS_JSON_URL__!${__MEMPOOL_POOLS_JSON_URL__}!g" mempool-config.json sed -i "s!__MEMPOOL_POOLS_JSON_TREE_URL__!${__MEMPOOL_POOLS_JSON_TREE_URL__}!g" mempool-config.json +sed -i "s!__MEMPOOL_AUDIT__!${__MEMPOOL_AUDIT__}!g" mempool-config.json sed -i "s!__MEMPOOL_ADVANCED_GBT_MEMPOOL__!${__MEMPOOL_ADVANCED_GBT_MEMPOOL__}!g" mempool-config.json sed -i "s!__MEMPOOL_ADVANCED_GBT_AUDIT__!${__MEMPOOL_ADVANCED_GBT_AUDIT__}!g" mempool-config.json sed -i "s!__MEMPOOL_CPFP_INDEXING__!${__MEMPOOL_CPFP_INDEXING__}!g" mempool-config.json diff --git a/docker/frontend/entrypoint.sh b/docker/frontend/entrypoint.sh index 3e2210360..18cb782e9 100644 --- a/docker/frontend/entrypoint.sh +++ b/docker/frontend/entrypoint.sh @@ -31,6 +31,7 @@ __LIQUID_WEBSITE_URL__=${LIQUID_WEBSITE_URL:=https://liquid.network} __BISQ_WEBSITE_URL__=${BISQ_WEBSITE_URL:=https://bisq.markets} __MINING_DASHBOARD__=${MINING_DASHBOARD:=true} __LIGHTNING__=${LIGHTNING:=false} +__AUDIT__=${AUDIT:=false} __MAINNET_BLOCK_AUDIT_START_HEIGHT__=${MAINNET_BLOCK_AUDIT_START_HEIGHT:=0} __TESTNET_BLOCK_AUDIT_START_HEIGHT__=${TESTNET_BLOCK_AUDIT_START_HEIGHT:=0} __SIGNET_BLOCK_AUDIT_START_HEIGHT__=${SIGNET_BLOCK_AUDIT_START_HEIGHT:=0} @@ -55,6 +56,7 @@ export __LIQUID_WEBSITE_URL__ export __BISQ_WEBSITE_URL__ export __MINING_DASHBOARD__ export __LIGHTNING__ +export __AUDIT__ export __MAINNET_BLOCK_AUDIT_START_HEIGHT__ export __TESTNET_BLOCK_AUDIT_START_HEIGHT__ export __SIGNET_BLOCK_AUDIT_START_HEIGHT__ diff --git a/frontend/mempool-frontend-config.sample.json b/frontend/mempool-frontend-config.sample.json index 5c0f92acf..9035315a4 100644 --- a/frontend/mempool-frontend-config.sample.json +++ b/frontend/mempool-frontend-config.sample.json @@ -17,6 +17,7 @@ "LIQUID_WEBSITE_URL": "https://liquid.network", "BISQ_WEBSITE_URL": "https://bisq.markets", "MINING_DASHBOARD": true, + "AUDIT": false, "MAINNET_BLOCK_AUDIT_START_HEIGHT": 0, "TESTNET_BLOCK_AUDIT_START_HEIGHT": 0, "SIGNET_BLOCK_AUDIT_START_HEIGHT": 0, diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index f1ebfed6a..668d0af48 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -58,7 +58,7 @@ export class BlockComponent implements OnInit, OnDestroy { overviewError: any = null; webGlEnabled = true; indexingAvailable = false; - auditModeEnabled: boolean = !this.stateService.hideAudit.value; + auditModeEnabled: boolean = this.stateService.env.AUDIT && !this.stateService.hideAudit.value; auditAvailable = true; showAudit: boolean; isMobile = window.innerWidth <= 767.98; @@ -110,12 +110,15 @@ export class BlockComponent implements OnInit, OnDestroy { }); this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true); - this.setAuditAvailable(this.indexingAvailable); - this.auditPrefSubscription = this.stateService.hideAudit.subscribe((hide) => { - this.auditModeEnabled = !hide; - this.showAudit = this.auditAvailable && this.auditModeEnabled; - }); + this.setAuditAvailable(this.stateService.env.AUDIT && this.indexingAvailable); + + if (this.stateService.env.AUDIT) { + this.auditPrefSubscription = this.stateService.hideAudit.subscribe((hide) => { + this.auditModeEnabled = !hide; + this.showAudit = this.auditAvailable && this.auditModeEnabled; + }); + } this.txsLoadingStatus$ = this.route.paramMap .pipe( @@ -221,7 +224,9 @@ export class BlockComponent implements OnInit, OnDestroy { setTimeout(() => { this.nextBlockSubscription = this.apiService.getBlock$(block.previousblockhash).subscribe(); this.nextBlockTxListSubscription = this.electrsApiService.getBlockTransactions$(block.previousblockhash).subscribe(); - this.apiService.getBlockAudit$(block.previousblockhash); + if (this.stateService.env.AUDIT) { + this.apiService.getBlockAudit$(block.previousblockhash); + } }, 100); } this.updateAuditAvailableFromBlockHeight(block.height); @@ -269,7 +274,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.isLoadingOverview = false; }); - if (!this.indexingAvailable) { + if (!this.indexingAvailable || !this.stateService.env.AUDIT) { this.overviewSubscription = block$.pipe( startWith(null), pairwise(), @@ -300,7 +305,7 @@ export class BlockComponent implements OnInit, OnDestroy { }); } - if (this.indexingAvailable) { + if (this.indexingAvailable && this.stateService.env.AUDIT) { this.auditSubscription = block$.pipe( startWith(null), pairwise(), @@ -613,6 +618,9 @@ export class BlockComponent implements OnInit, OnDestroy { } updateAuditAvailableFromBlockHeight(blockHeight: number): void { + if (!this.stateService.env.AUDIT) { + this.setAuditAvailable(false); + } switch (this.stateService.network) { case 'testnet': if (blockHeight < this.stateService.env.TESTNET_BLOCK_AUDIT_START_HEIGHT) { diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.html b/frontend/src/app/components/blocks-list/blocks-list.component.html index 424ea2ec4..46da3fa91 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -14,7 +14,7 @@ i18n-ngbTooltip="mining.pool-name" ngbTooltip="Pool" placement="bottom" #miningpool [disableTooltip]="!isEllipsisActive(miningpool)">Pool Timestamp Mined - Health Reward @@ -45,7 +45,7 @@ - + - + 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 93f7814cf..1cc428be3 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -22,6 +22,7 @@ export class BlocksList implements OnInit, OnDestroy { latestScoreSubscription: Subscription; indexingAvailable = false; + auditAvailable = false; isLoading = true; loadingScores = true; fromBlockHeight = undefined; @@ -44,6 +45,7 @@ export class BlocksList implements OnInit, OnDestroy { ngOnInit(): void { this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true); + this.auditAvailable = this.stateService.env.AUDIT; if (!this.widget) { this.websocketService.want(['blocks']); @@ -111,7 +113,7 @@ export class BlocksList implements OnInit, OnDestroy { }, []) ); - if (this.indexingAvailable) { + if (this.indexingAvailable && this.auditAvailable) { this.auditScoreSubscription = this.fromHeightSubject.pipe( switchMap((fromBlockHeight) => { this.loadingScores = true; diff --git a/frontend/src/app/components/graphs/graphs.component.html b/frontend/src/app/components/graphs/graphs.component.html index dd47a4ac7..af5136a38 100644 --- a/frontend/src/app/components/graphs/graphs.component.html +++ b/frontend/src/app/components/graphs/graphs.component.html @@ -22,7 +22,7 @@ i18n="mining.block-rewards">Block Rewards Block Sizes and Weights - Block Prediction Accuracy
    diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 02b160fe9..33de7823d 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -39,6 +39,7 @@ export interface Env { BISQ_WEBSITE_URL: string; MINING_DASHBOARD: boolean; LIGHTNING: boolean; + AUDIT: boolean; MAINNET_BLOCK_AUDIT_START_HEIGHT: number; TESTNET_BLOCK_AUDIT_START_HEIGHT: number; SIGNET_BLOCK_AUDIT_START_HEIGHT: number; @@ -67,6 +68,7 @@ const defaultEnv: Env = { 'BISQ_WEBSITE_URL': 'https://bisq.markets', 'MINING_DASHBOARD': true, 'LIGHTNING': false, + 'AUDIT': false, 'MAINNET_BLOCK_AUDIT_START_HEIGHT': 0, 'TESTNET_BLOCK_AUDIT_START_HEIGHT': 0, 'SIGNET_BLOCK_AUDIT_START_HEIGHT': 0, diff --git a/production/mempool-config.mainnet.json b/production/mempool-config.mainnet.json index ab2fa69c1..1258e62fb 100644 --- a/production/mempool-config.mainnet.json +++ b/production/mempool-config.mainnet.json @@ -10,6 +10,7 @@ "POLL_RATE_MS": 1000, "INDEXING_BLOCKS_AMOUNT": -1, "BLOCKS_SUMMARIES_INDEXING": true, + "AUDIT": true, "ADVANCED_GBT_AUDIT": true, "ADVANCED_GBT_MEMPOOL": false, "USE_SECOND_NODE_FOR_MINFEE": true diff --git a/production/mempool-config.signet.json b/production/mempool-config.signet.json index 313a09679..3c661c39f 100644 --- a/production/mempool-config.signet.json +++ b/production/mempool-config.signet.json @@ -7,6 +7,7 @@ "SPAWN_CLUSTER_PROCS": 0, "API_URL_PREFIX": "/api/v1/", "INDEXING_BLOCKS_AMOUNT": -1, + "AUDIT": true, "ADVANCED_GBT_AUDIT": true, "ADVANCED_GBT_MEMPOOL": false, "POLL_RATE_MS": 1000 diff --git a/production/mempool-config.testnet.json b/production/mempool-config.testnet.json index 908df7886..352529c6e 100644 --- a/production/mempool-config.testnet.json +++ b/production/mempool-config.testnet.json @@ -7,6 +7,7 @@ "SPAWN_CLUSTER_PROCS": 0, "API_URL_PREFIX": "/api/v1/", "INDEXING_BLOCKS_AMOUNT": -1, + "AUDIT": true, "ADVANCED_GBT_AUDIT": true, "ADVANCED_GBT_MEMPOOL": false, "POLL_RATE_MS": 1000 diff --git a/production/mempool-frontend-config.mainnet.json b/production/mempool-frontend-config.mainnet.json index e612c0440..5eb3d0a7d 100644 --- a/production/mempool-frontend-config.mainnet.json +++ b/production/mempool-frontend-config.mainnet.json @@ -10,5 +10,6 @@ "LIQUID_WEBSITE_URL": "https://liquid.network", "BISQ_WEBSITE_URL": "https://bisq.markets", "ITEMS_PER_PAGE": 25, - "LIGHTNING": true + "LIGHTNING": true, + "AUDIT": true } From c923a4bc2265bfce6cb04faa40df6600f18ba51e Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 14 Feb 2023 12:23:26 -0600 Subject: [PATCH 47/67] simplify audit availability logic --- .../app/components/block/block.component.ts | 20 +++++++++---------- .../blocks-list/blocks-list.component.html | 6 +++--- .../blocks-list/blocks-list.component.ts | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index 668d0af48..ba5dd8cf7 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -57,8 +57,8 @@ export class BlockComponent implements OnInit, OnDestroy { transactionsError: any = null; overviewError: any = null; webGlEnabled = true; - indexingAvailable = false; - auditModeEnabled: boolean = this.stateService.env.AUDIT && !this.stateService.hideAudit.value; + auditSupported: boolean = this.stateService.env.AUDIT && this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true; + auditModeEnabled: boolean = !this.stateService.hideAudit.value; auditAvailable = true; showAudit: boolean; isMobile = window.innerWidth <= 767.98; @@ -109,11 +109,9 @@ export class BlockComponent implements OnInit, OnDestroy { this.timeLtr = !!ltr; }); - this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true); + this.setAuditAvailable(this.auditSupported); - this.setAuditAvailable(this.stateService.env.AUDIT && this.indexingAvailable); - - if (this.stateService.env.AUDIT) { + if (this.auditSupported) { this.auditPrefSubscription = this.stateService.hideAudit.subscribe((hide) => { this.auditModeEnabled = !hide; this.showAudit = this.auditAvailable && this.auditModeEnabled; @@ -224,7 +222,7 @@ export class BlockComponent implements OnInit, OnDestroy { setTimeout(() => { this.nextBlockSubscription = this.apiService.getBlock$(block.previousblockhash).subscribe(); this.nextBlockTxListSubscription = this.electrsApiService.getBlockTransactions$(block.previousblockhash).subscribe(); - if (this.stateService.env.AUDIT) { + if (this.auditSupported) { this.apiService.getBlockAudit$(block.previousblockhash); } }, 100); @@ -274,7 +272,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.isLoadingOverview = false; }); - if (!this.indexingAvailable || !this.stateService.env.AUDIT) { + if (!this.auditSupported) { this.overviewSubscription = block$.pipe( startWith(null), pairwise(), @@ -305,7 +303,7 @@ export class BlockComponent implements OnInit, OnDestroy { }); } - if (this.indexingAvailable && this.stateService.env.AUDIT) { + if (this.auditSupported) { this.auditSubscription = block$.pipe( startWith(null), pairwise(), @@ -610,7 +608,7 @@ export class BlockComponent implements OnInit, OnDestroy { setAuditAvailable(available: boolean): void { this.auditAvailable = available; - this.showAudit = this.auditAvailable && this.auditModeEnabled; + this.showAudit = this.auditAvailable && this.auditModeEnabled && this.auditSupported; } toggleAuditMode(): void { @@ -618,7 +616,7 @@ export class BlockComponent implements OnInit, OnDestroy { } updateAuditAvailableFromBlockHeight(blockHeight: number): void { - if (!this.stateService.env.AUDIT) { + if (!this.auditSupported) { this.setAuditAvailable(false); } switch (this.stateService.network) { diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.html b/frontend/src/app/components/blocks-list/blocks-list.component.html index 46da3fa91..ffb5f5f88 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -14,7 +14,7 @@ i18n-ngbTooltip="mining.pool-name" ngbTooltip="Pool" placement="bottom" #miningpool [disableTooltip]="!isEllipsisActive(miningpool)">Pool Timestamp Mined - Health Reward @@ -45,7 +45,7 @@ - + - + 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 1cc428be3..160e0c882 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.ts +++ b/frontend/src/app/components/blocks-list/blocks-list.component.ts @@ -45,7 +45,7 @@ export class BlocksList implements OnInit, OnDestroy { ngOnInit(): void { this.indexingAvailable = (this.stateService.env.BASE_MODULE === 'mempool' && this.stateService.env.MINING_DASHBOARD === true); - this.auditAvailable = this.stateService.env.AUDIT; + this.auditAvailable = this.indexingAvailable && this.stateService.env.AUDIT; if (!this.widget) { this.websocketService.want(['blocks']); From 215e92d33e79276c38c2390652ec8ff3e143aee6 Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Wed, 15 Feb 2023 01:27:43 -0500 Subject: [PATCH 48/67] Switch audit faq conditions to env.audit From OFFICIAL_MEMPOOL_SPACE. --- frontend/src/app/docs/api-docs/api-docs-data.ts | 4 ++-- frontend/src/app/docs/api-docs/api-docs-nav.component.html | 2 +- frontend/src/app/docs/api-docs/api-docs-nav.component.ts | 4 ++-- frontend/src/app/docs/api-docs/api-docs.component.html | 2 +- frontend/src/app/docs/api-docs/api-docs.component.ts | 2 ++ 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/docs/api-docs/api-docs-data.ts b/frontend/src/app/docs/api-docs/api-docs-data.ts index 5cc5ca3aa..a6e8e418f 100644 --- a/frontend/src/app/docs/api-docs/api-docs-data.ts +++ b/frontend/src/app/docs/api-docs/api-docs-data.ts @@ -8671,7 +8671,7 @@ export const faqData = [ type: "endpoint", category: "advanced", showConditions: bitcoinNetworks, - options: { officialOnly: true }, + options: { auditOnly: true }, fragment: "how-do-block-audits-work", title: "How do block audits work?", }, @@ -8679,7 +8679,7 @@ export const faqData = [ type: "endpoint", category: "advanced", showConditions: bitcoinNetworks, - options: { officialOnly: true }, + options: { auditOnly: true }, fragment: "what-is-block-health", title: "What is block health?", }, diff --git a/frontend/src/app/docs/api-docs/api-docs-nav.component.html b/frontend/src/app/docs/api-docs/api-docs-nav.component.html index 3abdc91be..0353d5fb0 100644 --- a/frontend/src/app/docs/api-docs/api-docs-nav.component.html +++ b/frontend/src/app/docs/api-docs/api-docs-nav.component.html @@ -1,4 +1,4 @@ diff --git a/frontend/src/app/docs/api-docs/api-docs-nav.component.ts b/frontend/src/app/docs/api-docs/api-docs-nav.component.ts index ad9d0b9a5..439f20339 100644 --- a/frontend/src/app/docs/api-docs/api-docs-nav.component.ts +++ b/frontend/src/app/docs/api-docs/api-docs-nav.component.ts @@ -15,7 +15,7 @@ export class ApiDocsNavComponent implements OnInit { @Output() navLinkClickEvent: EventEmitter = new EventEmitter(); env: Env; tabData: any[]; - officialMempoolInstance: boolean; + auditEnabled: boolean; constructor( private stateService: StateService @@ -23,7 +23,7 @@ export class ApiDocsNavComponent implements OnInit { ngOnInit(): void { this.env = this.stateService.env; - this.officialMempoolInstance = this.env.OFFICIAL_MEMPOOL_SPACE; + this.auditEnabled = this.env.AUDIT; if (this.whichTab === 'rest') { this.tabData = restApiDocsData; } else if (this.whichTab === 'faq') { diff --git a/frontend/src/app/docs/api-docs/api-docs.component.html b/frontend/src/app/docs/api-docs/api-docs.component.html index c984c2b77..e9a905fab 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -15,7 +15,7 @@
    -
    +

    {{ item.title }}

    {{ item.title }}{{ item.category }}
    diff --git a/frontend/src/app/docs/api-docs/api-docs.component.ts b/frontend/src/app/docs/api-docs/api-docs.component.ts index 0bffd5b2a..1d5eec453 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.ts +++ b/frontend/src/app/docs/api-docs/api-docs.component.ts @@ -28,6 +28,7 @@ export class ApiDocsComponent implements OnInit, AfterViewInit { wsDocs: any; screenWidth: number; officialMempoolInstance: boolean; + auditEnabled: boolean; @ViewChildren(FaqTemplateDirective) faqTemplates: QueryList; dict = {}; @@ -60,6 +61,7 @@ export class ApiDocsComponent implements OnInit, AfterViewInit { ngOnInit(): void { this.env = this.stateService.env; this.officialMempoolInstance = this.env.OFFICIAL_MEMPOOL_SPACE; + this.auditEnabled = this.env.AUDIT; this.network$ = merge(of(''), this.stateService.networkChanged$).pipe( tap((network: string) => { if (this.env.BASE_MODULE === 'mempool' && network !== '') { From 774f7630ce9cb29327c9aac192b53aed0b38586d Mon Sep 17 00:00:00 2001 From: hunicus <93150691+hunicus@users.noreply.github.com> Date: Wed, 15 Feb 2023 00:56:02 -0500 Subject: [PATCH 49/67] Remove colon from block overview table --- frontend/src/app/components/block/block.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/block/block.component.html b/frontend/src/app/components/block/block.component.html index 7e30d4bbf..0fda470d6 100644 --- a/frontend/src/app/components/block/block.component.html +++ b/frontend/src/app/components/block/block.component.html @@ -143,7 +143,7 @@ - Subsidy + fees: + Subsidy + fees @@ -158,7 +158,7 @@ - Subsidy + fees: + Subsidy + fees From 3beb85dd4a217008ea1c8bcc3d640708a6b918d8 Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Wed, 15 Feb 2023 16:41:09 +0900 Subject: [PATCH 50/67] Add footer to all dashboards --- .../mining-dashboard.component.html | 20 +++++++++++++++++++ .../mining-dashboard.component.scss | 19 ++++++++++++++++++ .../lightning-dashboard.component.html | 18 +++++++++++++++-- .../lightning-dashboard.component.scss | 19 ++++++++++++++++++ 4 files changed, 74 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html index 36aec8906..1a932567c 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html @@ -74,4 +74,24 @@
    + +
    +
    + +
    +
    + +
    +
    + + + +
    +
    diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss index 303591974..218b8e04d 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss @@ -103,4 +103,23 @@ margin-bottom: 10px; text-decoration: none; color: inherit; +} + +.terms-of-service { + margin-top: 1rem; +} + +.pref-selectors { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + + .selector { + margin-left: .5em; + margin-bottom: .5em; + &:first { + margin-left: 0; + } + } } \ No newline at end of file diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html index a371f474d..030019b3c 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html @@ -84,8 +84,22 @@
    -
    - Connect to our nodes +
    +
    + +
    +
    + +
    +
    + +
    diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss index 303591974..218b8e04d 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss @@ -103,4 +103,23 @@ margin-bottom: 10px; text-decoration: none; color: inherit; +} + +.terms-of-service { + margin-top: 1rem; +} + +.pref-selectors { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + + .selector { + margin-left: .5em; + margin-bottom: .5em; + &:first { + margin-left: 0; + } + } } \ No newline at end of file From 5e10b75b87d43da71031718da9708f5ce0e2ef9c Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Wed, 15 Feb 2023 17:33:30 +0900 Subject: [PATCH 51/67] Fix lightning dashboard btc/fiat UI issue --- .../channels-statistics/channels-statistics.component.html | 4 ++-- .../lightning/node-statistics/node-statistics.component.html | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/lightning/channels-statistics/channels-statistics.component.html b/frontend/src/app/lightning/channels-statistics/channels-statistics.component.html index bedcc0ded..44e19b8f6 100644 --- a/frontend/src/app/lightning/channels-statistics/channels-statistics.component.html +++ b/frontend/src/app/lightning/channels-statistics/channels-statistics.component.html @@ -17,7 +17,7 @@ sats
    - +
    @@ -63,7 +63,7 @@ sats
    - +
    diff --git a/frontend/src/app/lightning/node-statistics/node-statistics.component.html b/frontend/src/app/lightning/node-statistics/node-statistics.component.html index 74c14c8b0..97df1f546 100644 --- a/frontend/src/app/lightning/node-statistics/node-statistics.component.html +++ b/frontend/src/app/lightning/node-statistics/node-statistics.component.html @@ -5,11 +5,10 @@
    - +
    - - +
    From 9734052477dff2e59c04c43b93576a3afb946443 Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Wed, 15 Feb 2023 17:45:29 +0900 Subject: [PATCH 52/67] Fix database used when database disabled --- backend/src/api/pools-parser.ts | 2 +- backend/src/index.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/api/pools-parser.ts b/backend/src/api/pools-parser.ts index c3cc994a2..e37414bbe 100644 --- a/backend/src/api/pools-parser.ts +++ b/backend/src/api/pools-parser.ts @@ -127,7 +127,7 @@ class PoolsParser { if (!equals(JSON.parse(existingPool.addresses), poolObj.addresses) || !equals(JSON.parse(existingPool.regexes), poolObj.regexes)) { finalPoolDataUpdate.push(poolObj); } - } else { + } else if (config.DATABASE.ENABLED) { // Double check that if we're not just renaming a pool (same address same regex) const [poolToRename]: any[] = await DB.query(` SELECT * FROM pools diff --git a/backend/src/index.ts b/backend/src/index.ts index cad675e27..a81275066 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -105,7 +105,9 @@ class Server { .use(express.text({ type: ['text/plain', 'application/base64'] })) ; - await priceUpdater.$initializeLatestPriceWithDb(); + if (config.DATABASE.ENABLED) { + await priceUpdater.$initializeLatestPriceWithDb(); + } this.server = http.createServer(this.app); this.wss = new WebSocket.Server({ server: this.server }); From 408b0a23ce992814672d2e127446108c25ee02a9 Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Wed, 15 Feb 2023 18:06:22 +0900 Subject: [PATCH 53/67] Disable fiat display on pools blocks and ln channel component --- frontend/src/app/components/pool/pool.component.html | 4 ++-- .../lightning/channel/channel-box/channel-box.component.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/pool/pool.component.html b/frontend/src/app/components/pool/pool.component.html index ef907215c..53982ca86 100644 --- a/frontend/src/app/components/pool/pool.component.html +++ b/frontend/src/app/components/pool/pool.component.html @@ -235,10 +235,10 @@ - + - + {{ block.tx_count | number }} diff --git a/frontend/src/app/lightning/channel/channel-box/channel-box.component.html b/frontend/src/app/lightning/channel/channel-box/channel-box.component.html index 9fe9b100b..effe6b891 100644 --- a/frontend/src/app/lightning/channel/channel-box/channel-box.component.html +++ b/frontend/src/app/lightning/channel/channel-box/channel-box.component.html @@ -7,7 +7,7 @@
    -
    +
    From cc5873f9956933e349e4426d5ce5b4f8f3830905 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 15 Feb 2023 09:26:12 -0600 Subject: [PATCH 54/67] fix liquid address table overflow --- .../components/address/address.component.html | 4 ++-- .../components/address/address.component.scss | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/address/address.component.html b/frontend/src/app/components/address/address.component.html index d3b23315a..ae2d7ba9c 100644 --- a/frontend/src/app/components/address/address.component.html +++ b/frontend/src/app/components/address/address.component.html @@ -1,4 +1,4 @@ -
    +

    Address