From 3db1486bfb1ac7a684c6d48691591e4adbe7591e Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Sun, 26 Mar 2023 14:35:10 +0900 Subject: [PATCH 01/79] Fix transaction amount overflow --- .../transactions-list.component.html | 4 ++-- .../transactions-list.component.scss | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html index cb54e1870..1549f7871 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -77,7 +77,7 @@ - +
@@ -206,7 +206,7 @@ - +
diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.scss b/frontend/src/app/components/transactions-list/transactions-list.component.scss index 08d7d7486..5d6dd7d61 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.scss +++ b/frontend/src/app/components/transactions-list/transactions-list.component.scss @@ -46,7 +46,16 @@ } td.amount { - width: 32.5%; + width: 36%; + @media (max-width: 576px) { + width: 50%; + } +} +td.amount.large { + width: 45%; + @media (max-width: 576px) { + width: 60%; + } } .extra-info { From e9386ec003682ff921737709d234f4832822b0c8 Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Sun, 26 Mar 2023 16:39:45 +0200 Subject: [PATCH 02/79] Add Bitcoin Core RPC cookie authentication option --- backend/mempool-config.sample.json | 8 +++++-- .../__fixtures__/mempool-config.template.json | 10 ++++++--- backend/src/__tests__/config.test.ts | 8 +++++-- .../bitcoin/bitcoin-api-abstract-factory.ts | 1 + backend/src/api/bitcoin/bitcoin-client.ts | 3 +++ .../src/api/bitcoin/bitcoin-second-client.ts | 3 +++ backend/src/config.ts | 8 +++++++ backend/src/rpc-api/jsonrpc.ts | 21 ++++++++++++++++--- docker/README.md | 16 ++++++++++---- docker/backend/mempool-config.json | 8 +++++-- docker/backend/start.sh | 8 +++++++ 11 files changed, 78 insertions(+), 16 deletions(-) diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index 32becd00d..68b5c3801 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -35,7 +35,9 @@ "PORT": 8332, "USERNAME": "mempool", "PASSWORD": "mempool", - "TIMEOUT": 60000 + "TIMEOUT": 60000, + "COOKIE": false, + "COOKIE_PATH": "/path/to/bitcoin/.cookie" }, "ELECTRUM": { "HOST": "127.0.0.1", @@ -52,7 +54,9 @@ "PORT": 8332, "USERNAME": "mempool", "PASSWORD": "mempool", - "TIMEOUT": 60000 + "TIMEOUT": 60000, + "COOKIE": false, + "COOKIE_PATH": "/path/to/bitcoin/.cookie" }, "DATABASE": { "ENABLED": true, diff --git a/backend/src/__fixtures__/mempool-config.template.json b/backend/src/__fixtures__/mempool-config.template.json index 919784464..1d5c7135a 100644 --- a/backend/src/__fixtures__/mempool-config.template.json +++ b/backend/src/__fixtures__/mempool-config.template.json @@ -36,7 +36,9 @@ "PORT": 15, "USERNAME": "__CORE_RPC_USERNAME__", "PASSWORD": "__CORE_RPC_PASSWORD__", - "TIMEOUT": 1000 + "TIMEOUT": 1000, + "COOKIE": "__CORE_RPC_COOKIE__", + "COOKIE_PATH": "__CORE_RPC_COOKIE_PATH__" }, "ELECTRUM": { "HOST": "__ELECTRUM_HOST__", @@ -53,7 +55,9 @@ "PORT": 17, "USERNAME": "__SECOND_CORE_RPC_USERNAME__", "PASSWORD": "__SECOND_CORE_RPC_PASSWORD__", - "TIMEOUT": 2000 + "TIMEOUT": 2000, + "COOKIE": "__SECOND_CORE_RPC_COOKIE__", + "COOKIE_PATH": "__SECOND_CORE_RPC_COOKIE_PATH__" }, "DATABASE": { "ENABLED": false, @@ -119,4 +123,4 @@ "CLIGHTNING": { "SOCKET": "__CLIGHTNING_SOCKET__" } -} \ No newline at end of file +} diff --git a/backend/src/__tests__/config.test.ts b/backend/src/__tests__/config.test.ts index 278d83f50..f4cf719c6 100644 --- a/backend/src/__tests__/config.test.ts +++ b/backend/src/__tests__/config.test.ts @@ -54,7 +54,9 @@ describe('Mempool Backend Config', () => { PORT: 8332, USERNAME: 'mempool', PASSWORD: 'mempool', - TIMEOUT: 60000 + TIMEOUT: 60000, + COOKIE: false, + COOKIE_PATH: '' }); expect(config.SECOND_CORE_RPC).toStrictEqual({ @@ -62,7 +64,9 @@ describe('Mempool Backend Config', () => { PORT: 8332, USERNAME: 'mempool', PASSWORD: 'mempool', - TIMEOUT: 60000 + TIMEOUT: 60000, + COOKIE: false, + COOKIE_PATH: '' }); expect(config.DATABASE).toStrictEqual({ diff --git a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts index 7b2802d1b..f8dfe8c26 100644 --- a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts +++ b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts @@ -25,4 +25,5 @@ export interface BitcoinRpcCredentials { user: string; pass: string; timeout: number; + cookie?: string; } diff --git a/backend/src/api/bitcoin/bitcoin-client.ts b/backend/src/api/bitcoin/bitcoin-client.ts index 429638984..f0dab4441 100644 --- a/backend/src/api/bitcoin/bitcoin-client.ts +++ b/backend/src/api/bitcoin/bitcoin-client.ts @@ -2,12 +2,15 @@ import config from '../../config'; const bitcoin = require('../../rpc-api/index'); import { BitcoinRpcCredentials } from './bitcoin-api-abstract-factory'; +export const defaultCookiePath = `${process.env.HOME}/.bitcoin/${{mainnet:'',testnet:'testnet3/',signet:'signet/'}[config.MEMPOOL.NETWORK]}.cookie`; + const nodeRpcCredentials: BitcoinRpcCredentials = { host: config.CORE_RPC.HOST, port: config.CORE_RPC.PORT, user: config.CORE_RPC.USERNAME, pass: config.CORE_RPC.PASSWORD, timeout: config.CORE_RPC.TIMEOUT, + cookie: config.CORE_RPC.COOKIE ? config.CORE_RPC.COOKIE_PATH || defaultCookiePath : undefined, }; export default new bitcoin.Client(nodeRpcCredentials); diff --git a/backend/src/api/bitcoin/bitcoin-second-client.ts b/backend/src/api/bitcoin/bitcoin-second-client.ts index 7f81a96a0..85d05556e 100644 --- a/backend/src/api/bitcoin/bitcoin-second-client.ts +++ b/backend/src/api/bitcoin/bitcoin-second-client.ts @@ -2,12 +2,15 @@ import config from '../../config'; const bitcoin = require('../../rpc-api/index'); import { BitcoinRpcCredentials } from './bitcoin-api-abstract-factory'; +import { defaultCookiePath } from './bitcoin-client'; + const nodeRpcCredentials: BitcoinRpcCredentials = { host: config.SECOND_CORE_RPC.HOST, port: config.SECOND_CORE_RPC.PORT, user: config.SECOND_CORE_RPC.USERNAME, pass: config.SECOND_CORE_RPC.PASSWORD, timeout: config.SECOND_CORE_RPC.TIMEOUT, + cookie: config.SECOND_CORE_RPC.COOKIE ? config.SECOND_CORE_RPC.COOKIE_PATH || defaultCookiePath : undefined, }; export default new bitcoin.Client(nodeRpcCredentials); diff --git a/backend/src/config.ts b/backend/src/config.ts index ff5ea4f9f..b9a3f366a 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -70,6 +70,8 @@ interface IConfig { USERNAME: string; PASSWORD: string; TIMEOUT: number; + COOKIE: boolean; + COOKIE_PATH: string; }; SECOND_CORE_RPC: { HOST: string; @@ -77,6 +79,8 @@ interface IConfig { USERNAME: string; PASSWORD: string; TIMEOUT: number; + COOKIE: boolean; + COOKIE_PATH: string; }; DATABASE: { ENABLED: boolean; @@ -180,6 +184,8 @@ const defaults: IConfig = { 'USERNAME': 'mempool', 'PASSWORD': 'mempool', 'TIMEOUT': 60000, + 'COOKIE': false, + 'COOKIE_PATH': '' // default value depends on network, see src/api/bitcoin/bitcoin-client }, 'SECOND_CORE_RPC': { 'HOST': '127.0.0.1', @@ -187,6 +193,8 @@ const defaults: IConfig = { 'USERNAME': 'mempool', 'PASSWORD': 'mempool', 'TIMEOUT': 60000, + 'COOKIE': false, + 'COOKIE_PATH': '' }, 'DATABASE': { 'ENABLED': true, diff --git a/backend/src/rpc-api/jsonrpc.ts b/backend/src/rpc-api/jsonrpc.ts index 4f7a38baa..0bcbdc16c 100644 --- a/backend/src/rpc-api/jsonrpc.ts +++ b/backend/src/rpc-api/jsonrpc.ts @@ -1,5 +1,6 @@ var http = require('http') var https = require('https') +import { readFileSync } from 'fs'; var JsonRPC = function (opts) { // @ts-ignore @@ -55,7 +56,13 @@ JsonRPC.prototype.call = function (method, params) { } // use HTTP auth if user and password set - if (this.opts.user && this.opts.pass) { + if (this.opts.cookie) { + if (!this.cachedCookie) { + this.cachedCookie = readFileSync(this.opts.cookie).toString(); + } + // @ts-ignore + requestOptions.auth = this.cachedCookie; + } else if (this.opts.user && this.opts.pass) { // @ts-ignore requestOptions.auth = this.opts.user + ':' + this.opts.pass } @@ -93,7 +100,7 @@ JsonRPC.prototype.call = function (method, params) { reject(err) }) - request.on('response', function (response) { + request.on('response', (response) => { clearTimeout(reqTimeout) // We need to buffer the response chunks in a nonblocking way. @@ -104,7 +111,7 @@ JsonRPC.prototype.call = function (method, params) { // When all the responses are finished, we decode the JSON and // depending on whether it's got a result or an error, we call // emitSuccess or emitError on the promise. - response.on('end', function () { + response.on('end', () => { var err if (cbCalled) return @@ -113,6 +120,14 @@ JsonRPC.prototype.call = function (method, params) { try { var decoded = JSON.parse(buffer) } catch (e) { + // if we authenticated using a cookie and it failed, read the cookie file again + if ( + response.statusCode === 401 /* Unauthorized */ && + this.opts.cookie + ) { + this.cachedCookie = undefined; + } + if (response.statusCode !== 200) { err = new Error('Invalid params, response status code: ' + response.statusCode) err.code = -32602 diff --git a/docker/README.md b/docker/README.md index b669b37c8..74b9be0d9 100644 --- a/docker/README.md +++ b/docker/README.md @@ -162,7 +162,9 @@ Corresponding `docker-compose.yml` overrides: "PORT": 8332, "USERNAME": "mempool", "PASSWORD": "mempool", - "TIMEOUT": 60000 + "TIMEOUT": 60000, + "COOKIE": "false", + "COOKIE_PATH": "" }, ``` @@ -174,7 +176,9 @@ Corresponding `docker-compose.yml` overrides: CORE_RPC_PORT: "" CORE_RPC_USERNAME: "" CORE_RPC_PASSWORD: "" - CORE_RPC_TIMEOUT: 60000 + CORE_RPC_TIMEOUT: 60000, + CORE_RPC_COOKIE: "" + CORE_RPC_COOKIE_PATH: "" ... ``` @@ -229,7 +233,9 @@ Corresponding `docker-compose.yml` overrides: "PORT": 8332, "USERNAME": "mempool", "PASSWORD": "mempool", - "TIMEOUT": 60000 + "TIMEOUT": 60000, + "COOKIE": "false", + "COOKIE_PATH": "" }, ``` @@ -241,7 +247,9 @@ Corresponding `docker-compose.yml` overrides: SECOND_CORE_RPC_PORT: "" SECOND_CORE_RPC_USERNAME: "" SECOND_CORE_RPC_PASSWORD: "" - SECOND_CORE_RPC_TIMEOUT: "" + SECOND_CORE_RPC_TIMEOUT: "", + SECOND_CORE_RPC_COOKIE: "" + SECOND_CORE_RPC_COOKIE_PATH: "" ... ``` diff --git a/docker/backend/mempool-config.json b/docker/backend/mempool-config.json index 06ed51f41..7a1db21b4 100644 --- a/docker/backend/mempool-config.json +++ b/docker/backend/mempool-config.json @@ -36,7 +36,9 @@ "PORT": __CORE_RPC_PORT__, "USERNAME": "__CORE_RPC_USERNAME__", "PASSWORD": "__CORE_RPC_PASSWORD__", - "TIMEOUT": __CORE_RPC_TIMEOUT__ + "TIMEOUT": __CORE_RPC_TIMEOUT__, + "COOKIE": __CORE_RPC_COOKIE__, + "COOKIE_PATH": "__CORE_RPC_COOKIE_PATH__" }, "ELECTRUM": { "HOST": "__ELECTRUM_HOST__", @@ -53,7 +55,9 @@ "PORT": __SECOND_CORE_RPC_PORT__, "USERNAME": "__SECOND_CORE_RPC_USERNAME__", "PASSWORD": "__SECOND_CORE_RPC_PASSWORD__", - "TIMEOUT": __SECOND_CORE_RPC_TIMEOUT__ + "TIMEOUT": __SECOND_CORE_RPC_TIMEOUT__, + "COOKIE": __SECOND_CORE_RPC_COOKIE__, + "COOKIE_PATH": "__SECOND_CORE_RPC_COOKIE_PATH__" }, "DATABASE": { "ENABLED": __DATABASE_ENABLED__, diff --git a/docker/backend/start.sh b/docker/backend/start.sh index cb1108a05..0aa8fde5c 100755 --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -38,6 +38,8 @@ __CORE_RPC_PORT__=${CORE_RPC_PORT:=8332} __CORE_RPC_USERNAME__=${CORE_RPC_USERNAME:=mempool} __CORE_RPC_PASSWORD__=${CORE_RPC_PASSWORD:=mempool} __CORE_RPC_TIMEOUT__=${CORE_RPC_TIMEOUT:=60000} +__CORE_RPC_COOKIE__=${CORE_RPC_COOKIE:=false} +__CORE_RPC_COOKIE_PATH__=${CORE_RPC_COOKIE:=""} # ELECTRUM __ELECTRUM_HOST__=${ELECTRUM_HOST:=127.0.0.1} @@ -55,6 +57,8 @@ __SECOND_CORE_RPC_PORT__=${SECOND_CORE_RPC_PORT:=8332} __SECOND_CORE_RPC_USERNAME__=${SECOND_CORE_RPC_USERNAME:=mempool} __SECOND_CORE_RPC_PASSWORD__=${SECOND_CORE_RPC_PASSWORD:=mempool} __SECOND_CORE_RPC_TIMEOUT__=${SECOND_CORE_RPC_TIMEOUT:=60000} +__SECOND_CORE_RPC_COOKIE__=${SECOND_CORE_RPC_COOKIE:=false} +__SECOND_CORE_RPC_COOKIE_PATH__=${SECOND_CORE_RPC_COOKIE:=""} # DATABASE __DATABASE_ENABLED__=${DATABASE_ENABLED:=true} @@ -165,6 +169,8 @@ sed -i "s!__CORE_RPC_PORT__!${__CORE_RPC_PORT__}!g" mempool-config.json sed -i "s!__CORE_RPC_USERNAME__!${__CORE_RPC_USERNAME__}!g" mempool-config.json sed -i "s!__CORE_RPC_PASSWORD__!${__CORE_RPC_PASSWORD__}!g" mempool-config.json sed -i "s!__CORE_RPC_TIMEOUT__!${__CORE_RPC_TIMEOUT__}!g" mempool-config.json +sed -i "s!__CORE_RPC_COOKIE__!${__CORE_RPC_COOKIE__}!g" mempool-config.json +sed -i "s!__CORE_RPC_COOKIE_PATH__!${__CORE_RPC_COOKIE_PATH__}!g" mempool-config.json sed -i "s!__ELECTRUM_HOST__!${__ELECTRUM_HOST__}!g" mempool-config.json sed -i "s!__ELECTRUM_PORT__!${__ELECTRUM_PORT__}!g" mempool-config.json @@ -179,6 +185,8 @@ sed -i "s!__SECOND_CORE_RPC_PORT__!${__SECOND_CORE_RPC_PORT__}!g" mempool-config sed -i "s!__SECOND_CORE_RPC_USERNAME__!${__SECOND_CORE_RPC_USERNAME__}!g" mempool-config.json sed -i "s!__SECOND_CORE_RPC_PASSWORD__!${__SECOND_CORE_RPC_PASSWORD__}!g" mempool-config.json sed -i "s!__SECOND_CORE_RPC_TIMEOUT__!${__SECOND_CORE_RPC_TIMEOUT__}!g" mempool-config.json +sed -i "s!__SECOND_CORE_RPC_COOKIE__!${__SECOND_CORE_RPC_COOKIE__}!g" mempool-config.json +sed -i "s!__SECOND_CORE_RPC_COOKIE_PATH__!${__SECOND_CORE_RPC_COOKIE_PATH__}!g" mempool-config.json sed -i "s!__DATABASE_ENABLED__!${__DATABASE_ENABLED__}!g" mempool-config.json sed -i "s!__DATABASE_HOST__!${__DATABASE_HOST__}!g" mempool-config.json From dc491a598405cfaa641dfbed011e5a5cd3812654 Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Mon, 27 Mar 2023 21:28:45 +0200 Subject: [PATCH 03/79] change default cookie path to /bitcoin/.cookie --- backend/src/__tests__/config.test.ts | 4 ++-- backend/src/api/bitcoin/bitcoin-client.ts | 4 +--- backend/src/api/bitcoin/bitcoin-second-client.ts | 4 +--- backend/src/config.ts | 4 ++-- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/backend/src/__tests__/config.test.ts b/backend/src/__tests__/config.test.ts index f4cf719c6..d1d8222bd 100644 --- a/backend/src/__tests__/config.test.ts +++ b/backend/src/__tests__/config.test.ts @@ -56,7 +56,7 @@ describe('Mempool Backend Config', () => { PASSWORD: 'mempool', TIMEOUT: 60000, COOKIE: false, - COOKIE_PATH: '' + COOKIE_PATH: '/bitcoin/.cookie' }); expect(config.SECOND_CORE_RPC).toStrictEqual({ @@ -66,7 +66,7 @@ describe('Mempool Backend Config', () => { PASSWORD: 'mempool', TIMEOUT: 60000, COOKIE: false, - COOKIE_PATH: '' + COOKIE_PATH: '/bitcoin/.cookie' }); expect(config.DATABASE).toStrictEqual({ diff --git a/backend/src/api/bitcoin/bitcoin-client.ts b/backend/src/api/bitcoin/bitcoin-client.ts index f0dab4441..e8b30a888 100644 --- a/backend/src/api/bitcoin/bitcoin-client.ts +++ b/backend/src/api/bitcoin/bitcoin-client.ts @@ -2,15 +2,13 @@ import config from '../../config'; const bitcoin = require('../../rpc-api/index'); import { BitcoinRpcCredentials } from './bitcoin-api-abstract-factory'; -export const defaultCookiePath = `${process.env.HOME}/.bitcoin/${{mainnet:'',testnet:'testnet3/',signet:'signet/'}[config.MEMPOOL.NETWORK]}.cookie`; - const nodeRpcCredentials: BitcoinRpcCredentials = { host: config.CORE_RPC.HOST, port: config.CORE_RPC.PORT, user: config.CORE_RPC.USERNAME, pass: config.CORE_RPC.PASSWORD, timeout: config.CORE_RPC.TIMEOUT, - cookie: config.CORE_RPC.COOKIE ? config.CORE_RPC.COOKIE_PATH || defaultCookiePath : undefined, + cookie: config.CORE_RPC.COOKIE ? config.CORE_RPC.COOKIE_PATH : undefined, }; export default new bitcoin.Client(nodeRpcCredentials); diff --git a/backend/src/api/bitcoin/bitcoin-second-client.ts b/backend/src/api/bitcoin/bitcoin-second-client.ts index 85d05556e..6ae9cefb0 100644 --- a/backend/src/api/bitcoin/bitcoin-second-client.ts +++ b/backend/src/api/bitcoin/bitcoin-second-client.ts @@ -2,15 +2,13 @@ import config from '../../config'; const bitcoin = require('../../rpc-api/index'); import { BitcoinRpcCredentials } from './bitcoin-api-abstract-factory'; -import { defaultCookiePath } from './bitcoin-client'; - const nodeRpcCredentials: BitcoinRpcCredentials = { host: config.SECOND_CORE_RPC.HOST, port: config.SECOND_CORE_RPC.PORT, user: config.SECOND_CORE_RPC.USERNAME, pass: config.SECOND_CORE_RPC.PASSWORD, timeout: config.SECOND_CORE_RPC.TIMEOUT, - cookie: config.SECOND_CORE_RPC.COOKIE ? config.SECOND_CORE_RPC.COOKIE_PATH || defaultCookiePath : undefined, + cookie: config.SECOND_CORE_RPC.COOKIE ? config.SECOND_CORE_RPC.COOKIE_PATH : undefined, }; export default new bitcoin.Client(nodeRpcCredentials); diff --git a/backend/src/config.ts b/backend/src/config.ts index b9a3f366a..eb1b0af21 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -185,7 +185,7 @@ const defaults: IConfig = { 'PASSWORD': 'mempool', 'TIMEOUT': 60000, 'COOKIE': false, - 'COOKIE_PATH': '' // default value depends on network, see src/api/bitcoin/bitcoin-client + 'COOKIE_PATH': '/bitcoin/.cookie' }, 'SECOND_CORE_RPC': { 'HOST': '127.0.0.1', @@ -194,7 +194,7 @@ const defaults: IConfig = { 'PASSWORD': 'mempool', 'TIMEOUT': 60000, 'COOKIE': false, - 'COOKIE_PATH': '' + 'COOKIE_PATH': '/bitcoin/.cookie' }, 'DATABASE': { 'ENABLED': true, From 0d69ad43ab26c7d2150e4b8460e762b7e06241e0 Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Sun, 18 Jun 2023 20:34:56 +0200 Subject: [PATCH 04/79] fix config issues --- .../src/__fixtures__/mempool-config.template.json | 4 ++-- docker/README.md | 12 ++++++------ docker/backend/mempool-config.json | 2 +- docker/backend/start.sh | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/src/__fixtures__/mempool-config.template.json b/backend/src/__fixtures__/mempool-config.template.json index 1d5c7135a..cdf188b88 100644 --- a/backend/src/__fixtures__/mempool-config.template.json +++ b/backend/src/__fixtures__/mempool-config.template.json @@ -37,7 +37,7 @@ "USERNAME": "__CORE_RPC_USERNAME__", "PASSWORD": "__CORE_RPC_PASSWORD__", "TIMEOUT": 1000, - "COOKIE": "__CORE_RPC_COOKIE__", + "COOKIE": false, "COOKIE_PATH": "__CORE_RPC_COOKIE_PATH__" }, "ELECTRUM": { @@ -56,7 +56,7 @@ "USERNAME": "__SECOND_CORE_RPC_USERNAME__", "PASSWORD": "__SECOND_CORE_RPC_PASSWORD__", "TIMEOUT": 2000, - "COOKIE": "__SECOND_CORE_RPC_COOKIE__", + "COOKIE": false, "COOKIE_PATH": "__SECOND_CORE_RPC_COOKIE_PATH__" }, "DATABASE": { diff --git a/docker/README.md b/docker/README.md index 74b9be0d9..997a330b4 100644 --- a/docker/README.md +++ b/docker/README.md @@ -163,7 +163,7 @@ Corresponding `docker-compose.yml` overrides: "USERNAME": "mempool", "PASSWORD": "mempool", "TIMEOUT": 60000, - "COOKIE": "false", + "COOKIE": false, "COOKIE_PATH": "" }, ``` @@ -176,8 +176,8 @@ Corresponding `docker-compose.yml` overrides: CORE_RPC_PORT: "" CORE_RPC_USERNAME: "" CORE_RPC_PASSWORD: "" - CORE_RPC_TIMEOUT: 60000, - CORE_RPC_COOKIE: "" + CORE_RPC_TIMEOUT: 60000 + CORE_RPC_COOKIE: false CORE_RPC_COOKIE_PATH: "" ... ``` @@ -234,7 +234,7 @@ Corresponding `docker-compose.yml` overrides: "USERNAME": "mempool", "PASSWORD": "mempool", "TIMEOUT": 60000, - "COOKIE": "false", + "COOKIE": false, "COOKIE_PATH": "" }, ``` @@ -247,8 +247,8 @@ Corresponding `docker-compose.yml` overrides: SECOND_CORE_RPC_PORT: "" SECOND_CORE_RPC_USERNAME: "" SECOND_CORE_RPC_PASSWORD: "" - SECOND_CORE_RPC_TIMEOUT: "", - SECOND_CORE_RPC_COOKIE: "" + SECOND_CORE_RPC_TIMEOUT: "" + SECOND_CORE_RPC_COOKIE: false SECOND_CORE_RPC_COOKIE_PATH: "" ... ``` diff --git a/docker/backend/mempool-config.json b/docker/backend/mempool-config.json index 7a1db21b4..a91a52d6b 100644 --- a/docker/backend/mempool-config.json +++ b/docker/backend/mempool-config.json @@ -129,4 +129,4 @@ "GEOLITE2_ASN": "__MAXMIND_GEOLITE2_ASN__", "GEOIP2_ISP": "__MAXMIND_GEOIP2_ISP__" } -} \ No newline at end of file +} diff --git a/docker/backend/start.sh b/docker/backend/start.sh index 0aa8fde5c..1dce5e811 100755 --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -39,7 +39,7 @@ __CORE_RPC_USERNAME__=${CORE_RPC_USERNAME:=mempool} __CORE_RPC_PASSWORD__=${CORE_RPC_PASSWORD:=mempool} __CORE_RPC_TIMEOUT__=${CORE_RPC_TIMEOUT:=60000} __CORE_RPC_COOKIE__=${CORE_RPC_COOKIE:=false} -__CORE_RPC_COOKIE_PATH__=${CORE_RPC_COOKIE:=""} +__CORE_RPC_COOKIE_PATH__=${CORE_RPC_COOKIE_PATH:=""} # ELECTRUM __ELECTRUM_HOST__=${ELECTRUM_HOST:=127.0.0.1} @@ -58,7 +58,7 @@ __SECOND_CORE_RPC_USERNAME__=${SECOND_CORE_RPC_USERNAME:=mempool} __SECOND_CORE_RPC_PASSWORD__=${SECOND_CORE_RPC_PASSWORD:=mempool} __SECOND_CORE_RPC_TIMEOUT__=${SECOND_CORE_RPC_TIMEOUT:=60000} __SECOND_CORE_RPC_COOKIE__=${SECOND_CORE_RPC_COOKIE:=false} -__SECOND_CORE_RPC_COOKIE_PATH__=${SECOND_CORE_RPC_COOKIE:=""} +__SECOND_CORE_RPC_COOKIE_PATH__=${SECOND_CORE_RPC_COOKIE_PATH:=""} # DATABASE __DATABASE_ENABLED__=${DATABASE_ENABLED:=true} From 223f6df3713729eb02dbedd4eefdf16a43c53dc9 Mon Sep 17 00:00:00 2001 From: nymkappa <1612910616@pm.me> Date: Sat, 5 Aug 2023 10:52:11 +0900 Subject: [PATCH 05/79] fix 'large' class trigger --- .../transactions-list/transactions-list.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html index ef34bf822..24d34d6ec 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -81,7 +81,7 @@
- +
@@ -222,7 +222,7 @@ - +
From c1f0eac8f8d3aa7db9a8cc93ed7ee4ee24ff5603 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 14 Sep 2023 22:57:37 +0000 Subject: [PATCH 06/79] Add REQUEST_TIMEOUT and FALLBACK_TIMEOUT esplora config options --- backend/mempool-config.sample.json | 2 ++ backend/src/__fixtures__/mempool-config.template.json | 2 ++ backend/src/__tests__/config.test.ts | 2 ++ backend/src/api/bitcoin/esplora-api.ts | 8 ++++---- backend/src/config.ts | 4 ++++ docker/backend/mempool-config.json | 2 ++ docker/backend/start.sh | 4 ++++ 7 files changed, 20 insertions(+), 4 deletions(-) diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index 00fe95cc5..32ff8330f 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -51,6 +51,8 @@ "REST_API_URL": "http://127.0.0.1:3000", "UNIX_SOCKET_PATH": "/tmp/esplora-bitcoin-mainnet", "RETRY_UNIX_SOCKET_AFTER": 30000, + "REQUEST_TIMEOUT": 10000, + "FALLBACK_TIMEOUT": 5000, "FALLBACK": [] }, "SECOND_CORE_RPC": { diff --git a/backend/src/__fixtures__/mempool-config.template.json b/backend/src/__fixtures__/mempool-config.template.json index 1b6c8d411..060bcad4f 100644 --- a/backend/src/__fixtures__/mempool-config.template.json +++ b/backend/src/__fixtures__/mempool-config.template.json @@ -52,6 +52,8 @@ "REST_API_URL": "__ESPLORA_REST_API_URL__", "UNIX_SOCKET_PATH": "__ESPLORA_UNIX_SOCKET_PATH__", "RETRY_UNIX_SOCKET_AFTER": 888, + "REQUEST_TIMEOUT": 10000, + "FALLBACK_TIMEOUT": 5000, "FALLBACK": [] }, "SECOND_CORE_RPC": { diff --git a/backend/src/__tests__/config.test.ts b/backend/src/__tests__/config.test.ts index 8097a2465..0ca6fa98a 100644 --- a/backend/src/__tests__/config.test.ts +++ b/backend/src/__tests__/config.test.ts @@ -56,6 +56,8 @@ describe('Mempool Backend Config', () => { REST_API_URL: 'http://127.0.0.1:3000', UNIX_SOCKET_PATH: null, RETRY_UNIX_SOCKET_AFTER: 30000, + REQUEST_TIMEOUT: 10000, + FALLBACK_TIMEOUT: 5000, FALLBACK: [], }); diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index d6d4327cb..0f3c6290d 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -75,9 +75,9 @@ class FailoverRouter { const results = await Promise.allSettled(this.hosts.map(async (host) => { if (host.socket) { - return this.pollConnection.get('/blocks/tip/height', { socketPath: host.host, timeout: 5000 }); + return this.pollConnection.get('/blocks/tip/height', { socketPath: host.host, timeout: config.ESPLORA.FALLBACK_TIMEOUT }); } else { - return this.pollConnection.get(host.host + '/blocks/tip/height', { timeout: 5000 }); + return this.pollConnection.get(host.host + '/blocks/tip/height', { timeout: config.ESPLORA.FALLBACK_TIMEOUT }); } })); const maxHeight = results.reduce((max, result) => Math.max(max, result.status === 'fulfilled' ? result.value?.data || 0 : 0), 0); @@ -168,10 +168,10 @@ class FailoverRouter { let axiosConfig; let url; if (host.socket) { - axiosConfig = { socketPath: host.host, timeout: 10000, responseType }; + axiosConfig = { socketPath: host.host, timeout: config.ESPLORA.REQUEST_TIMEOUT, responseType }; url = path; } else { - axiosConfig = { timeout: 10000, responseType }; + axiosConfig = { timeout: config.ESPLORA.REQUEST_TIMEOUT, responseType }; url = host.host + path; } return (method === 'post' diff --git a/backend/src/config.ts b/backend/src/config.ts index ed320d957..275d3314e 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -44,6 +44,8 @@ interface IConfig { REST_API_URL: string; UNIX_SOCKET_PATH: string | void | null; RETRY_UNIX_SOCKET_AFTER: number; + REQUEST_TIMEOUT: number; + FALLBACK_TIMEOUT: number; FALLBACK: string[]; }; LIGHTNING: { @@ -189,6 +191,8 @@ const defaults: IConfig = { 'REST_API_URL': 'http://127.0.0.1:3000', 'UNIX_SOCKET_PATH': null, 'RETRY_UNIX_SOCKET_AFTER': 30000, + 'REQUEST_TIMEOUT': 10000, + 'FALLBACK_TIMEOUT': 5000, 'FALLBACK': [], }, 'ELECTRUM': { diff --git a/docker/backend/mempool-config.json b/docker/backend/mempool-config.json index aa084133f..caa7b045f 100644 --- a/docker/backend/mempool-config.json +++ b/docker/backend/mempool-config.json @@ -52,6 +52,8 @@ "REST_API_URL": "__ESPLORA_REST_API_URL__", "UNIX_SOCKET_PATH": "__ESPLORA_UNIX_SOCKET_PATH__", "RETRY_UNIX_SOCKET_AFTER": __ESPLORA_RETRY_UNIX_SOCKET_AFTER__, + "REQUEST_TIMEOUT": __ESPLORA_REQUEST_TIMEOUT__, + "FALLBACK_TIMEOUT": __ESPLORA_FALLBACK_TIMEOUT__, "FALLBACK": __ESPLORA_FALLBACK__ }, "SECOND_CORE_RPC": { diff --git a/docker/backend/start.sh b/docker/backend/start.sh index 2e293ce34..5b8ae1464 100755 --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -53,6 +53,8 @@ __ELECTRUM_TLS_ENABLED__=${ELECTRUM_TLS_ENABLED:=false} __ESPLORA_REST_API_URL__=${ESPLORA_REST_API_URL:=http://127.0.0.1:3000} __ESPLORA_UNIX_SOCKET_PATH__=${ESPLORA_UNIX_SOCKET_PATH:="null"} __ESPLORA_RETRY_UNIX_SOCKET_AFTER__=${ESPLORA_RETRY_UNIX_SOCKET_AFTER:=30000} +__ESPLORA_REQUEST_TIMEOUT__=${ESPLORA_REQUEST_TIMEOUT:=5000} +__ESPLORA_FALLBACK_TIMEOUT__=${ESPLORA_FALLBACK_TIMEOUT:=5000} __ESPLORA_FALLBACK__=${ESPLORA_FALLBACK:=[]} # SECOND_CORE_RPC @@ -193,6 +195,8 @@ sed -i "s!__ELECTRUM_TLS_ENABLED__!${__ELECTRUM_TLS_ENABLED__}!g" mempool-config sed -i "s!__ESPLORA_REST_API_URL__!${__ESPLORA_REST_API_URL__}!g" mempool-config.json sed -i "s!__ESPLORA_UNIX_SOCKET_PATH__!${__ESPLORA_UNIX_SOCKET_PATH__}!g" mempool-config.json sed -i "s!__ESPLORA_RETRY_UNIX_SOCKET_AFTER__!${__ESPLORA_RETRY_UNIX_SOCKET_AFTER__}!g" mempool-config.json +sed -i "s!__ESPLORA_REQUEST_TIMEOUT__!${__ESPLORA_REQUEST_TIMEOUT__}!g" mempool-config.json +sed -i "s!__ESPLORA_FALLBACK_TIMEOUT__!${__ESPLORA_FALLBACK_TIMEOUT__}!g" mempool-config.json sed -i "s!__ESPLORA_FALLBACK__!${__ESPLORA_FALLBACK__}!g" mempool-config.json sed -i "s!__SECOND_CORE_RPC_HOST__!${__SECOND_CORE_RPC_HOST__}!g" mempool-config.json From 441b505aa3666b6298b8f6792f4ca5a1a4e0cf53 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 11 Sep 2023 11:04:01 +0900 Subject: [PATCH 07/79] Send correct tx conf status in websocket msgs --- backend/src/api/blocks.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 73b010b91..64dc1d5ba 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -81,6 +81,7 @@ class Blocks { private async $getTransactionsExtended( blockHash: string, blockHeight: number, + blockTime: number, onlyCoinbase: boolean, txIds: string[] | null = null, quiet: boolean = false, @@ -101,6 +102,12 @@ class Blocks { if (!onlyCoinbase) { for (const txid of txIds) { if (mempool[txid]) { + mempool[txid].status = { + confirmed: true, + block_height: blockHeight, + block_hash: blockHash, + block_time: blockTime, + }; transactionMap[txid] = mempool[txid]; foundInMempool++; totalFound++; @@ -608,7 +615,7 @@ class Blocks { } const blockHash = await bitcoinApi.$getBlockHash(blockHeight); const block: IEsploraApi.Block = await bitcoinApi.$getBlock(blockHash); - const transactions = await this.$getTransactionsExtended(blockHash, block.height, true, null, true); + const transactions = await this.$getTransactionsExtended(blockHash, block.height, block.timestamp, true, null, true); const blockExtended = await this.$getBlockExtended(block, transactions); newlyIndexed++; @@ -701,7 +708,7 @@ class Blocks { const verboseBlock = await bitcoinClient.getBlock(blockHash, 2); const block = BitcoinApi.convertBlock(verboseBlock); const txIds: string[] = verboseBlock.tx.map(tx => tx.txid); - const transactions = await this.$getTransactionsExtended(blockHash, block.height, false, txIds, false, true) as MempoolTransactionExtended[]; + const transactions = await this.$getTransactionsExtended(blockHash, block.height, block.timestamp, false, txIds, false, true) as MempoolTransactionExtended[]; // fill in missing transaction fee data from verboseBlock for (let i = 0; i < transactions.length; i++) { @@ -890,7 +897,7 @@ class Blocks { const blockHash = await bitcoinApi.$getBlockHash(height); const block: IEsploraApi.Block = await bitcoinApi.$getBlock(blockHash); - const transactions = await this.$getTransactionsExtended(blockHash, block.height, true); + const transactions = await this.$getTransactionsExtended(blockHash, block.height, block.timestamp, true); const blockExtended = await this.$getBlockExtended(block, transactions); if (Common.indexingEnabled()) { @@ -902,7 +909,7 @@ class Blocks { public async $indexStaleBlock(hash: string): Promise { const block: IEsploraApi.Block = await bitcoinApi.$getBlock(hash); - const transactions = await this.$getTransactionsExtended(hash, block.height, true); + const transactions = await this.$getTransactionsExtended(hash, block.height, block.timestamp, true); const blockExtended = await this.$getBlockExtended(block, transactions); blockExtended.canonical = await bitcoinApi.$getBlockHash(block.height); From f3fc774c2d3f5de657e00414ab37a654b20e7605 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 21 Sep 2023 21:57:54 +0100 Subject: [PATCH 08/79] Add standalone block visualization page --- frontend/src/app/app-routing.module.ts | 5 + .../block-view/block-view.component.html | 13 ++ .../block-view/block-view.component.scss | 22 +++ .../block-view/block-view.component.ts | 176 ++++++++++++++++++ frontend/src/app/shared/shared.module.ts | 3 + 5 files changed, 219 insertions(+) create mode 100644 frontend/src/app/components/block-view/block-view.component.html create mode 100644 frontend/src/app/components/block-view/block-view.component.scss create mode 100644 frontend/src/app/components/block-view/block-view.component.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 79a8e1c02..7ca9e107b 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -4,6 +4,7 @@ import { AppPreloadingStrategy } from './app.preloading-strategy' import { StartComponent } from './components/start/start.component'; import { TransactionComponent } from './components/transaction/transaction.component'; import { BlockComponent } from './components/block/block.component'; +import { BlockViewComponent } from './components/block-view/block-view.component'; import { ClockComponent } from './components/clock/clock.component'; import { AddressComponent } from './components/address/address.component'; import { MasterPageComponent } from './components/master-page/master-page.component'; @@ -373,6 +374,10 @@ let routes: Routes = [ path: 'clock/:mode/:index', component: ClockComponent, }, + { + path: 'view/block/:id', + component: BlockViewComponent, + }, { path: 'status', data: { networks: ['bitcoin', 'liquid'] }, diff --git a/frontend/src/app/components/block-view/block-view.component.html b/frontend/src/app/components/block-view/block-view.component.html new file mode 100644 index 000000000..905c69198 --- /dev/null +++ b/frontend/src/app/components/block-view/block-view.component.html @@ -0,0 +1,13 @@ +
+
+ +
+
diff --git a/frontend/src/app/components/block-view/block-view.component.scss b/frontend/src/app/components/block-view/block-view.component.scss new file mode 100644 index 000000000..782d416d8 --- /dev/null +++ b/frontend/src/app/components/block-view/block-view.component.scss @@ -0,0 +1,22 @@ +.block-wrapper { + width: 100vw; + height: 100vh; + background: #181b2d; +} + +.block-container { + flex-grow: 0; + flex-shrink: 0; + width: 100vw; + max-width: 100vh; + height: 100vh; + padding: 0; + margin: auto; + display: flex; + justify-content: center; + align-items: center; + + * { + flex-grow: 1; + } +} \ No newline at end of file diff --git a/frontend/src/app/components/block-view/block-view.component.ts b/frontend/src/app/components/block-view/block-view.component.ts new file mode 100644 index 000000000..ef1a7247b --- /dev/null +++ b/frontend/src/app/components/block-view/block-view.component.ts @@ -0,0 +1,176 @@ +import { Component, OnInit, OnDestroy, ViewChild, HostListener } from '@angular/core'; +import { ActivatedRoute, ParamMap } from '@angular/router'; +import { ElectrsApiService } from '../../services/electrs-api.service'; +import { switchMap, tap, throttleTime, catchError, shareReplay, startWith, pairwise, filter } from 'rxjs/operators'; +import { of, Subscription, asyncScheduler } from 'rxjs'; +import { StateService } from '../../services/state.service'; +import { SeoService } from '../../services/seo.service'; +import { OpenGraphService } from '../../services/opengraph.service'; +import { BlockExtended, TransactionStripped } from '../../interfaces/node-api.interface'; +import { ApiService } from '../../services/api.service'; +import { seoDescriptionNetwork } from '../../shared/common.utils'; +import { BlockOverviewGraphComponent } from '../block-overview-graph/block-overview-graph.component'; + +function bestFitResolution(min, max, n) { + const target = (min + max) / 2; + let bestScore = Infinity; + let best = null; + for (let i = min; i <= max; i++) { + const remainder = (n % i); + if (remainder < bestScore || (remainder === bestScore && (Math.abs(i - target) < Math.abs(best - target)))) { + bestScore = remainder; + best = i; + } + } + return best; +} + +@Component({ + selector: 'app-block-view', + templateUrl: './block-view.component.html', + styleUrls: ['./block-view.component.scss'] +}) +export class BlockViewComponent implements OnInit, OnDestroy { + network = ''; + block: BlockExtended; + blockHeight: number; + blockHash: string; + rawId: string; + isLoadingBlock = true; + strippedTransactions: TransactionStripped[]; + isLoadingOverview = true; + autofit: boolean = false; + resolution: number = 80; + + overviewSubscription: Subscription; + networkChangedSubscription: Subscription; + queryParamsSubscription: Subscription; + + @ViewChild('blockGraph') blockGraph: BlockOverviewGraphComponent; + + constructor( + private route: ActivatedRoute, + private electrsApiService: ElectrsApiService, + public stateService: StateService, + private seoService: SeoService, + private openGraphService: OpenGraphService, + private apiService: ApiService + ) { } + + ngOnInit() { + this.network = this.stateService.network; + + this.queryParamsSubscription = this.route.queryParams.subscribe((params) => { + this.autofit = params.autofit === 'true'; + if (this.autofit) { + this.onResize(); + } + }); + + const block$ = this.route.paramMap.pipe( + switchMap((params: ParamMap) => { + this.rawId = params.get('id') || ''; + + const blockHash: string = params.get('id') || ''; + this.block = undefined; + + let isBlockHeight = false; + if (/^[0-9]+$/.test(blockHash)) { + isBlockHeight = true; + } else { + this.blockHash = blockHash; + } + + this.isLoadingBlock = true; + this.isLoadingOverview = true; + + if (isBlockHeight) { + return this.electrsApiService.getBlockHashFromHeight$(parseInt(blockHash, 10)) + .pipe( + switchMap((hash) => { + if (hash) { + this.blockHash = hash; + return this.apiService.getBlock$(hash); + } else { + return null; + } + }), + catchError(() => { + return of(null); + }), + ); + } + return this.apiService.getBlock$(blockHash); + }), + filter((block: BlockExtended | void) => block != null), + tap((block: BlockExtended) => { + this.block = block; + this.blockHeight = block.height; + + this.seoService.setTitle($localize`:@@block.component.browser-title:Block ${block.height}:BLOCK_HEIGHT:: ${block.id}:BLOCK_ID:`); + if( this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet' ) { + this.seoService.setDescription($localize`:@@meta.description.liquid.block:See size, weight, fee range, included transactions, and more for Liquid${seoDescriptionNetwork(this.stateService.network)} block ${block.height}:BLOCK_HEIGHT: (${block.id}:BLOCK_ID:).`); + } else { + this.seoService.setDescription($localize`:@@meta.description.bitcoin.block:See size, weight, fee range, included transactions, audit (expected v actual), and more for Bitcoin${seoDescriptionNetwork(this.stateService.network)} block ${block.height}:BLOCK_HEIGHT: (${block.id}:BLOCK_ID:).`); + } + this.isLoadingBlock = false; + this.isLoadingOverview = true; + }), + shareReplay(1) + ); + + this.overviewSubscription = block$.pipe( + switchMap((block) => this.apiService.getStrippedBlockTransactions$(block.id) + .pipe( + catchError(() => { + return of([]); + }), + switchMap((transactions) => { + return of(transactions); + }) + ) + ), + ) + .subscribe((transactions: TransactionStripped[]) => { + this.strippedTransactions = transactions; + this.isLoadingOverview = false; + if (this.blockGraph) { + this.blockGraph.destroy(); + this.blockGraph.setup(this.strippedTransactions); + } + }, + () => { + this.isLoadingOverview = false; + if (this.blockGraph) { + this.blockGraph.destroy(); + } + }); + + this.networkChangedSubscription = this.stateService.networkChanged$ + .subscribe((network) => this.network = network); + } + + @HostListener('window:resize', ['$event']) + onResize(): void { + if (this.autofit) { + this.resolution = bestFitResolution(64, 96, Math.min(window.innerWidth, window.innerHeight)); + console.log('resized, new resolution ', this.resolution, window.innerWidth, window.innerHeight); + // if (this.blockGraph && this.strippedTransactions) { + // this.blockGraph.destroy(); + // this.blockGraph.setup(this.strippedTransactions); + // } + } + } + + ngOnDestroy() { + if (this.overviewSubscription) { + this.overviewSubscription.unsubscribe(); + } + if (this.networkChangedSubscription) { + this.networkChangedSubscription.unsubscribe(); + } + if (this.queryParamsSubscription) { + this.queryParamsSubscription.unsubscribe(); + } + } +} diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index f7c253a96..bba70a2ce 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -97,6 +97,7 @@ import { AcceleratePreviewComponent } from '../components/accelerate-preview/acc import { AccelerateFeeGraphComponent } from '../components/accelerate-preview/accelerate-fee-graph.component'; import { MempoolErrorComponent } from './components/mempool-error/mempool-error.component'; +import { BlockViewComponent } from '../components/block-view/block-view.component'; import { MempoolBlockOverviewComponent } from '../components/mempool-block-overview/mempool-block-overview.component'; import { ClockchainComponent } from '../components/clockchain/clockchain.component'; import { ClockFaceComponent } from '../components/clock-face/clock-face.component'; @@ -134,6 +135,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir FiatCurrencyPipe, ColoredPriceDirective, BlockchainComponent, + BlockViewComponent, MempoolBlocksComponent, BlockchainBlocksComponent, AmountComponent, @@ -196,6 +198,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir AccelerateFeeGraphComponent, CalculatorComponent, BitcoinsatoshisPipe, + BlockViewComponent, MempoolBlockOverviewComponent, ClockchainComponent, ClockComponent, From 80afcae645a282235a08428c3e2e3b7179539443 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 23 Sep 2023 00:40:47 +0100 Subject: [PATCH 09/79] Fix faq blockchain positioning --- .../app/docs/api-docs/api-docs.component.html | 12 +++++- .../app/docs/api-docs/api-docs.component.scss | 43 ++++++++++++++++--- .../app/docs/api-docs/api-docs.component.ts | 9 +++- 3 files changed, 56 insertions(+), 8 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 40a7ae486..48a8bf418 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.html +++ b/frontend/src/app/docs/api-docs/api-docs.component.html @@ -137,8 +137,16 @@

A mempool explorer is a tool that enables you to view real-time and historical information about a node's mempool, visualize its transactions, and search and view those transactions.

The mempool.space website invented the concept of visualizing a Bitcoin node's mempool as projected blocks. These blocks are the inspiration for our half-filled block logo.

Projected blocks are on the left of the dotted white line, and confirmed blocks are on the right.

-
- +
+
+ +
+ + +
+
+
+
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 b90b843d9..f90274046 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.scss +++ b/frontend/src/app/docs/api-docs/api-docs.component.scss @@ -259,13 +259,46 @@ h3 { } .blockchain-wrapper { - position: relative; + display: block; + height: 100%; width: 100%; - overflow: auto; - scrollbar-width: none; + min-height: 220px; + position: relative; + overflow: hidden; + + .position-container { + position: absolute; + left: 50%; + bottom: 150px; + } + + #divider { + width: 2px; + height: 175px; + left: 0; + top: -40px; + position: absolute; + } + + &.time-ltr { + .blocks-wrapper { + transform: scaleX(-1); + } + } } -.blockchain-wrapper::-webkit-scrollbar { - display: none; + +:host-context(.ltr-layout) { + .blockchain-wrapper.time-ltr .blocks-wrapper, + .blockchain-wrapper .blocks-wrapper { + direction: ltr; + } +} + +:host-context(.rtl-layout) { + .blockchain-wrapper.time-ltr .blocks-wrapper, + .blockchain-wrapper .blocks-wrapper { + direction: rtl; + } } #disclaimer { 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 b0ae5967d..333bb01ad 100644 --- a/frontend/src/app/docs/api-docs/api-docs.component.ts +++ b/frontend/src/app/docs/api-docs/api-docs.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit, Input, QueryList, AfterViewInit, ViewChildren } from '@angular/core'; import { Env, StateService } from '../../services/state.service'; -import { Observable, merge, of, Subject } from 'rxjs'; +import { Observable, merge, of, Subject, Subscription } from 'rxjs'; import { tap, takeUntil } from 'rxjs/operators'; import { ActivatedRoute } from "@angular/router"; import { faqData, restApiDocsData, wsApiDocsData } from './api-docs-data'; @@ -30,6 +30,8 @@ export class ApiDocsComponent implements OnInit, AfterViewInit { officialMempoolInstance: boolean; auditEnabled: boolean; mobileViewport: boolean = false; + timeLtrSubscription: Subscription; + timeLtr: boolean = this.stateService.timeLtr.value; @ViewChildren(FaqTemplateDirective) faqTemplates: QueryList; dict = {}; @@ -104,12 +106,17 @@ export class ApiDocsComponent implements OnInit, AfterViewInit { this.electrsPort = 51302; break; } }); + + this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { + this.timeLtr = !!ltr; + }); } ngOnDestroy(): void { this.destroy$.next(true); this.destroy$.complete(); window.removeEventListener('scroll', this.onDocScroll); + this.timeLtrSubscription.unsubscribe(); } onDocScroll() { From 72750267d01922f0e189e8105c12e5f48c2e6001 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 23 Sep 2023 22:25:22 +0100 Subject: [PATCH 10/79] Enable navigation from standalone block page --- .../block-view/block-view.component.html | 1 + .../block-view/block-view.component.ts | 30 +++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/components/block-view/block-view.component.html b/frontend/src/app/components/block-view/block-view.component.html index 905c69198..9a2ddf373 100644 --- a/frontend/src/app/components/block-view/block-view.component.html +++ b/frontend/src/app/components/block-view/block-view.component.html @@ -8,6 +8,7 @@ [orientation]="'top'" [flip]="false" [disableSpinner]="true" + (txClickEvent)="onTxClick($event)" >
diff --git a/frontend/src/app/components/block-view/block-view.component.ts b/frontend/src/app/components/block-view/block-view.component.ts index ef1a7247b..5c3b7719c 100644 --- a/frontend/src/app/components/block-view/block-view.component.ts +++ b/frontend/src/app/components/block-view/block-view.component.ts @@ -1,17 +1,17 @@ import { Component, OnInit, OnDestroy, ViewChild, HostListener } from '@angular/core'; -import { ActivatedRoute, ParamMap } from '@angular/router'; +import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { ElectrsApiService } from '../../services/electrs-api.service'; -import { switchMap, tap, throttleTime, catchError, shareReplay, startWith, pairwise, filter } from 'rxjs/operators'; -import { of, Subscription, asyncScheduler } from 'rxjs'; +import { switchMap, tap, catchError, shareReplay, filter } from 'rxjs/operators'; +import { of, Subscription } from 'rxjs'; import { StateService } from '../../services/state.service'; import { SeoService } from '../../services/seo.service'; -import { OpenGraphService } from '../../services/opengraph.service'; import { BlockExtended, TransactionStripped } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { seoDescriptionNetwork } from '../../shared/common.utils'; import { BlockOverviewGraphComponent } from '../block-overview-graph/block-overview-graph.component'; +import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; -function bestFitResolution(min, max, n) { +function bestFitResolution(min, max, n): number { const target = (min + max) / 2; let bestScore = Infinity; let best = null; @@ -50,14 +50,14 @@ export class BlockViewComponent implements OnInit, OnDestroy { constructor( private route: ActivatedRoute, + private router: Router, private electrsApiService: ElectrsApiService, public stateService: StateService, private seoService: SeoService, - private openGraphService: OpenGraphService, private apiService: ApiService ) { } - ngOnInit() { + ngOnInit(): void { this.network = this.stateService.network; this.queryParamsSubscription = this.route.queryParams.subscribe((params) => { @@ -150,19 +150,23 @@ export class BlockViewComponent implements OnInit, OnDestroy { .subscribe((network) => this.network = network); } + onTxClick(event: { tx: TransactionStripped, keyModifier: boolean }): void { + const url = new RelativeUrlPipe(this.stateService).transform(`/tx/${event.tx.txid}`); + if (!event.keyModifier) { + this.router.navigate([url]); + } else { + window.open(url, '_blank'); + } + } + @HostListener('window:resize', ['$event']) onResize(): void { if (this.autofit) { this.resolution = bestFitResolution(64, 96, Math.min(window.innerWidth, window.innerHeight)); - console.log('resized, new resolution ', this.resolution, window.innerWidth, window.innerHeight); - // if (this.blockGraph && this.strippedTransactions) { - // this.blockGraph.destroy(); - // this.blockGraph.setup(this.strippedTransactions); - // } } } - ngOnDestroy() { + ngOnDestroy(): void { if (this.overviewSubscription) { this.overviewSubscription.unsubscribe(); } From 6773af92edce65739570682dc55ffca369b6ed74 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 23 Sep 2023 23:09:11 +0100 Subject: [PATCH 11/79] Add standalone mempool block visualization page --- frontend/src/app/app-routing.module.ts | 5 ++ .../mempool-block-view.component.html | 5 ++ .../mempool-block-view.component.scss | 22 +++++ .../mempool-block-view.component.ts | 85 +++++++++++++++++++ frontend/src/app/shared/shared.module.ts | 3 + 5 files changed, 120 insertions(+) create mode 100644 frontend/src/app/components/mempool-block-view/mempool-block-view.component.html create mode 100644 frontend/src/app/components/mempool-block-view/mempool-block-view.component.scss create mode 100644 frontend/src/app/components/mempool-block-view/mempool-block-view.component.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 7ca9e107b..7c2ac1274 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -5,6 +5,7 @@ import { StartComponent } from './components/start/start.component'; import { TransactionComponent } from './components/transaction/transaction.component'; import { BlockComponent } from './components/block/block.component'; import { BlockViewComponent } from './components/block-view/block-view.component'; +import { MempoolBlockViewComponent } from './components/mempool-block-view/mempool-block-view.component'; import { ClockComponent } from './components/clock/clock.component'; import { AddressComponent } from './components/address/address.component'; import { MasterPageComponent } from './components/master-page/master-page.component'; @@ -378,6 +379,10 @@ let routes: Routes = [ path: 'view/block/:id', component: BlockViewComponent, }, + { + path: 'view/mempool-block/:index', + component: MempoolBlockViewComponent, + }, { path: 'status', data: { networks: ['bitcoin', 'liquid'] }, diff --git a/frontend/src/app/components/mempool-block-view/mempool-block-view.component.html b/frontend/src/app/components/mempool-block-view/mempool-block-view.component.html new file mode 100644 index 000000000..9d51ff4e9 --- /dev/null +++ b/frontend/src/app/components/mempool-block-view/mempool-block-view.component.html @@ -0,0 +1,5 @@ +
+
+ +
+
\ No newline at end of file diff --git a/frontend/src/app/components/mempool-block-view/mempool-block-view.component.scss b/frontend/src/app/components/mempool-block-view/mempool-block-view.component.scss new file mode 100644 index 000000000..782d416d8 --- /dev/null +++ b/frontend/src/app/components/mempool-block-view/mempool-block-view.component.scss @@ -0,0 +1,22 @@ +.block-wrapper { + width: 100vw; + height: 100vh; + background: #181b2d; +} + +.block-container { + flex-grow: 0; + flex-shrink: 0; + width: 100vw; + max-width: 100vh; + height: 100vh; + padding: 0; + margin: auto; + display: flex; + justify-content: center; + align-items: center; + + * { + flex-grow: 1; + } +} \ No newline at end of file diff --git a/frontend/src/app/components/mempool-block-view/mempool-block-view.component.ts b/frontend/src/app/components/mempool-block-view/mempool-block-view.component.ts new file mode 100644 index 000000000..ebeb0801c --- /dev/null +++ b/frontend/src/app/components/mempool-block-view/mempool-block-view.component.ts @@ -0,0 +1,85 @@ +import { Component, OnInit, OnDestroy, HostListener } from '@angular/core'; +import { ActivatedRoute, ParamMap } from '@angular/router'; +import { Subscription, filter, map, switchMap, tap } from 'rxjs'; +import { StateService } from '../../services/state.service'; +import { WebsocketService } from '../../services/websocket.service'; + +function bestFitResolution(min, max, n): number { + const target = (min + max) / 2; + let bestScore = Infinity; + let best = null; + for (let i = min; i <= max; i++) { + const remainder = (n % i); + if (remainder < bestScore || (remainder === bestScore && (Math.abs(i - target) < Math.abs(best - target)))) { + bestScore = remainder; + best = i; + } + } + return best; +} + +@Component({ + selector: 'app-mempool-block-view', + templateUrl: './mempool-block-view.component.html', + styleUrls: ['./mempool-block-view.component.scss'] +}) +export class MempoolBlockViewComponent implements OnInit, OnDestroy { + autofit: boolean = false; + resolution: number = 80; + index: number = 0; + + routeParamsSubscription: Subscription; + queryParamsSubscription: Subscription; + + constructor( + private route: ActivatedRoute, + private websocketService: WebsocketService, + public stateService: StateService, + ) { } + + ngOnInit(): void { + this.websocketService.want(['blocks', 'mempool-blocks']); + + this.routeParamsSubscription = this.route.paramMap + .pipe( + switchMap((params: ParamMap) => { + this.index = parseInt(params.get('index'), 10) || 0; + return this.stateService.mempoolBlocks$ + .pipe( + map((blocks) => { + if (!blocks.length) { + return [{ index: 0, blockSize: 0, blockVSize: 0, feeRange: [0, 0], medianFee: 0, nTx: 0, totalFees: 0 }]; + } + return blocks; + }), + filter((mempoolBlocks) => mempoolBlocks.length > 0), + tap((mempoolBlocks) => { + while (!mempoolBlocks[this.index]) { + this.index--; + } + }) + ); + }) + ).subscribe(); + + this.queryParamsSubscription = this.route.queryParams.subscribe((params) => { + this.autofit = params.autofit === 'true'; + if (this.autofit) { + this.onResize(); + } + }); + } + + + @HostListener('window:resize', ['$event']) + onResize(): void { + if (this.autofit) { + this.resolution = bestFitResolution(64, 96, Math.min(window.innerWidth, window.innerHeight)); + } + } + + ngOnDestroy(): void { + this.routeParamsSubscription.unsubscribe(); + this.queryParamsSubscription.unsubscribe(); + } +} diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index bba70a2ce..dce65bfae 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -98,6 +98,7 @@ import { AccelerateFeeGraphComponent } from '../components/accelerate-preview/ac import { MempoolErrorComponent } from './components/mempool-error/mempool-error.component'; import { BlockViewComponent } from '../components/block-view/block-view.component'; +import { MempoolBlockViewComponent } from '../components/mempool-block-view/mempool-block-view.component'; import { MempoolBlockOverviewComponent } from '../components/mempool-block-overview/mempool-block-overview.component'; import { ClockchainComponent } from '../components/clockchain/clockchain.component'; import { ClockFaceComponent } from '../components/clock-face/clock-face.component'; @@ -136,6 +137,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir ColoredPriceDirective, BlockchainComponent, BlockViewComponent, + MempoolBlockViewComponent, MempoolBlocksComponent, BlockchainBlocksComponent, AmountComponent, @@ -199,6 +201,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir CalculatorComponent, BitcoinsatoshisPipe, BlockViewComponent, + MempoolBlockViewComponent, MempoolBlockOverviewComponent, ClockchainComponent, ClockComponent, From 6de8cbe9b904e20b8a266b4f19f8e0b6ea23e688 Mon Sep 17 00:00:00 2001 From: orangesurf Date: Mon, 25 Sep 2023 19:57:52 +0100 Subject: [PATCH 12/79] Add primal.net logo to footer --- .../shared/components/global-footer/global-footer.component.html | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/shared/components/global-footer/global-footer.component.html b/frontend/src/app/shared/components/global-footer/global-footer.component.html index 34d47379e..a717bd7ab 100644 --- a/frontend/src/app/shared/components/global-footer/global-footer.component.html +++ b/frontend/src/app/shared/components/global-footer/global-footer.component.html @@ -84,6 +84,7 @@ GitHub Twitter + YouTube Matrix From 420fbf3d6feec10a3e2ce6b507c2e5ded7717fb1 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 29 Sep 2023 00:02:01 +0100 Subject: [PATCH 13/79] Hide the blockchain arrow when transaction is replaced --- backend/src/api/websocket-handler.ts | 2 +- .../src/app/components/transaction/transaction.component.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/api/websocket-handler.ts b/backend/src/api/websocket-handler.ts index c50941f39..3a444701f 100644 --- a/backend/src/api/websocket-handler.ts +++ b/backend/src/api/websocket-handler.ts @@ -577,7 +577,7 @@ class WebsocketHandler { response['utxoSpent'] = JSON.stringify(outspends); } - const rbfReplacedBy = rbfCache.getReplacedBy(client['track-tx']); + const rbfReplacedBy = rbfChanges.map[client['track-tx']] ? rbfCache.getReplacedBy(client['track-tx']) : false; if (rbfReplacedBy) { response['rbfTransaction'] = JSON.stringify({ txid: rbfReplacedBy, diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 505c4686d..4743e5cd6 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -422,6 +422,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { } this.rbfTransaction = rbfTransaction; this.replaced = true; + this.stateService.markBlock$.next({}); + if (rbfTransaction && !this.tx) { this.fetchCachedTx$.next(this.txId); } From 10be2c8176813bd82ca6f14effeac78ab0143389 Mon Sep 17 00:00:00 2001 From: softsimon Date: Wed, 4 Oct 2023 01:41:49 +0400 Subject: [PATCH 14/79] Fix block pagination for liquid --- backend/src/api/bitcoin/bitcoin.routes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index 90a31ecae..240fb07ce 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -478,7 +478,7 @@ class BitcoinRoutes { } let nextHash = startFromHash; - for (let i = 0; i < 10 && nextHash; i++) { + for (let i = 0; i < 15 && nextHash; i++) { const localBlock = blocks.getBlocks().find((b) => b.id === nextHash); if (localBlock) { returnBlocks.push(localBlock); From a35c8be25c6f1f17a93d25215e08e346c6f6c753 Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Sun, 8 Oct 2023 09:03:22 -0700 Subject: [PATCH 15/79] Configurable unfurler config --- unfurler/src/config.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/unfurler/src/config.ts b/unfurler/src/config.ts index 76bc2a75f..5d5104478 100644 --- a/unfurler/src/config.ts +++ b/unfurler/src/config.ts @@ -1,4 +1,16 @@ -const configFile = require('../config.json'); +const fs = require('fs'); +const path = require('path'); + +const configPath = process.env.UNFURLER_CONFIG || '../config.json'; +const absolutePath = path.resolve(configPath); +let config; + +try { + config = JSON.parse(fs.readFileSync(absolutePath, 'utf8')); +} catch (e) { + console.error(`Could not read config file ${absolutePath}: ${e}`); + process.exit(-1); +} interface IConfig { SERVER: { @@ -57,7 +69,7 @@ class Config implements IConfig { SYSLOG: IConfig['SYSLOG']; constructor() { - const configs = this.merge(configFile, defaults); + const configs = this.merge(config, defaults); this.SERVER = configs.SERVER; this.MEMPOOL = configs.MEMPOOL; this.PUPPETEER = configs.PUPPETEER; From e27cf3398187aa3b26d370caad4990384b99571b Mon Sep 17 00:00:00 2001 From: orangesurf Date: Sun, 8 Oct 2023 17:52:03 +0100 Subject: [PATCH 16/79] Improve footer formatting for small screens --- .../global-footer/global-footer.component.html | 2 +- .../global-footer/global-footer.component.scss | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/shared/components/global-footer/global-footer.component.html b/frontend/src/app/shared/components/global-footer/global-footer.component.html index a717bd7ab..b162d9070 100644 --- a/frontend/src/app/shared/components/global-footer/global-footer.component.html +++ b/frontend/src/app/shared/components/global-footer/global-footer.component.html @@ -84,7 +84,7 @@ GitHub Twitter - + YouTube Matrix diff --git a/frontend/src/app/shared/components/global-footer/global-footer.component.scss b/frontend/src/app/shared/components/global-footer/global-footer.component.scss index 3bdc239a9..148383cb4 100644 --- a/frontend/src/app/shared/components/global-footer/global-footer.component.scss +++ b/frontend/src/app/shared/components/global-footer/global-footer.component.scss @@ -88,7 +88,14 @@ footer .row.link-tree { footer .row.social-links { text-align: center; - margin: 24px 0; + display: flex; + flex-wrap: wrap; + width: fit-content; + margin: 0 auto; + + @media (max-width: 450px){ + width: 250px; + } } footer .row.social-links a { @@ -97,6 +104,7 @@ footer .row.social-links a { footer .row.social-links svg { width: 20px; + margin: 10px 0 10px 0; } footer .row.version { @@ -189,10 +197,6 @@ footer .sponsor { margin-top: 15px; } - footer .row.social-links { - margin: 48px 0 24px 0; - } - footer .selector:not(:last-child) { margin-right: 10px; } @@ -236,10 +240,6 @@ footer .sponsor { margin-top: 15px; } - footer .services.row.social-links { - margin: 48px 0 24px 0; - } - footer .services.selector:not(:last-child) { margin-right: 10px; } From 21ede8671d2d5ac18a05f9f526bb0623e4b5067a Mon Sep 17 00:00:00 2001 From: orangesurf <91332210+orangesurf@users.noreply.github.com> Date: Mon, 9 Oct 2023 19:01:48 +0000 Subject: [PATCH 17/79] Update global-footer.component.html --- .../components/global-footer/global-footer.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/shared/components/global-footer/global-footer.component.html b/frontend/src/app/shared/components/global-footer/global-footer.component.html index b162d9070..a571e33c5 100644 --- a/frontend/src/app/shared/components/global-footer/global-footer.component.html +++ b/frontend/src/app/shared/components/global-footer/global-footer.component.html @@ -84,7 +84,7 @@ GitHub Twitter - + YouTube Matrix From af3d6eccfbe056011eb028a97fd1186f3c081794 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 11 Oct 2023 01:09:10 +0000 Subject: [PATCH 18/79] Fix node group map channel count --- frontend/src/app/lightning/group/group-preview.component.ts | 2 +- frontend/src/app/lightning/group/group.component.ts | 2 +- frontend/src/app/lightning/lightning-api.service.ts | 2 +- frontend/src/app/lightning/nodes-map/nodes-map.component.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/lightning/group/group-preview.component.ts b/frontend/src/app/lightning/group/group-preview.component.ts index fc81eab38..35bcb6e0f 100644 --- a/frontend/src/app/lightning/group/group-preview.component.ts +++ b/frontend/src/app/lightning/group/group-preview.component.ts @@ -57,7 +57,7 @@ export class GroupPreviewComponent implements OnInit { return of(null); } - return this.lightningApiService.getNodGroupNodes$(this.groupId); + return this.lightningApiService.getNodeGroup$(this.groupId); }), map((nodes) => { for (const node of nodes) { diff --git a/frontend/src/app/lightning/group/group.component.ts b/frontend/src/app/lightning/group/group.component.ts index 0786076ed..4c2cd4dd9 100644 --- a/frontend/src/app/lightning/group/group.component.ts +++ b/frontend/src/app/lightning/group/group.component.ts @@ -41,7 +41,7 @@ export class GroupComponent implements OnInit { this.seoService.setTitle(`Mempool.space Lightning Nodes`); this.seoService.setDescription(`See all Lightning nodes run by mempool.space -- these are the nodes that provide the data on the mempool.space Lightning dashboard.`); - this.nodes$ = this.lightningApiService.getNodGroupNodes$('mempool.space') + this.nodes$ = this.lightningApiService.getNodeGroup$('mempool.space') .pipe( map((nodes) => { for (const node of nodes) { diff --git a/frontend/src/app/lightning/lightning-api.service.ts b/frontend/src/app/lightning/lightning-api.service.ts index bdcc78f3f..fda93d446 100644 --- a/frontend/src/app/lightning/lightning-api.service.ts +++ b/frontend/src/app/lightning/lightning-api.service.ts @@ -27,7 +27,7 @@ export class LightningApiService { return this.httpClient.get(this.apiBasePath + '/api/v1/lightning/nodes/' + publicKey); } - getNodGroupNodes$(name: string): Observable { + getNodeGroup$(name: string): Observable { return this.httpClient.get(this.apiBasePath + '/api/v1/lightning/nodes/group/' + name); } diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts index ea80d8799..fef3fe021 100644 --- a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts +++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts @@ -88,7 +88,7 @@ export class NodesMap implements OnInit, OnChanges { node.public_key, node.alias, node.capacity, - node.channels, + node.active_channel_count, node.country, node.iso_code, ]); From e9e507af691c04f453f6a569e9433e0a588398d4 Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Tue, 10 Oct 2023 19:53:57 -0700 Subject: [PATCH 19/79] Fix default config path --- unfurler/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unfurler/src/config.ts b/unfurler/src/config.ts index 5d5104478..445ae4514 100644 --- a/unfurler/src/config.ts +++ b/unfurler/src/config.ts @@ -1,7 +1,7 @@ const fs = require('fs'); const path = require('path'); -const configPath = process.env.UNFURLER_CONFIG || '../config.json'; +const configPath = process.env.UNFURLER_CONFIG || './config.json'; const absolutePath = path.resolve(configPath); let config; From 0783507821480884c74fc27eb8994f5515b2ee33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 02:38:59 +0000 Subject: [PATCH 20/79] Bump @babel/core from 7.21.4 to 7.23.2 in /backend Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.21.4 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/@babel/core@7.23.2/packages/babel-core) --- updated-dependencies: - dependency-name: "@babel/core" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/package-lock.json | 557 ++++++++++++++++++++------------------ backend/package.json | 4 +- 2 files changed, 289 insertions(+), 272 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index c8dea34c0..5b1252599 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -9,7 +9,6 @@ "version": "3.0.0-dev", "license": "GNU Affero General Public License v3.0", "dependencies": { - "@babel/core": "^7.21.3", "@mempool/electrum-client": "1.1.9", "@types/node": "^18.15.3", "axios": "~1.5.0", @@ -26,7 +25,7 @@ }, "devDependencies": { "@babel/code-frame": "^7.18.6", - "@babel/core": "^7.21.3", + "@babel/core": "^7.23.2", "@types/compression": "^1.7.2", "@types/crypto-js": "^4.1.1", "@types/express": "^4.17.17", @@ -65,47 +64,48 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", - "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", + "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", - "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", + "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-compilation-targets": "^7.21.4", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.4", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.4", - "@babel/types": "^7.21.4", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -115,13 +115,19 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/@babel/generator": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", - "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.21.4", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -145,87 +151,84 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", - "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.21.4", - "@babel/helper-validator-option": "^7.21.0", - "browserslist": "^4.21.3", + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", - "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "dependencies": { - "@babel/types": "^7.21.4" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-plugin-utils": { @@ -238,78 +241,78 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", + "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -317,9 +320,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", - "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -506,33 +509,33 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", - "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.4", - "@babel/types": "^7.21.4", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -541,13 +544,13 @@ } }, "node_modules/@babel/types": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", - "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2590,9 +2593,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "dev": true, "funding": [ { @@ -2602,13 +2605,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -2700,9 +2707,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001473", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001473.tgz", - "integrity": "sha512-ewDad7+D2vlyy+E4UJuVfiBsU69IL+8oVmTuZnH5Q6CIUbxNfI50uVpRHbUPDD6SUaN2o0Lh4DhTrvLG/Tn1yg==", + "version": "1.0.30001547", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz", + "integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==", "dev": true, "funding": [ { @@ -3023,9 +3030,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.348", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.348.tgz", - "integrity": "sha512-gM7TdwuG3amns/1rlgxMbeeyNoBFPa+4Uu0c7FeROWh4qWmvSOnvcslKmWy51ggLKZ2n/F/4i2HJ+PVNxH9uCQ==", + "version": "1.4.551", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.551.tgz", + "integrity": "sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==", "dev": true }, "node_modules/emittery": { @@ -6184,9 +6191,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "node_modules/normalize-path": { @@ -7399,9 +7406,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "funding": [ { @@ -7411,6 +7418,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -7418,7 +7429,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -7683,50 +7694,59 @@ } }, "@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" } }, "@babel/compat-data": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", - "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", + "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", "dev": true }, "@babel/core": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", - "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", + "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", "dev": true, "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-compilation-targets": "^7.21.4", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.4", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.4", - "@babel/types": "^7.21.4", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + } } }, "@babel/generator": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", - "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "requires": { - "@babel/types": "^7.21.4", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -7746,66 +7766,63 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", - "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dev": true, "requires": { - "@babel/compat-data": "^7.21.4", - "@babel/helper-validator-option": "^7.21.0", - "browserslist": "^4.21.3", + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-imports": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", - "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "requires": { - "@babel/types": "^7.21.4" + "@babel/types": "^7.22.15" } }, "@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" } }, "@babel/helper-plugin-utils": { @@ -7815,67 +7832,67 @@ "dev": true }, "@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "requires": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.22.5" } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", "dev": true }, "@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", + "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", "dev": true, "requires": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0" } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", - "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -8005,42 +8022,42 @@ } }, "@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", - "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "requires": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.4", - "@babel/types": "^7.21.4", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", - "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -9615,15 +9632,15 @@ } }, "browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" } }, "bs-logger": { @@ -9694,9 +9711,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001473", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001473.tgz", - "integrity": "sha512-ewDad7+D2vlyy+E4UJuVfiBsU69IL+8oVmTuZnH5Q6CIUbxNfI50uVpRHbUPDD6SUaN2o0Lh4DhTrvLG/Tn1yg==", + "version": "1.0.30001547", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz", + "integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==", "dev": true }, "chalk": { @@ -9924,9 +9941,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { - "version": "1.4.348", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.348.tgz", - "integrity": "sha512-gM7TdwuG3amns/1rlgxMbeeyNoBFPa+4Uu0c7FeROWh4qWmvSOnvcslKmWy51ggLKZ2n/F/4i2HJ+PVNxH9uCQ==", + "version": "1.4.551", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.551.tgz", + "integrity": "sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==", "dev": true }, "emittery": { @@ -12280,9 +12297,9 @@ "dev": true }, "node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "normalize-path": { @@ -13118,9 +13135,9 @@ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, "requires": { "escalade": "^3.1.1", diff --git a/backend/package.json b/backend/package.json index 2db8f2046..621ef93bb 100644 --- a/backend/package.json +++ b/backend/package.json @@ -38,7 +38,7 @@ "rust-build": "cd rust-gbt && npm run build-release" }, "dependencies": { - "@babel/core": "^7.21.3", + "@babel/core": "^7.23.2", "@mempool/electrum-client": "1.1.9", "@types/node": "^18.15.3", "axios": "~1.5.0", @@ -55,7 +55,7 @@ }, "devDependencies": { "@babel/code-frame": "^7.18.6", - "@babel/core": "^7.21.3", + "@babel/core": "^7.23.2", "@types/compression": "^1.7.2", "@types/crypto-js": "^4.1.1", "@types/express": "^4.17.17", From 1bc02fd2163d809be4e9983ea85cc653c50be6d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 11:23:40 +0000 Subject: [PATCH 21/79] Bump @babel/traverse from 7.22.8 to 7.23.2 in /frontend Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.8 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 217 ++++++++++++++++++++++++------------- 1 file changed, 144 insertions(+), 73 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 087c738ac..3001dbf70 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -31,7 +31,6 @@ "bootstrap": "~4.6.2", "browserify": "^17.0.0", "clipboard": "^2.0.11", - "cypress": "^13.3.0", "domino": "^2.1.6", "echarts": "~5.4.3", "echarts-gl": "^2.0.9", @@ -1251,11 +1250,12 @@ "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==" }, "node_modules/@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dependencies": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" @@ -1465,20 +1465,33 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" @@ -1628,9 +1641,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } @@ -1670,12 +1683,12 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -1683,9 +1696,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", - "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -2880,18 +2893,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.22.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", - "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.7", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/types": "^7.22.5", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -2899,13 +2912,36 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dependencies": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@babel/types": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", - "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -17769,11 +17805,12 @@ "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==" }, "@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "requires": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" } }, "@babel/compat-data": { @@ -17938,17 +17975,29 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" }, "@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "requires": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "dependencies": { + "@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + } + } } }, "@babel/helper-hoist-variables": { @@ -18050,9 +18099,9 @@ "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" }, "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" }, "@babel/helper-validator-option": { "version": "7.22.5", @@ -18080,19 +18129,19 @@ } }, "@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "requires": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", - "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==" + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.22.5", @@ -18869,29 +18918,51 @@ } }, "@babel/traverse": { - "version": "7.22.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", - "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.7", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/types": "^7.22.5", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "requires": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + } } }, "@babel/types": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", - "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "requires": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, From 6efeeb1d64c0eaff9ca2a375e5efba999b4c6ebc Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 20 Oct 2023 15:10:40 +0000 Subject: [PATCH 22/79] Hide mempool count line by default --- .../app/components/mempool-graph/mempool-graph.component.ts | 4 ++-- .../src/app/components/statistics/statistics.component.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts index 935e79b2c..6e1d99f37 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts @@ -27,7 +27,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { @Input() data: any[]; @Input() filterSize = 100000; @Input() limitFilterFee = 1; - @Input() hideCount: boolean = false; + @Input() hideCount: boolean = true; @Input() height: number | string = 200; @Input() top: number | string = 20; @Input() right: number | string = 10; @@ -53,7 +53,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { chartInstance: any = undefined; weightMode: boolean = false; isWidget: boolean = false; - showCount: boolean = true; + showCount: boolean = false; constructor( private vbytesPipe: VbytesPipe, diff --git a/frontend/src/app/components/statistics/statistics.component.ts b/frontend/src/app/components/statistics/statistics.component.ts index ba9068975..6bc58b6d7 100644 --- a/frontend/src/app/components/statistics/statistics.component.ts +++ b/frontend/src/app/components/statistics/statistics.component.ts @@ -32,7 +32,7 @@ export class StatisticsComponent implements OnInit { chartColors = chartColors; filterSize = 100000; filterFeeIndex = 1; - showCount = true; + showCount = false; maxFeeIndex: number; dropDownOpen = false; From abee175f4f1817ef766c5f8bcf32b00896a2b92d Mon Sep 17 00:00:00 2001 From: fanquake Date: Mon, 23 Oct 2023 14:43:51 +0100 Subject: [PATCH 23/79] contrib: add fanquake CLA --- contributors/fanquake.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 contributors/fanquake.txt diff --git a/contributors/fanquake.txt b/contributors/fanquake.txt new file mode 100644 index 000000000..3212bdcbf --- /dev/null +++ b/contributors/fanquake.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 October 23, 2023. + +Signed: fanquake From 26f5776d6db02889fbfe2baba2d8851cad5ed6a4 Mon Sep 17 00:00:00 2001 From: fanquake Date: Mon, 23 Oct 2023 14:39:30 +0100 Subject: [PATCH 24/79] Upgrade bitcoin core to v25.1 --- production/install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/production/install b/production/install index b4f6d53c5..18ee06233 100755 --- a/production/install +++ b/production/install @@ -332,7 +332,7 @@ BITCOIN_REPO_URL=https://github.com/bitcoin/bitcoin BITCOIN_REPO_NAME=bitcoin BITCOIN_REPO_BRANCH=master #BITCOIN_LATEST_RELEASE=$(curl -s https://api.github.com/repos/bitcoin/bitcoin/releases/latest|grep tag_name|head -1|cut -d '"' -f4) -BITCOIN_LATEST_RELEASE=v25.0 +BITCOIN_LATEST_RELEASE=v25.1 echo -n '.' BISQ_REPO_URL=https://github.com/bisq-network/bisq From 59a92d03638ac0bf48e31a4b125b9eb2cc28b20b Mon Sep 17 00:00:00 2001 From: TKone7 Date: Mon, 23 Oct 2023 15:49:34 +0200 Subject: [PATCH 25/79] Fix cleanup logic Keep only cache entries with an expiry in the future. --- backend/src/api/memory-cache.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/memory-cache.ts b/backend/src/api/memory-cache.ts index fe4162420..e71aaa6a2 100644 --- a/backend/src/api/memory-cache.ts +++ b/backend/src/api/memory-cache.ts @@ -31,7 +31,7 @@ class MemoryCache { } private cleanup() { - this.cache = this.cache.filter((cache) => cache.expires < (new Date())); + this.cache = this.cache.filter((cache) => cache.expires > (new Date())); } } From 4ca273c8c18c753c97fa54fe4cb0d0c305c6d704 Mon Sep 17 00:00:00 2001 From: TKone7 Date: Mon, 23 Oct 2023 15:56:07 +0200 Subject: [PATCH 26/79] Agree to Contributor License Agreement --- contributors/TKone7.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 contributors/TKone7.txt diff --git a/contributors/TKone7.txt b/contributors/TKone7.txt new file mode 100644 index 000000000..0112e34a3 --- /dev/null +++ b/contributors/TKone7.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 October 23, 2023. + +Signed: TKone7 From 83fde600f12dcfd213a9ddd9a38c03e412ccf5bf Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 25 Oct 2023 16:53:05 +0000 Subject: [PATCH 27/79] Enforce purging rate minimum on recommended fees --- backend/src/api/fee-api.ts | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/backend/src/api/fee-api.ts b/backend/src/api/fee-api.ts index 82778825e..97dfc29d2 100644 --- a/backend/src/api/fee-api.ts +++ b/backend/src/api/fee-api.ts @@ -3,21 +3,30 @@ import { Common } from './common'; import mempool from './mempool'; import projectedBlocks from './mempool-blocks'; +interface RecommendedFees { + fastestFee: number, + halfHourFee: number, + hourFee: number, + economyFee: number, + minimumFee: number, +} + class FeeApi { constructor() { } defaultFee = Common.isLiquid() ? 0.1 : 1; - public getRecommendedFee() { + public getRecommendedFee(): RecommendedFees { const pBlocks = projectedBlocks.getMempoolBlocks(); const mPool = mempool.getMempoolInfo(); const minimumFee = Math.ceil(mPool.mempoolminfee * 100000); + const defaultMinFee = Math.max(minimumFee, this.defaultFee); if (!pBlocks.length) { return { - 'fastestFee': this.defaultFee, - 'halfHourFee': this.defaultFee, - 'hourFee': this.defaultFee, + 'fastestFee': defaultMinFee, + 'halfHourFee': defaultMinFee, + 'hourFee': defaultMinFee, 'economyFee': minimumFee, 'minimumFee': minimumFee, }; @@ -27,11 +36,15 @@ class FeeApi { const secondMedianFee = pBlocks[1] ? this.optimizeMedianFee(pBlocks[1], pBlocks[2], firstMedianFee) : this.defaultFee; const thirdMedianFee = pBlocks[2] ? this.optimizeMedianFee(pBlocks[2], pBlocks[3], secondMedianFee) : this.defaultFee; + // explicitly enforce a minimum of ceil(mempoolminfee) on all recommendations. + // simply rounding up recommended rates is insufficient, as the purging rate + // can exceed the median rate of projected blocks in some extreme scenarios + // (see https://bitcoin.stackexchange.com/a/120024) return { - 'fastestFee': firstMedianFee, - 'halfHourFee': secondMedianFee, - 'hourFee': thirdMedianFee, - 'economyFee': Math.min(2 * minimumFee, thirdMedianFee), + 'fastestFee': Math.max(minimumFee, firstMedianFee), + 'halfHourFee': Math.max(minimumFee, secondMedianFee), + 'hourFee': Math.max(minimumFee, thirdMedianFee), + 'economyFee': Math.max(minimumFee, Math.min(2 * minimumFee, thirdMedianFee)), 'minimumFee': minimumFee, }; } From 5523c77a043b6d704b24c3ddd3036df5445ee931 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 28 Oct 2023 21:06:33 +0000 Subject: [PATCH 28/79] Fix stray , in docker backend config --- docker/backend/mempool-config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/backend/mempool-config.json b/docker/backend/mempool-config.json index 457eccd4a..712b95b3e 100644 --- a/docker/backend/mempool-config.json +++ b/docker/backend/mempool-config.json @@ -70,7 +70,7 @@ "USERNAME": "__DATABASE_USERNAME__", "PASSWORD": "__DATABASE_PASSWORD__", "TIMEOUT": __DATABASE_TIMEOUT__, - "PID_DIR": "__DATABASE_PID_DIR__", + "PID_DIR": "__DATABASE_PID_DIR__" }, "SYSLOG": { "ENABLED": __SYSLOG_ENABLED__, From 939fe951d47dcd6a9681e50155e491ffd38d3f82 Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Sun, 5 Nov 2023 13:47:23 +0100 Subject: [PATCH 29/79] README: add `nix-bitcoin` to node distros --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dd2e62478..9b56dc436 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Mempool can be conveniently installed on the following full-node distros: - [RoninDojo](https://code.samourai.io/ronindojo/RoninDojo) - [myNode](https://github.com/mynodebtc/mynode) - [Start9](https://github.com/Start9Labs/embassy-os) +- [nix-bitcoin](https://github.com/fort-nix/nix-bitcoin/blob/a1eacce6768ca4894f365af8f79be5bbd594e1c3/examples/configuration.nix#L129) **We highly recommend you deploy your own Mempool instance this way.** No matter which option you pick, you'll be able to get your own fully-sovereign instance of Mempool up quickly without needing to fiddle with any settings. From 14c87e2f03f5a5976e8188c9de96c157d391f06a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 01:44:00 +0000 Subject: [PATCH 30/79] Bump crypto-js from 4.1.1 to 4.2.0 in /backend Bumps [crypto-js](https://github.com/brix/crypto-js) from 4.1.1 to 4.2.0. - [Commits](https://github.com/brix/crypto-js/compare/4.1.1...4.2.0) --- updated-dependencies: - dependency-name: crypto-js dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- backend/package-lock.json | 15 ++++++++------- backend/package.json | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 5b1252599..33f19a26d 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -9,11 +9,12 @@ "version": "3.0.0-dev", "license": "GNU Affero General Public License v3.0", "dependencies": { + "@babel/core": "^7.23.2", "@mempool/electrum-client": "1.1.9", "@types/node": "^18.15.3", "axios": "~1.5.0", "bitcoinjs-lib": "~6.1.3", - "crypto-js": "~4.1.1", + "crypto-js": "~4.2.0", "express": "~4.18.2", "maxmind": "~4.3.11", "mysql2": "~3.6.0", @@ -2899,9 +2900,9 @@ } }, "node_modules/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, "node_modules/debug": { "version": "4.3.4", @@ -9849,9 +9850,9 @@ } }, "crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, "debug": { "version": "4.3.4", diff --git a/backend/package.json b/backend/package.json index 621ef93bb..bdce50902 100644 --- a/backend/package.json +++ b/backend/package.json @@ -43,7 +43,7 @@ "@types/node": "^18.15.3", "axios": "~1.5.0", "bitcoinjs-lib": "~6.1.3", - "crypto-js": "~4.1.1", + "crypto-js": "~4.2.0", "express": "~4.18.2", "maxmind": "~4.3.11", "mysql2": "~3.6.0", From 5f034d34b341b8a840dc6093b9a2beb1831f9514 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 01:45:30 +0000 Subject: [PATCH 31/79] Bump browserify-sign from 4.2.1 to 4.2.2 in /frontend Bumps [browserify-sign](https://github.com/crypto-browserify/browserify-sign) from 4.2.1 to 4.2.2. - [Changelog](https://github.com/browserify/browserify-sign/blob/main/CHANGELOG.md) - [Commits](https://github.com/crypto-browserify/browserify-sign/compare/v4.2.1...v4.2.2) --- updated-dependencies: - dependency-name: browserify-sign dependency-type: indirect ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 63 ++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3001dbf70..49410de87 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -5611,9 +5611,9 @@ "optional": true }, "node_modules/bn.js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", - "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, "node_modules/body-parser": { "version": "1.20.1", @@ -5908,25 +5908,28 @@ } }, "node_modules/browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", + "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", "dependencies": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", + "elliptic": "^6.5.4", "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "parse-asn1": "^5.1.6", + "readable-stream": "^3.6.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 4" } }, "node_modules/browserify-sign/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -20918,9 +20921,9 @@ "optional": true }, "bn.js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", - "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, "body-parser": { "version": "1.20.1", @@ -21264,25 +21267,25 @@ } }, "browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", + "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", + "elliptic": "^6.5.4", "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "parse-asn1": "^5.1.6", + "readable-stream": "^3.6.2", + "safe-buffer": "^5.2.1" }, "dependencies": { "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", From a33dc74c884359166a2d3dc86712933a327d4029 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 6 Nov 2023 18:19:54 +0000 Subject: [PATCH 32/79] Shake the echarts tree --- frontend/package.json | 1 - .../block-fee-rates-graph.component.ts | 2 +- .../block-fees-graph.component.ts | 6 +++--- .../block-health-graph.component.ts | 2 +- .../block-rewards-graph.component.ts | 6 +++--- .../block-sizes-weights-graph.component.ts | 2 +- .../hashrate-chart/hashrate-chart.component.ts | 12 ++++++------ .../hashrate-chart-pools.component.ts | 2 +- .../incoming-transactions-graph.component.ts | 2 +- .../lbtc-pegs-graph.component.ts | 2 +- .../mempool-graph/mempool-graph.component.ts | 2 +- .../pool-ranking/pool-ranking.component.ts | 2 +- .../components/pool/pool-preview.component.ts | 4 ++-- .../src/app/components/pool/pool.component.ts | 4 ++-- frontend/src/app/graphs/echarts.ts | 17 +++++++++++++++++ frontend/src/app/graphs/graphs.module.ts | 2 +- .../node-fee-chart/node-fee-chart.component.ts | 2 +- .../node-statistics-chart.component.ts | 2 +- .../nodes-channels-map.component.ts | 5 ++--- .../nodes-channels/node-channels.component.ts | 6 +++--- .../lightning/nodes-map/nodes-map.component.ts | 4 ++-- .../nodes-networks-chart.component.ts | 10 +++++----- .../nodes-per-country-chart.component.ts | 2 +- .../nodes-per-isp-chart.component.ts | 2 +- .../lightning-statistics-chart.component.ts | 4 ++-- 25 files changed, 60 insertions(+), 45 deletions(-) create mode 100644 frontend/src/app/graphs/echarts.ts diff --git a/frontend/package.json b/frontend/package.json index 294ace61d..0c7874c30 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -85,7 +85,6 @@ "clipboard": "^2.0.11", "domino": "^2.1.6", "echarts": "~5.4.3", - "echarts-gl": "^2.0.9", "lightweight-charts": "~3.8.0", "ngx-echarts": "~16.0.0", "ngx-infinite-scroll": "^16.0.0", diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts index b4c4e9a3b..c4d061927 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnInit } from '@angular/core'; -import { EChartsOption } from 'echarts'; +import { EChartsOption } from '../../graphs/echarts'; import { Observable, Subscription, combineLatest } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; 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 722929f9e..fc71fb575 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,5 +1,5 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; -import { EChartsOption, graphic } from 'echarts'; +import { echarts, EChartsOption } from '../../graphs/echarts'; import { Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; @@ -123,11 +123,11 @@ export class BlockFeesGraphComponent implements OnInit { this.chartOptions = { title: title, color: [ - new graphic.LinearGradient(0, 0, 0, 1, [ + new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#FDD835' }, { offset: 1, color: '#FB8C00' }, ]), - new graphic.LinearGradient(0, 0, 0, 1, [ + new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#C0CA33' }, { offset: 1, color: '#1B5E20' }, ]), diff --git a/frontend/src/app/components/block-health-graph/block-health-graph.component.ts b/frontend/src/app/components/block-health-graph/block-health-graph.component.ts index 299044dbb..4fea6f245 100644 --- a/frontend/src/app/components/block-health-graph/block-health-graph.component.ts +++ b/frontend/src/app/components/block-health-graph/block-health-graph.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnInit } from '@angular/core'; -import { EChartsOption } from 'echarts'; +import { EChartsOption } from '../../graphs/echarts'; import { Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; 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 505da17a5..9c987fb57 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,5 +1,5 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; -import { EChartsOption, graphic } from 'echarts'; +import { echarts, EChartsOption } from '../../graphs/echarts'; import { Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; @@ -123,11 +123,11 @@ export class BlockRewardsGraphComponent implements OnInit { title: title, animation: false, color: [ - new graphic.LinearGradient(0, 0, 0, 1, [ + new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#FDD835' }, { offset: 1, color: '#FB8C00' }, ]), - new graphic.LinearGradient(0, 0, 0, 1, [ + new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#C0CA33' }, { offset: 1, color: '#1B5E20' }, ]), diff --git a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts index e42c6a8df..5d01e48e9 100644 --- a/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts +++ b/frontend/src/app/components/block-sizes-weights-graph/block-sizes-weights-graph.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; -import { EChartsOption} from 'echarts'; +import { EChartsOption} from '../../graphs/echarts'; import { Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts index 592aba60b..5f17938ae 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; -import { EChartsOption, graphic } from 'echarts'; +import { echarts, EChartsOption } from '../../graphs/echarts'; import { merge, Observable, of } from 'rxjs'; import { map, mergeMap, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; @@ -204,7 +204,7 @@ export class HashrateChartComponent implements OnInit { title: title, animation: false, color: [ - new graphic.LinearGradient(0, 0, 0, 0.65, [ + new echarts.graphic.LinearGradient(0, 0, 0, 0.65, [ { offset: 0, color: '#F4511E99' }, { offset: 0.25, color: '#FB8C0099' }, { offset: 0.5, color: '#FFB30099' }, @@ -212,7 +212,7 @@ export class HashrateChartComponent implements OnInit { { offset: 1, color: '#7CB34299' } ]), '#D81B60', - new graphic.LinearGradient(0, 0, 0, 0.65, [ + new echarts.graphic.LinearGradient(0, 0, 0, 0.65, [ { offset: 0, color: '#F4511E' }, { offset: 0.25, color: '#FB8C00' }, { offset: 0.5, color: '#FFB300' }, @@ -342,7 +342,7 @@ export class HashrateChartComponent implements OnInit { type: 'value', axisLabel: { color: 'rgb(110, 112, 121)', - formatter: (val) => { + formatter: (val): string => { const selectedPowerOfTen: any = selectPowerOfTen(val); const newVal = Math.round(val / selectedPowerOfTen.divider); return `${newVal} ${selectedPowerOfTen.unit}H/s`; @@ -364,9 +364,9 @@ export class HashrateChartComponent implements OnInit { position: 'right', axisLabel: { color: 'rgb(110, 112, 121)', - formatter: (val) => { + formatter: (val): string => { if (this.stateService.network === 'signet') { - return val; + return `${val}`; } const selectedPowerOfTen: any = selectPowerOfTen(val); const newVal = Math.round(val / selectedPowerOfTen.divider); diff --git a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts index e7e3685d3..b0557ca7c 100644 --- a/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts +++ b/frontend/src/app/components/hashrates-chart-pools/hashrate-chart-pools.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; -import { EChartsOption } from 'echarts'; +import { EChartsOption } from '../../graphs/echarts'; import { Observable } from 'rxjs'; import { delay, map, retryWhen, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts index 3b93cb686..1b68a5a99 100644 --- a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts +++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts @@ -1,5 +1,5 @@ import { Component, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core'; -import { EChartsOption } from 'echarts'; +import { EChartsOption } from '../../graphs/echarts'; import { OnChanges } from '@angular/core'; import { StorageService } from '../../services/storage.service'; import { download, formatterXAxis, formatterXAxisLabel } from '../../shared/graphs.utils'; diff --git a/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.ts b/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.ts index e4aa95492..c4e8cbf91 100644 --- a/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.ts +++ b/frontend/src/app/components/lbtc-pegs-graph/lbtc-pegs-graph.component.ts @@ -1,6 +1,6 @@ import { Component, Inject, LOCALE_ID, ChangeDetectionStrategy, Input, OnChanges, OnInit } from '@angular/core'; import { formatDate, formatNumber } from '@angular/common'; -import { EChartsOption } from 'echarts'; +import { EChartsOption } from '../../graphs/echarts'; @Component({ selector: 'app-lbtc-pegs-graph', diff --git a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts index 935e79b2c..3ea30810f 100644 --- a/frontend/src/app/components/mempool-graph/mempool-graph.component.ts +++ b/frontend/src/app/components/mempool-graph/mempool-graph.component.ts @@ -6,7 +6,7 @@ import { formatNumber } from '@angular/common'; import { OptimizedMempoolStats } from '../../interfaces/node-api.interface'; import { StateService } from '../../services/state.service'; import { StorageService } from '../../services/storage.service'; -import { EChartsOption } from 'echarts'; +import { EChartsOption } from '../../graphs/echarts'; import { feeLevels, chartColors } from '../../app.constants'; import { download, formatterXAxis, formatterXAxisLabel } from '../../shared/graphs.utils'; diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts index 91475040c..392cdf8c5 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, Component, Input, NgZone, OnInit, HostBinding } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { EChartsOption, PieSeriesOption } from 'echarts'; +import { EChartsOption, PieSeriesOption } from '../../graphs/echarts'; import { merge, Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { SeoService } from '../../services/seo.service'; diff --git a/frontend/src/app/components/pool/pool-preview.component.ts b/frontend/src/app/components/pool/pool-preview.component.ts index b2302b9a7..e0c786082 100644 --- a/frontend/src/app/components/pool/pool-preview.component.ts +++ b/frontend/src/app/components/pool/pool-preview.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Inject, LOCALE_ID, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { EChartsOption, graphic } from 'echarts'; +import { echarts, EChartsOption } from '../../graphs/echarts'; import { Observable, of } from 'rxjs'; import { map, switchMap, catchError } from 'rxjs/operators'; import { PoolStat } from '../../interfaces/node-api.interface'; @@ -127,7 +127,7 @@ export class PoolPreviewComponent implements OnInit { title: title, animation: false, color: [ - new graphic.LinearGradient(0, 0, 0, 0.65, [ + new echarts.graphic.LinearGradient(0, 0, 0, 0.65, [ { offset: 0, color: '#F4511E' }, { offset: 0.25, color: '#FB8C00' }, { offset: 0.5, color: '#FFB300' }, diff --git a/frontend/src/app/components/pool/pool.component.ts b/frontend/src/app/components/pool/pool.component.ts index 0d465bc3c..86fdeda18 100644 --- a/frontend/src/app/components/pool/pool.component.ts +++ b/frontend/src/app/components/pool/pool.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { EChartsOption, graphic } from 'echarts'; +import { echarts, EChartsOption } from '../../graphs/echarts'; import { BehaviorSubject, Observable, of, timer } from 'rxjs'; import { catchError, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators'; import { BlockExtended, PoolStat } from '../../interfaces/node-api.interface'; @@ -131,7 +131,7 @@ export class PoolComponent implements OnInit { title: title, animation: false, color: [ - new graphic.LinearGradient(0, 0, 0, 0.65, [ + new echarts.graphic.LinearGradient(0, 0, 0, 0.65, [ { offset: 0, color: '#F4511E' }, { offset: 0.25, color: '#FB8C00' }, { offset: 0.5, color: '#FFB300' }, diff --git a/frontend/src/app/graphs/echarts.ts b/frontend/src/app/graphs/echarts.ts new file mode 100644 index 000000000..588e5e5f1 --- /dev/null +++ b/frontend/src/app/graphs/echarts.ts @@ -0,0 +1,17 @@ +// Import tree-shakeable echarts +import * as echarts from 'echarts/core'; +import { LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart } from 'echarts/charts'; +import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, DataZoomComponent, VisualMapComponent } from 'echarts/components'; +import { SVGRenderer, CanvasRenderer } from 'echarts/renderers'; +// Typescript interfaces +import { EChartsOption, TreemapSeriesOption, LineSeriesOption, PieSeriesOption } from 'echarts'; + + +echarts.use([ + SVGRenderer, CanvasRenderer, + TitleComponent, TooltipComponent, GridComponent, + LegendComponent, GeoComponent, DataZoomComponent, + VisualMapComponent, + LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart +]); +export { echarts, EChartsOption, TreemapSeriesOption, LineSeriesOption, PieSeriesOption }; \ No newline at end of file diff --git a/frontend/src/app/graphs/graphs.module.ts b/frontend/src/app/graphs/graphs.module.ts index 87e8a620b..a2160977c 100644 --- a/frontend/src/app/graphs/graphs.module.ts +++ b/frontend/src/app/graphs/graphs.module.ts @@ -53,7 +53,7 @@ import { CommonModule } from '@angular/common'; SharedModule, GraphsRoutingModule, NgxEchartsModule.forRoot({ - echarts: () => import('echarts') + echarts: () => import('./echarts').then(m => m.echarts), }) ], exports: [ diff --git a/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.ts b/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.ts index f20142e47..ad667bcf6 100644 --- a/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.ts +++ b/frontend/src/app/lightning/node-fee-chart/node-fee-chart.component.ts @@ -1,5 +1,5 @@ import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; -import { EChartsOption } from 'echarts'; +import { EChartsOption } from '../../graphs/echarts'; import { switchMap } from 'rxjs/operators'; import { download } from '../../shared/graphs.utils'; import { LightningApiService } from '../lightning-api.service'; diff --git a/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.ts b/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.ts index 9b10152b5..a9308a887 100644 --- a/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.ts +++ b/frontend/src/app/lightning/node-statistics-chart/node-statistics-chart.component.ts @@ -1,5 +1,5 @@ import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; -import { EChartsOption } from 'echarts'; +import { EChartsOption } from '../../graphs/echarts'; import { Observable } from 'rxjs'; import { switchMap, tap } from 'rxjs/operators'; import { formatNumber } from '@angular/common'; diff --git a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts index 3090a803c..01978c324 100644 --- a/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts +++ b/frontend/src/app/lightning/nodes-channels-map/nodes-channels-map.component.ts @@ -6,8 +6,7 @@ import { AssetsService } from '../../services/assets.service'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; import { StateService } from '../../services/state.service'; -import { EChartsOption, registerMap } from 'echarts'; -import 'echarts-gl'; +import { EChartsOption, echarts } from '../../graphs/echarts'; import { isMobile } from '../../shared/common.utils'; @Component({ @@ -88,7 +87,7 @@ export class NodesChannelsMap implements OnInit { this.style !== 'channelpage' ? this.apiService.getChannelsGeo$(params.get('public_key') ?? undefined, this.style) : [''], [params.get('public_key') ?? undefined] ).pipe(tap((data) => { - registerMap('world', data[0]); + echarts.registerMap('world', data[0]); const channelsLoc = []; const nodes = []; diff --git a/frontend/src/app/lightning/nodes-channels/node-channels.component.ts b/frontend/src/app/lightning/nodes-channels/node-channels.component.ts index 91d43f6ab..be596d8f9 100644 --- a/frontend/src/app/lightning/nodes-channels/node-channels.component.ts +++ b/frontend/src/app/lightning/nodes-channels/node-channels.component.ts @@ -1,7 +1,7 @@ import { formatNumber } from '@angular/common'; import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnChanges } from '@angular/core'; import { Router } from '@angular/router'; -import { ECharts, EChartsOption, TreemapSeriesOption } from 'echarts'; +import { EChartsOption, TreemapSeriesOption } from '../../graphs/echarts'; import { Observable, share, switchMap, tap } from 'rxjs'; import { lerpColor } from '../../shared/graphs.utils'; import { AmountShortenerPipe } from '../../shared/pipes/amount-shortener.pipe'; @@ -18,7 +18,7 @@ import { StateService } from '../../services/state.service'; export class NodeChannels implements OnChanges { @Input() publicKey: string; - chartInstance: ECharts; + chartInstance: any; chartOptions: EChartsOption = {}; chartInitOptions = { renderer: 'svg', @@ -129,7 +129,7 @@ export class NodeChannels implements OnChanges { }; } - onChartInit(ec: ECharts): void { + onChartInit(ec: any): void { this.chartInstance = ec; this.chartInstance.on('click', (e) => { diff --git a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts index ea80d8799..bb9e21c4b 100644 --- a/frontend/src/app/lightning/nodes-map/nodes-map.component.ts +++ b/frontend/src/app/lightning/nodes-map/nodes-map.component.ts @@ -3,7 +3,7 @@ import { SeoService } from '../../services/seo.service'; import { ApiService } from '../../services/api.service'; import { Observable, BehaviorSubject, switchMap, tap, combineLatest } from 'rxjs'; import { AssetsService } from '../../services/assets.service'; -import { EChartsOption, registerMap } from 'echarts'; +import { EChartsOption, echarts } from '../../graphs/echarts'; import { lerpColor } from '../../shared/graphs.utils'; import { Router } from '@angular/router'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; @@ -63,7 +63,7 @@ export class NodesMap implements OnInit, OnChanges { this.assetsService.getWorldMapJson$, this.nodes$ ).pipe(tap((data) => { - registerMap('world', data[0]); + echarts.registerMap('world', data[0]); let maxLiquidity = data[1].maxLiquidity; let inputNodes: any[] = data[1].nodes; diff --git a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts index f62a6a244..7352d884d 100644 --- a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts +++ b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; -import { EChartsOption, graphic, LineSeriesOption} from 'echarts'; +import { echarts, EChartsOption, LineSeriesOption } from '../../graphs/echarts'; import { Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { formatNumber } from '@angular/common'; @@ -152,7 +152,7 @@ export class NodesNetworksChartComponent implements OnInit { opacity: 0.5, }, stack: 'Total', - color: new graphic.LinearGradient(0, 0.75, 0, 1, [ + color: new echarts.graphic.LinearGradient(0, 0.75, 0, 1, [ { offset: 0, color: '#D81B60' }, { offset: 1, color: '#D81B60AA' }, ]), @@ -174,7 +174,7 @@ export class NodesNetworksChartComponent implements OnInit { opacity: 0.5, }, stack: 'Total', - color: new graphic.LinearGradient(0, 0.75, 0, 1, [ + color: new echarts.graphic.LinearGradient(0, 0.75, 0, 1, [ { offset: 0, color: '#be7d4c' }, { offset: 1, color: '#be7d4cAA' }, ]), @@ -195,7 +195,7 @@ export class NodesNetworksChartComponent implements OnInit { opacity: 0.5, }, stack: 'Total', - color: new graphic.LinearGradient(0, 0.75, 0, 1, [ + color: new echarts.graphic.LinearGradient(0, 0.75, 0, 1, [ { offset: 0, color: '#FFB300' }, { offset: 1, color: '#FFB300AA' }, ]), @@ -216,7 +216,7 @@ export class NodesNetworksChartComponent implements OnInit { opacity: 0.5, }, stack: 'Total', - color: new graphic.LinearGradient(0, 0.75, 0, 1, [ + color: new echarts.graphic.LinearGradient(0, 0.75, 0, 1, [ { offset: 0, color: '#7D4698' }, { offset: 1, color: '#7D4698AA' }, ]), diff --git a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts index 5bfa0fc2c..e305d8959 100644 --- a/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts +++ b/frontend/src/app/lightning/nodes-per-country-chart/nodes-per-country-chart.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, OnInit, HostBinding, NgZone } from '@angular/core'; import { Router } from '@angular/router'; -import { EChartsOption, PieSeriesOption } from 'echarts'; +import { EChartsOption, PieSeriesOption } from '../../graphs/echarts'; import { map, Observable, share, tap } from 'rxjs'; import { chartColors } from '../../app.constants'; import { ApiService } from '../../services/api.service'; diff --git a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts index 67f393bc2..5342f616b 100644 --- a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, OnInit, HostBinding, NgZone, Input } from '@angular/core'; import { Router } from '@angular/router'; -import { EChartsOption, PieSeriesOption } from 'echarts'; +import { EChartsOption, PieSeriesOption } from '../../graphs/echarts'; import { combineLatest, map, Observable, share, startWith, Subject, switchMap, tap } from 'rxjs'; import { chartColors } from '../../app.constants'; import { ApiService } from '../../services/api.service'; diff --git a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts index 41e170de6..7417a35cd 100644 --- a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts +++ b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts @@ -1,5 +1,5 @@ import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; -import { EChartsOption, graphic } from 'echarts'; +import { echarts, EChartsOption } from '../../graphs/echarts'; import { Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { SeoService } from '../../services/seo.service'; @@ -132,7 +132,7 @@ export class LightningStatisticsChartComponent implements OnInit { animation: false, color: [ '#FFB300', - new graphic.LinearGradient(0, 0.75, 0, 1, [ + new echarts.graphic.LinearGradient(0, 0.75, 0, 1, [ { offset: 0, color: '#D81B60' }, { offset: 1, color: '#D81B60AA' }, ]), From d18b8881d0660774af7d3a5c3ee7411f8baad6fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Nov 2023 02:16:55 +0000 Subject: [PATCH 33/79] Bump axios from 1.5.0 to 1.6.1 in /backend Bumps [axios](https://github.com/axios/axios) from 1.5.0 to 1.6.1. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.5.0...v1.6.1) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/package-lock.json | 14 +++++++------- backend/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 33f19a26d..3e9e31988 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -12,7 +12,7 @@ "@babel/core": "^7.23.2", "@mempool/electrum-client": "1.1.9", "@types/node": "^18.15.3", - "axios": "~1.5.0", + "axios": "~1.6.1", "bitcoinjs-lib": "~6.1.3", "crypto-js": "~4.2.0", "express": "~4.18.2", @@ -2325,9 +2325,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", + "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -9415,9 +9415,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", + "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", diff --git a/backend/package.json b/backend/package.json index bdce50902..0c1d3cc4a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -41,7 +41,7 @@ "@babel/core": "^7.23.2", "@mempool/electrum-client": "1.1.9", "@types/node": "^18.15.3", - "axios": "~1.5.0", + "axios": "~1.6.1", "bitcoinjs-lib": "~6.1.3", "crypto-js": "~4.2.0", "express": "~4.18.2", From b5a8687b6a9fa3273ab1de2e38c0612b67600c51 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 8 Nov 2023 00:09:23 +0000 Subject: [PATCH 34/79] Split Block component into separate module --- frontend/src/app/app-routing.module.ts | 61 +++---------------- .../src/app/components/block/block.module.ts | 43 +++++++++++++ frontend/src/app/shared/shared.module.ts | 3 - 3 files changed, 53 insertions(+), 54 deletions(-) create mode 100644 frontend/src/app/components/block/block.module.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 7c2ac1274..cbf38c69d 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -3,7 +3,6 @@ import { Routes, RouterModule } from '@angular/router'; import { AppPreloadingStrategy } from './app.preloading-strategy' import { StartComponent } from './components/start/start.component'; import { TransactionComponent } from './components/transaction/transaction.component'; -import { BlockComponent } from './components/block/block.component'; import { BlockViewComponent } from './components/block-view/block-view.component'; import { MempoolBlockViewComponent } from './components/mempool-block-view/mempool-block-view.component'; import { ClockComponent } from './components/clock/clock.component'; @@ -100,16 +99,8 @@ let routes: Routes = [ { path: 'block', component: StartComponent, - data: { networkSpecific: true }, - children: [ - { - path: ':id', - component: BlockComponent, - data: { - ogImage: true - } - }, - ], + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('./components/block/block.module').then(m => m.BlockModule), }, { path: 'docs', @@ -209,17 +200,9 @@ let routes: Routes = [ }, { path: 'block', - data: { networkSpecific: true }, component: StartComponent, - children: [ - { - path: ':id', - component: BlockComponent, - data: { - ogImage: true - } - }, - ], + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('./components/block/block.module').then(m => m.BlockModule), }, { path: 'docs', @@ -319,17 +302,9 @@ let routes: Routes = [ }, { path: 'block', - data: { networkSpecific: true }, component: StartComponent, - children: [ - { - path: ':id', - component: BlockComponent, - data: { - ogImage: true - } - }, - ], + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('./components/block/block.module').then(m => m.BlockModule), }, { path: 'docs', @@ -466,17 +441,9 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: 'block', - data: { networkSpecific: true }, component: StartComponent, - children: [ - { - path: ':id', - component: BlockComponent, - data: { - ogImage: true - } - }, - ], + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('./components/block/block.module').then(m => m.BlockModule), }, { path: 'assets', @@ -584,17 +551,9 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: 'block', - data: { networkSpecific: true }, component: StartComponent, - children: [ - { - path: ':id', - component: BlockComponent, - data: { - ogImage: true - } - }, - ], + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('./components/block/block.module').then(m => m.BlockModule), }, { path: 'assets', diff --git a/frontend/src/app/components/block/block.module.ts b/frontend/src/app/components/block/block.module.ts new file mode 100644 index 000000000..d6991c68a --- /dev/null +++ b/frontend/src/app/components/block/block.module.ts @@ -0,0 +1,43 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Routes, RouterModule } from '@angular/router'; +import { BlockComponent } from './block.component'; +import { SharedModule } from '../../shared/shared.module'; + +const routes: Routes = [ + { + path: ':id', + component: BlockComponent, + data: { + ogImage: true + } + } +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes) + ], + exports: [ + RouterModule + ] +}) +export class BlockRoutingModule { } + +@NgModule({ + imports: [ + CommonModule, + BlockRoutingModule, + SharedModule, + ], + declarations: [ + BlockComponent, + ] +}) +export class BlockModule { } + + + + + + diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index dce65bfae..fb10a9e19 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -47,7 +47,6 @@ import { CapAddressPipe } from './pipes/cap-address-pipe/cap-address-pipe'; import { StartComponent } from '../components/start/start.component'; import { TransactionComponent } from '../components/transaction/transaction.component'; import { TransactionsListComponent } from '../components/transactions-list/transactions-list.component'; -import { BlockComponent } from '../components/block/block.component'; import { BlockOverviewGraphComponent } from '../components/block-overview-graph/block-overview-graph.component'; import { BlockOverviewTooltipComponent } from '../components/block-overview-tooltip/block-overview-tooltip.component'; import { AddressComponent } from '../components/address/address.component'; @@ -149,7 +148,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir LiquidMasterPageComponent, StartComponent, TransactionComponent, - BlockComponent, BlockOverviewGraphComponent, BlockOverviewTooltipComponent, TransactionsListComponent, @@ -276,7 +274,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir AmountComponent, StartComponent, TransactionComponent, - BlockComponent, BlockOverviewGraphComponent, BlockOverviewTooltipComponent, TransactionsListComponent, From 0cf898a19ff71771aef35e63f2caf38612bac1e5 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 9 Nov 2023 07:05:37 +0000 Subject: [PATCH 35/79] Split transaction components into separate module --- frontend/src/app/app-routing.module.ts | 46 ++++--------------- .../transaction/transaction.module.ts | 45 ++++++++++++++++++ .../tx-bowtie-graph/tx-bowtie.module.ts | 28 +++++++++++ frontend/src/app/previews.module.ts | 2 + frontend/src/app/shared/shared.module.ts | 9 ---- 5 files changed, 85 insertions(+), 45 deletions(-) create mode 100644 frontend/src/app/components/transaction/transaction.module.ts create mode 100644 frontend/src/app/components/tx-bowtie-graph/tx-bowtie.module.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index cbf38c69d..479080af5 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { AppPreloadingStrategy } from './app.preloading-strategy' import { StartComponent } from './components/start/start.component'; -import { TransactionComponent } from './components/transaction/transaction.component'; import { BlockViewComponent } from './components/block-view/block-view.component'; import { MempoolBlockViewComponent } from './components/mempool-block-view/mempool-block-view.component'; import { ClockComponent } from './components/clock/clock.component'; @@ -88,13 +87,8 @@ let routes: Routes = [ { path: 'tx', component: StartComponent, - data: { networkSpecific: true }, - children: [ - { - path: ':id', - component: TransactionComponent - }, - ], + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('./components/transaction/transaction.module').then(m => m.TransactionModule), }, { path: 'block', @@ -189,14 +183,9 @@ let routes: Routes = [ }, { path: 'tx', - data: { networkSpecific: true }, component: StartComponent, - children: [ - { - path: ':id', - component: TransactionComponent - }, - ], + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('./components/transaction/transaction.module').then(m => m.TransactionModule), }, { path: 'block', @@ -291,14 +280,9 @@ let routes: Routes = [ }, { path: 'tx', - data: { networkSpecific: true }, component: StartComponent, - children: [ - { - path: ':id', - component: TransactionComponent - }, - ], + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('./components/transaction/transaction.module').then(m => m.TransactionModule), }, { path: 'block', @@ -430,14 +414,9 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: 'tx', - data: { networkSpecific: true }, component: StartComponent, - children: [ - { - path: ':id', - component: TransactionComponent - }, - ], + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('./components/transaction/transaction.module').then(m => m.TransactionModule), }, { path: 'block', @@ -540,14 +519,9 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: 'tx', - data: { networkSpecific: true }, component: StartComponent, - children: [ - { - path: ':id', - component: TransactionComponent - }, - ], + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('./components/transaction/transaction.module').then(m => m.TransactionModule), }, { path: 'block', diff --git a/frontend/src/app/components/transaction/transaction.module.ts b/frontend/src/app/components/transaction/transaction.module.ts new file mode 100644 index 000000000..d933cc350 --- /dev/null +++ b/frontend/src/app/components/transaction/transaction.module.ts @@ -0,0 +1,45 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Routes, RouterModule } from '@angular/router'; +import { TransactionComponent } from './transaction.component'; +import { SharedModule } from '../../shared/shared.module'; +import { TxBowtieModule } from '../tx-bowtie-graph/tx-bowtie.module'; + +const routes: Routes = [ + { + path: ':id', + component: TransactionComponent, + data: { + ogImage: true + } + } +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes) + ], + exports: [ + RouterModule + ] +}) +export class TransactionRoutingModule { } + +@NgModule({ + imports: [ + CommonModule, + TransactionRoutingModule, + SharedModule, + TxBowtieModule, + ], + declarations: [ + TransactionComponent, + ] +}) +export class TransactionModule { } + + + + + + diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie.module.ts b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie.module.ts new file mode 100644 index 000000000..617425e7a --- /dev/null +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '../../shared/shared.module'; +import { TxBowtieGraphComponent } from '../tx-bowtie-graph/tx-bowtie-graph.component'; +import { TxBowtieGraphTooltipComponent } from '../tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component'; + + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + ], + declarations: [ + TxBowtieGraphComponent, + TxBowtieGraphTooltipComponent, + ], + exports: [ + TxBowtieGraphComponent, + TxBowtieGraphTooltipComponent, + ] +}) +export class TxBowtieModule { } + + + + + + diff --git a/frontend/src/app/previews.module.ts b/frontend/src/app/previews.module.ts index 2e8dbdc75..95124f232 100644 --- a/frontend/src/app/previews.module.ts +++ b/frontend/src/app/previews.module.ts @@ -9,6 +9,7 @@ import { BlockPreviewComponent } from './components/block/block-preview.componen import { AddressPreviewComponent } from './components/address/address-preview.component'; import { PoolPreviewComponent } from './components/pool/pool-preview.component'; import { MasterPagePreviewComponent } from './components/master-page-preview/master-page-preview.component'; +import { TxBowtieModule } from './components/tx-bowtie-graph/tx-bowtie.module'; @NgModule({ declarations: [ TransactionPreviewComponent, @@ -23,6 +24,7 @@ import { MasterPagePreviewComponent } from './components/master-page-preview/mas RouterModule, PreviewsRoutingModule, GraphsModule, + TxBowtieModule, ], }) export class PreviewsModule { } diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index fb10a9e19..76ed41477 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -45,7 +45,6 @@ import { AmountComponent } from '../components/amount/amount.component'; import { RouterModule } from '@angular/router'; import { CapAddressPipe } from './pipes/cap-address-pipe/cap-address-pipe'; import { StartComponent } from '../components/start/start.component'; -import { TransactionComponent } from '../components/transaction/transaction.component'; import { TransactionsListComponent } from '../components/transactions-list/transactions-list.component'; import { BlockOverviewGraphComponent } from '../components/block-overview-graph/block-overview-graph.component'; import { BlockOverviewTooltipComponent } from '../components/block-overview-tooltip/block-overview-tooltip.component'; @@ -64,8 +63,6 @@ import { DifficultyMiningComponent } from '../components/difficulty-mining/diffi import { TermsOfServiceComponent } from '../components/terms-of-service/terms-of-service.component'; import { RbfTimelineComponent } from '../components/rbf-timeline/rbf-timeline.component'; import { RbfTimelineTooltipComponent } from '../components/rbf-timeline/rbf-timeline-tooltip.component'; -import { TxBowtieGraphComponent } from '../components/tx-bowtie-graph/tx-bowtie-graph.component'; -import { TxBowtieGraphTooltipComponent } from '../components/tx-bowtie-graph-tooltip/tx-bowtie-graph-tooltip.component'; import { PrivacyPolicyComponent } from '../components/privacy-policy/privacy-policy.component'; import { TrademarkPolicyComponent } from '../components/trademark-policy/trademark-policy.component'; import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component'; @@ -147,7 +144,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir BisqMasterPageComponent, LiquidMasterPageComponent, StartComponent, - TransactionComponent, BlockOverviewGraphComponent, BlockOverviewTooltipComponent, TransactionsListComponent, @@ -164,8 +160,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir DifficultyTooltipComponent, RbfTimelineComponent, RbfTimelineTooltipComponent, - TxBowtieGraphComponent, - TxBowtieGraphTooltipComponent, TermsOfServiceComponent, PrivacyPolicyComponent, TrademarkPolicyComponent, @@ -273,7 +267,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir BlockchainBlocksComponent, AmountComponent, StartComponent, - TransactionComponent, BlockOverviewGraphComponent, BlockOverviewTooltipComponent, TransactionsListComponent, @@ -290,8 +283,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir DifficultyTooltipComponent, RbfTimelineComponent, RbfTimelineTooltipComponent, - TxBowtieGraphComponent, - TxBowtieGraphTooltipComponent, TermsOfServiceComponent, PrivacyPolicyComponent, TrademarkPolicyComponent, From ae02ff5b0dbdf88e7288b02fb41eea482a836c3c Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 9 Nov 2023 07:06:09 +0000 Subject: [PATCH 36/79] Split About component into separate module --- frontend/src/app/app-routing.module.ts | 11 +++-- frontend/src/app/bisq/bisq.routing.module.ts | 3 +- .../src/app/components/about/about.module.ts | 40 +++++++++++++++++++ frontend/src/app/shared/shared.module.ts | 2 - 4 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 frontend/src/app/components/about/about.module.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 479080af5..8faa16add 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -7,7 +7,6 @@ import { MempoolBlockViewComponent } from './components/mempool-block-view/mempo import { ClockComponent } from './components/clock/clock.component'; import { AddressComponent } from './components/address/address.component'; import { MasterPageComponent } from './components/master-page/master-page.component'; -import { AboutComponent } from './components/about/about.component'; import { StatusViewComponent } from './components/status-view/status-view.component'; import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component'; import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component'; @@ -53,7 +52,7 @@ let routes: Routes = [ }, { path: 'about', - component: AboutComponent, + loadChildren: () => import('./components/about/about.module').then(m => m.AboutModule), }, { path: 'blocks', @@ -150,7 +149,7 @@ let routes: Routes = [ }, { path: 'about', - component: AboutComponent, + loadChildren: () => import('./components/about/about.module').then(m => m.AboutModule), }, { path: 'blocks', @@ -243,7 +242,7 @@ let routes: Routes = [ }, { path: 'about', - component: AboutComponent, + loadChildren: () => import('./components/about/about.module').then(m => m.AboutModule), }, { path: 'blocks', @@ -385,7 +384,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: 'about', - component: AboutComponent, + loadChildren: () => import('./components/about/about.module').then(m => m.AboutModule), }, { path: 'blocks', @@ -490,7 +489,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: 'about', - component: AboutComponent, + loadChildren: () => import('./components/about/about.module').then(m => m.AboutModule), }, { path: 'blocks', diff --git a/frontend/src/app/bisq/bisq.routing.module.ts b/frontend/src/app/bisq/bisq.routing.module.ts index 11acdca2a..b211afeaf 100644 --- a/frontend/src/app/bisq/bisq.routing.module.ts +++ b/frontend/src/app/bisq/bisq.routing.module.ts @@ -1,6 +1,5 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { AboutComponent } from '../components/about/about.component'; import { BisqTransactionsComponent } from './bisq-transactions/bisq-transactions.component'; import { BisqTransactionComponent } from './bisq-transaction/bisq-transaction.component'; import { BisqBlockComponent } from './bisq-block/bisq-block.component'; @@ -64,7 +63,7 @@ const routes: Routes = [ }, { path: 'about', - component: AboutComponent, + loadChildren: () => import('../components/about/about.module').then(m => m.AboutModule), }, { path: 'docs', diff --git a/frontend/src/app/components/about/about.module.ts b/frontend/src/app/components/about/about.module.ts new file mode 100644 index 000000000..1eb471f14 --- /dev/null +++ b/frontend/src/app/components/about/about.module.ts @@ -0,0 +1,40 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Routes, RouterModule } from '@angular/router'; +import { AboutComponent } from './about.component'; +import { SharedModule } from '../../shared/shared.module'; + +const routes: Routes = [ + { + path: '', + component: AboutComponent, + } +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes) + ], + exports: [ + RouterModule + ] +}) +export class AboutRoutingModule { } + +@NgModule({ + imports: [ + CommonModule, + AboutRoutingModule, + SharedModule, + ], + declarations: [ + AboutComponent, + ] +}) +export class AboutModule { } + + + + + + diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 76ed41477..1e1fe42b3 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -11,7 +11,6 @@ import { MenuComponent } from '../components/menu/menu.component'; import { PreviewTitleComponent } from '../components/master-page-preview/preview-title.component'; import { BisqMasterPageComponent } from '../components/bisq-master-page/bisq-master-page.component'; import { LiquidMasterPageComponent } from '../components/liquid-master-page/liquid-master-page.component'; -import { AboutComponent } from '../components/about/about.component'; import { VbytesPipe } from './pipes/bytes-pipe/vbytes.pipe'; import { ShortenStringPipe } from './pipes/shorten-string-pipe/shorten-string.pipe'; import { CeilPipe } from './pipes/math-ceil/math-ceil.pipe'; @@ -137,7 +136,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir MempoolBlocksComponent, BlockchainBlocksComponent, AmountComponent, - AboutComponent, MasterPageComponent, MenuComponent, PreviewTitleComponent, From 854a9dd05718833e240127935a2759558a2b36b1 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 9 Nov 2023 07:53:46 +0000 Subject: [PATCH 37/79] Split legal page components into separate modules --- frontend/src/app/app-routing.module.ts | 33 +++++++-------- frontend/src/app/bisq/bisq.routing.module.ts | 3 +- .../privacy-policy/privacy-policy.module.ts | 40 +++++++++++++++++++ .../terms-of-service.module.ts | 40 +++++++++++++++++++ .../trademark-policy.module.ts | 40 +++++++++++++++++++ frontend/src/app/shared/shared.module.ts | 9 ----- 6 files changed, 136 insertions(+), 29 deletions(-) create mode 100644 frontend/src/app/components/privacy-policy/privacy-policy.module.ts create mode 100644 frontend/src/app/components/terms-of-service/terms-of-service.module.ts create mode 100644 frontend/src/app/components/trademark-policy/trademark-policy.module.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 8faa16add..2ebb1c74b 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -8,9 +8,6 @@ import { ClockComponent } from './components/clock/clock.component'; import { AddressComponent } from './components/address/address.component'; import { MasterPageComponent } from './components/master-page/master-page.component'; import { StatusViewComponent } from './components/status-view/status-view.component'; -import { TermsOfServiceComponent } from './components/terms-of-service/terms-of-service.component'; -import { PrivacyPolicyComponent } from './components/privacy-policy/privacy-policy.component'; -import { TrademarkPolicyComponent } from './components/trademark-policy/trademark-policy.component'; import { BisqMasterPageComponent } from './components/bisq-master-page/bisq-master-page.component'; import { PushTransactionComponent } from './components/push-transaction/push-transaction.component'; import { BlocksList } from './components/blocks-list/blocks-list.component'; @@ -64,15 +61,15 @@ let routes: Routes = [ }, { path: 'terms-of-service', - component: TermsOfServiceComponent + loadChildren: () => import('./components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), }, { path: 'privacy-policy', - component: PrivacyPolicyComponent + loadChildren: () => import('./components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), }, { path: 'trademark-policy', - component: TrademarkPolicyComponent + loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), }, { path: 'address/:id', @@ -161,15 +158,15 @@ let routes: Routes = [ }, { path: 'terms-of-service', - component: TermsOfServiceComponent + loadChildren: () => import('./components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), }, { path: 'privacy-policy', - component: PrivacyPolicyComponent + loadChildren: () => import('./components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), }, { path: 'trademark-policy', - component: TrademarkPolicyComponent + loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), }, { path: 'address/:id', @@ -258,15 +255,15 @@ let routes: Routes = [ }, { path: 'terms-of-service', - component: TermsOfServiceComponent + loadChildren: () => import('./components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), }, { path: 'privacy-policy', - component: PrivacyPolicyComponent + loadChildren: () => import('./components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), }, { path: 'trademark-policy', - component: TrademarkPolicyComponent + loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), }, { path: 'address/:id', @@ -392,15 +389,15 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: 'terms-of-service', - component: TermsOfServiceComponent + loadChildren: () => import('./components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), }, { path: 'privacy-policy', - component: PrivacyPolicyComponent + loadChildren: () => import('./components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), }, { path: 'trademark-policy', - component: TrademarkPolicyComponent + loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), }, { path: 'address/:id', @@ -497,15 +494,15 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: 'terms-of-service', - component: TermsOfServiceComponent + loadChildren: () => import('./components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), }, { path: 'privacy-policy', - component: PrivacyPolicyComponent + loadChildren: () => import('./components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), }, { path: 'trademark-policy', - component: TrademarkPolicyComponent + loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), }, { path: 'address/:id', diff --git a/frontend/src/app/bisq/bisq.routing.module.ts b/frontend/src/app/bisq/bisq.routing.module.ts index b211afeaf..beea6b018 100644 --- a/frontend/src/app/bisq/bisq.routing.module.ts +++ b/frontend/src/app/bisq/bisq.routing.module.ts @@ -9,7 +9,6 @@ import { BisqStatsComponent } from './bisq-stats/bisq-stats.component'; import { BisqDashboardComponent } from './bisq-dashboard/bisq-dashboard.component'; import { BisqMarketComponent } from './bisq-market/bisq-market.component'; import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dashboard.component'; -import { TermsOfServiceComponent } from '../components/terms-of-service/terms-of-service.component'; import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component'; const routes: Routes = [ @@ -75,7 +74,7 @@ const routes: Routes = [ }, { path: 'terms-of-service', - component: TermsOfServiceComponent + loadChildren: () => import('../components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), }, { path: '**', diff --git a/frontend/src/app/components/privacy-policy/privacy-policy.module.ts b/frontend/src/app/components/privacy-policy/privacy-policy.module.ts new file mode 100644 index 000000000..6d279d80a --- /dev/null +++ b/frontend/src/app/components/privacy-policy/privacy-policy.module.ts @@ -0,0 +1,40 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Routes, RouterModule } from '@angular/router'; +import { PrivacyPolicyComponent } from './privacy-policy.component'; +import { SharedModule } from '../../shared/shared.module'; + +const routes: Routes = [ + { + path: '', + component: PrivacyPolicyComponent, + } +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes) + ], + exports: [ + RouterModule + ] +}) +export class PrivacyPolicyRoutingModule { } + +@NgModule({ + imports: [ + CommonModule, + PrivacyPolicyRoutingModule, + SharedModule, + ], + declarations: [ + PrivacyPolicyComponent, + ] +}) +export class PrivacyPolicyModule { } + + + + + + diff --git a/frontend/src/app/components/terms-of-service/terms-of-service.module.ts b/frontend/src/app/components/terms-of-service/terms-of-service.module.ts new file mode 100644 index 000000000..2ab139d8b --- /dev/null +++ b/frontend/src/app/components/terms-of-service/terms-of-service.module.ts @@ -0,0 +1,40 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Routes, RouterModule } from '@angular/router'; +import { TermsOfServiceComponent } from './terms-of-service.component'; +import { SharedModule } from '../../shared/shared.module'; + +const routes: Routes = [ + { + path: '', + component: TermsOfServiceComponent, + } +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes) + ], + exports: [ + RouterModule + ] +}) +export class TermsModule { } + +@NgModule({ + imports: [ + CommonModule, + TermsModule, + SharedModule, + ], + declarations: [ + TermsOfServiceComponent, + ] +}) +export class TermsOfServiceModule { } + + + + + + diff --git a/frontend/src/app/components/trademark-policy/trademark-policy.module.ts b/frontend/src/app/components/trademark-policy/trademark-policy.module.ts new file mode 100644 index 000000000..24f70be52 --- /dev/null +++ b/frontend/src/app/components/trademark-policy/trademark-policy.module.ts @@ -0,0 +1,40 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Routes, RouterModule } from '@angular/router'; +import { TrademarkPolicyComponent } from './trademark-policy.component'; +import { SharedModule } from '../../shared/shared.module'; + +const routes: Routes = [ + { + path: '', + component: TrademarkPolicyComponent, + } +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes) + ], + exports: [ + RouterModule + ] +}) +export class TrademarkRoutingModule { } + +@NgModule({ + imports: [ + CommonModule, + TrademarkRoutingModule, + SharedModule, + ], + declarations: [ + TrademarkPolicyComponent, + ] +}) +export class TrademarkModule { } + + + + + + diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 1e1fe42b3..82f9b5d00 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -59,11 +59,8 @@ import { FeesBoxComponent } from '../components/fees-box/fees-box.component'; import { DifficultyComponent } from '../components/difficulty/difficulty.component'; import { DifficultyTooltipComponent } from '../components/difficulty/difficulty-tooltip.component'; import { DifficultyMiningComponent } from '../components/difficulty-mining/difficulty-mining.component'; -import { TermsOfServiceComponent } from '../components/terms-of-service/terms-of-service.component'; import { RbfTimelineComponent } from '../components/rbf-timeline/rbf-timeline.component'; import { RbfTimelineTooltipComponent } from '../components/rbf-timeline/rbf-timeline-tooltip.component'; -import { PrivacyPolicyComponent } from '../components/privacy-policy/privacy-policy.component'; -import { TrademarkPolicyComponent } from '../components/trademark-policy/trademark-policy.component'; import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component'; import { AssetsFeaturedComponent } from '../components/assets/assets-featured/assets-featured.component'; import { AssetGroupComponent } from '../components/assets/asset-group/asset-group.component'; @@ -158,9 +155,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir DifficultyTooltipComponent, RbfTimelineComponent, RbfTimelineTooltipComponent, - TermsOfServiceComponent, - PrivacyPolicyComponent, - TrademarkPolicyComponent, PushTransactionComponent, AssetsNavComponent, AssetsFeaturedComponent, @@ -281,9 +275,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir DifficultyTooltipComponent, RbfTimelineComponent, RbfTimelineTooltipComponent, - TermsOfServiceComponent, - PrivacyPolicyComponent, - TrademarkPolicyComponent, PushTransactionComponent, AssetsNavComponent, AssetsFeaturedComponent, From 80bf2f9ebd3daeae019bab74438d140cfe9082ef Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 9 Nov 2023 08:22:53 +0000 Subject: [PATCH 38/79] Split master page components & deduplicate main routes --- frontend/src/app/app-routing.module.ts | 393 +----------------- frontend/src/app/bisq/bisq.module.ts | 2 + frontend/src/app/bisq/bisq.routing.module.ts | 143 ++++--- .../app/liquid/liquid-master-page.module.ts | 124 ++++++ frontend/src/app/master-page.module.ts | 119 ++++++ frontend/src/app/shared/shared.module.ts | 8 +- 6 files changed, 329 insertions(+), 460 deletions(-) create mode 100644 frontend/src/app/liquid/liquid-master-page.module.ts create mode 100644 frontend/src/app/master-page.module.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 2ebb1c74b..4159f4184 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -6,19 +6,14 @@ import { BlockViewComponent } from './components/block-view/block-view.component import { MempoolBlockViewComponent } from './components/mempool-block-view/mempool-block-view.component'; import { ClockComponent } from './components/clock/clock.component'; import { AddressComponent } from './components/address/address.component'; -import { MasterPageComponent } from './components/master-page/master-page.component'; import { StatusViewComponent } from './components/status-view/status-view.component'; -import { BisqMasterPageComponent } from './components/bisq-master-page/bisq-master-page.component'; import { PushTransactionComponent } from './components/push-transaction/push-transaction.component'; import { BlocksList } from './components/blocks-list/blocks-list.component'; -import { RbfList } from './components/rbf-list/rbf-list.component'; -import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component'; import { AssetGroupComponent } from './components/assets/asset-group/asset-group.component'; import { AssetsFeaturedComponent } from './components/assets/assets-featured/assets-featured.component'; import { AssetsComponent } from './components/assets/assets.component'; import { AssetComponent } from './components/asset/asset.component'; import { AssetsNavComponent } from './components/assets/assets-nav/assets-nav.component'; -import { CalculatorComponent } from './components/calculator/calculator.component'; const browserWindow = window || {}; // @ts-ignore @@ -36,77 +31,8 @@ let routes: Routes = [ }, { path: '', - component: MasterPageComponent, - children: [ - { - path: 'mining/blocks', - redirectTo: 'blocks', - pathMatch: 'full' - }, - { - path: 'tx/push', - component: PushTransactionComponent, - }, - { - path: 'about', - loadChildren: () => import('./components/about/about.module').then(m => m.AboutModule), - }, - { - path: 'blocks', - component: BlocksList, - }, - { - path: 'rbf', - component: RbfList, - }, - { - path: 'terms-of-service', - loadChildren: () => import('./components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), - }, - { - path: 'privacy-policy', - loadChildren: () => import('./components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), - }, - { - path: 'trademark-policy', - loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), - }, - { - path: 'address/:id', - children: [], - component: AddressComponent, - data: { - ogImage: true, - networkSpecific: true, - } - }, - { - path: 'tx', - component: StartComponent, - data: { preload: true, networkSpecific: true }, - loadChildren: () => import('./components/transaction/transaction.module').then(m => m.TransactionModule), - }, - { - path: 'block', - component: StartComponent, - data: { preload: true, networkSpecific: true }, - loadChildren: () => import('./components/block/block.module').then(m => m.BlockModule), - }, - { - path: 'docs', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule), - data: { preload: true }, - }, - { - path: 'api', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'lightning', - loadChildren: () => import('./lightning/lightning.module').then(m => m.LightningModule), - data: { preload: browserWindowEnv && browserWindowEnv.LIGHTNING === true, networks: ['bitcoin'] }, - }, - ], + loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule), + data: { preload: true }, }, { path: 'status', @@ -138,71 +64,8 @@ let routes: Routes = [ }, { path: '', - component: MasterPageComponent, - children: [ - { - path: 'tx/push', - component: PushTransactionComponent, - }, - { - path: 'about', - loadChildren: () => import('./components/about/about.module').then(m => m.AboutModule), - }, - { - path: 'blocks', - component: BlocksList, - }, - { - path: 'rbf', - component: RbfList, - }, - { - path: 'terms-of-service', - loadChildren: () => import('./components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), - }, - { - path: 'privacy-policy', - loadChildren: () => import('./components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), - }, - { - path: 'trademark-policy', - loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), - }, - { - path: 'address/:id', - children: [], - component: AddressComponent, - data: { - ogImage: true, - networkSpecific: true, - } - }, - { - path: 'tx', - component: StartComponent, - data: { preload: true, networkSpecific: true }, - loadChildren: () => import('./components/transaction/transaction.module').then(m => m.TransactionModule), - }, - { - path: 'block', - component: StartComponent, - data: { preload: true, networkSpecific: true }, - loadChildren: () => import('./components/block/block.module').then(m => m.BlockModule), - }, - { - path: 'docs', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'api', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'lightning', - data: { networks: ['bitcoin'] }, - loadChildren: () => import('./lightning/lightning.module').then(m => m.LightningModule) - }, - ], + loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule), + data: { preload: true }, }, { path: 'status', @@ -226,80 +89,8 @@ let routes: Routes = [ }, { path: '', - component: MasterPageComponent, - children: [ - { - path: 'mining/blocks', - redirectTo: 'blocks', - pathMatch: 'full' - }, - { - path: 'tx/push', - component: PushTransactionComponent, - }, - { - path: 'about', - loadChildren: () => import('./components/about/about.module').then(m => m.AboutModule), - }, - { - path: 'blocks', - component: BlocksList, - }, - { - path: 'rbf', - component: RbfList, - }, - { - path: 'tools/calculator', - component: CalculatorComponent - }, - { - path: 'terms-of-service', - loadChildren: () => import('./components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), - }, - { - path: 'privacy-policy', - loadChildren: () => import('./components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), - }, - { - path: 'trademark-policy', - loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), - }, - { - path: 'address/:id', - children: [], - component: AddressComponent, - data: { - ogImage: true, - networkSpecific: true, - } - }, - { - path: 'tx', - component: StartComponent, - data: { preload: true, networkSpecific: true }, - loadChildren: () => import('./components/transaction/transaction.module').then(m => m.TransactionModule), - }, - { - path: 'block', - component: StartComponent, - data: { preload: true, networkSpecific: true }, - loadChildren: () => import('./components/block/block.module').then(m => m.BlockModule), - }, - { - path: 'docs', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'api', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'lightning', - data: { networks: ['bitcoin'] }, - loadChildren: () => import('./lightning/lightning.module').then(m => m.LightningModule) - }, - ], + loadChildren: () => import('./master-page.module').then(m => m.MasterPageModule), + data: { preload: true }, }, { path: 'preview', @@ -356,7 +147,6 @@ let routes: Routes = [ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'bisq') { routes = [{ path: '', - component: BisqMasterPageComponent, loadChildren: () => import('./bisq/bisq.module').then(m => m.BisqModule) }]; } @@ -373,88 +163,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: '', - component: LiquidMasterPageComponent, - children: [ - { - path: 'tx/push', - component: PushTransactionComponent, - }, - { - path: 'about', - loadChildren: () => import('./components/about/about.module').then(m => m.AboutModule), - }, - { - path: 'blocks', - component: BlocksList, - }, - { - path: 'terms-of-service', - loadChildren: () => import('./components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), - }, - { - path: 'privacy-policy', - loadChildren: () => import('./components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), - }, - { - path: 'trademark-policy', - loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), - }, - { - path: 'address/:id', - children: [], - component: AddressComponent, - data: { - ogImage: true, - networkSpecific: true, - } - }, - { - path: 'tx', - component: StartComponent, - data: { preload: true, networkSpecific: true }, - loadChildren: () => import('./components/transaction/transaction.module').then(m => m.TransactionModule), - }, - { - path: 'block', - component: StartComponent, - data: { preload: true, networkSpecific: true }, - loadChildren: () => import('./components/block/block.module').then(m => m.BlockModule), - }, - { - path: 'assets', - data: { networks: ['liquid'] }, - component: AssetsNavComponent, - children: [ - { - path: 'all', - data: { networks: ['liquid'] }, - component: AssetsComponent, - }, - { - path: 'asset/:id', - data: { networkSpecific: true }, - component: AssetComponent - }, - { - path: 'group/:id', - data: { networkSpecific: true }, - component: AssetGroupComponent - }, - { - path: '**', - redirectTo: 'all' - } - ] - }, - { - path: 'docs', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'api', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - ], + loadChildren: () => import ('./liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule) }, { path: 'status', @@ -478,93 +187,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: '', - component: LiquidMasterPageComponent, - children: [ - { - path: 'tx/push', - component: PushTransactionComponent, - }, - { - path: 'about', - loadChildren: () => import('./components/about/about.module').then(m => m.AboutModule), - }, - { - path: 'blocks', - component: BlocksList, - }, - { - path: 'terms-of-service', - loadChildren: () => import('./components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), - }, - { - path: 'privacy-policy', - loadChildren: () => import('./components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), - }, - { - path: 'trademark-policy', - loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), - }, - { - path: 'address/:id', - children: [], - component: AddressComponent, - data: { - ogImage: true, - networkSpecific: true, - } - }, - { - path: 'tx', - component: StartComponent, - data: { preload: true, networkSpecific: true }, - loadChildren: () => import('./components/transaction/transaction.module').then(m => m.TransactionModule), - }, - { - path: 'block', - component: StartComponent, - data: { preload: true, networkSpecific: true }, - loadChildren: () => import('./components/block/block.module').then(m => m.BlockModule), - }, - { - path: 'assets', - data: { networks: ['liquid'] }, - component: AssetsNavComponent, - children: [ - { - path: 'featured', - data: { networkSpecific: true }, - component: AssetsFeaturedComponent, - }, - { - path: 'all', - data: { networks: ['liquid'] }, - component: AssetsComponent, - }, - { - path: 'asset/:id', - data: { networkSpecific: true }, - component: AssetComponent - }, - { - path: 'group/:id', - data: { networkSpecific: true }, - component: AssetGroupComponent - }, - { - path: '**', - redirectTo: 'featured' - } - ] - }, - { - path: 'docs', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'api', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) - }, - ], + loadChildren: () => import ('./liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule) }, { path: 'preview', diff --git a/frontend/src/app/bisq/bisq.module.ts b/frontend/src/app/bisq/bisq.module.ts index 93658d95a..f7f71156b 100644 --- a/frontend/src/app/bisq/bisq.module.ts +++ b/frontend/src/app/bisq/bisq.module.ts @@ -27,9 +27,11 @@ import { AutofocusDirective } from '../components/ngx-bootstrap-multiselect/auto import { MultiSelectSearchFilter } from '../components/ngx-bootstrap-multiselect/search-filter.pipe'; import { OffClickDirective } from '../components/ngx-bootstrap-multiselect/off-click.directive'; import { NgxDropdownMultiselectComponent } from '../components/ngx-bootstrap-multiselect/ngx-bootstrap-multiselect.component'; +import { BisqMasterPageComponent } from '../components/bisq-master-page/bisq-master-page.component'; @NgModule({ declarations: [ + BisqMasterPageComponent, BisqTransactionsComponent, BisqTransactionComponent, BisqBlockComponent, diff --git a/frontend/src/app/bisq/bisq.routing.module.ts b/frontend/src/app/bisq/bisq.routing.module.ts index beea6b018..7c6d2ee1b 100644 --- a/frontend/src/app/bisq/bisq.routing.module.ts +++ b/frontend/src/app/bisq/bisq.routing.module.ts @@ -1,5 +1,6 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { BisqMasterPageComponent } from '../components/bisq-master-page/bisq-master-page.component'; import { BisqTransactionsComponent } from './bisq-transactions/bisq-transactions.component'; import { BisqTransactionComponent } from './bisq-transaction/bisq-transaction.component'; import { BisqBlockComponent } from './bisq-block/bisq-block.component'; @@ -12,74 +13,80 @@ import { BisqMainDashboardComponent } from './bisq-main-dashboard/bisq-main-dash import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component'; const routes: Routes = [ - { - path: '', - component: BisqMainDashboardComponent, - }, - { - path: 'markets', - data: { networks: ['bisq'] }, - component: BisqDashboardComponent, - }, - { - path: 'transactions', - data: { networks: ['bisq'] }, - component: BisqTransactionsComponent - }, - { - path: 'market/:pair', - data: { networkSpecific: true }, - component: BisqMarketComponent, - }, - { - path: 'tx/push', - component: PushTransactionComponent, - }, - { - path: 'tx/:id', - data: { networkSpecific: true }, - component: BisqTransactionComponent - }, - { - path: 'blocks', - children: [], - component: BisqBlocksComponent - }, - { - path: 'block/:id', - data: { networkSpecific: true }, - component: BisqBlockComponent, - }, - { - path: 'address/:id', - data: { networkSpecific: true }, - component: BisqAddressComponent, - }, - { - path: 'stats', - data: { networks: ['bisq'] }, - component: BisqStatsComponent, - }, - { - path: 'about', - loadChildren: () => import('../components/about/about.module').then(m => m.AboutModule), - }, - { - path: 'docs', - loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'api', - loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) - }, - { - path: 'terms-of-service', - loadChildren: () => import('../components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), - }, - { - path: '**', - redirectTo: '' - } + { + path: '', + component: BisqMasterPageComponent, + children: [ + { + path: '', + component: BisqMainDashboardComponent, + }, + { + path: 'markets', + data: { networks: ['bisq'] }, + component: BisqDashboardComponent, + }, + { + path: 'transactions', + data: { networks: ['bisq'] }, + component: BisqTransactionsComponent + }, + { + path: 'market/:pair', + data: { networkSpecific: true }, + component: BisqMarketComponent, + }, + { + path: 'tx/push', + component: PushTransactionComponent, + }, + { + path: 'tx/:id', + data: { networkSpecific: true }, + component: BisqTransactionComponent + }, + { + path: 'blocks', + children: [], + component: BisqBlocksComponent + }, + { + path: 'block/:id', + data: { networkSpecific: true }, + component: BisqBlockComponent, + }, + { + path: 'address/:id', + data: { networkSpecific: true }, + component: BisqAddressComponent, + }, + { + path: 'stats', + data: { networks: ['bisq'] }, + component: BisqStatsComponent, + }, + { + path: 'about', + loadChildren: () => import('../components/about/about.module').then(m => m.AboutModule), + }, + { + path: 'docs', + loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) + }, + { + path: 'api', + loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) + }, + { + path: 'terms-of-service', + loadChildren: () => import('../components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), + }, + { + path: '**', + redirectTo: '' + } + ] + } ]; @NgModule({ diff --git a/frontend/src/app/liquid/liquid-master-page.module.ts b/frontend/src/app/liquid/liquid-master-page.module.ts new file mode 100644 index 000000000..37c6e0708 --- /dev/null +++ b/frontend/src/app/liquid/liquid-master-page.module.ts @@ -0,0 +1,124 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Routes, RouterModule } from '@angular/router'; +import { SharedModule } from '../shared/shared.module'; +import { LiquidMasterPageComponent } from '../components/liquid-master-page/liquid-master-page.component'; + +import { StartComponent } from '../components/start/start.component'; +import { AddressComponent } from '../components/address/address.component'; +import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component'; +import { BlocksList } from '../components/blocks-list/blocks-list.component'; +import { AssetGroupComponent } from '../components/assets/asset-group/asset-group.component'; +import { AssetsComponent } from '../components/assets/assets.component'; +import { AssetComponent } from '../components/asset/asset.component'; +import { AssetsNavComponent } from '../components/assets/assets-nav/assets-nav.component'; + +const routes: Routes = [ + { + path: '', + component: LiquidMasterPageComponent, + children: [ + { + path: 'tx/push', + component: PushTransactionComponent, + }, + { + path: 'about', + loadChildren: () => import('../components/about/about.module').then(m => m.AboutModule), + }, + { + path: 'blocks', + component: BlocksList, + }, + { + path: 'terms-of-service', + loadChildren: () => import('../components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), + }, + { + path: 'privacy-policy', + loadChildren: () => import('../components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), + }, + { + path: 'trademark-policy', + loadChildren: () => import('../components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), + }, + { + path: 'address/:id', + children: [], + component: AddressComponent, + data: { + ogImage: true, + networkSpecific: true, + } + }, + { + path: 'tx', + component: StartComponent, + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('../components/transaction/transaction.module').then(m => m.TransactionModule), + }, + { + path: 'block', + component: StartComponent, + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('../components/block/block.module').then(m => m.BlockModule), + }, + { + path: 'assets', + data: { networks: ['liquid'] }, + component: AssetsNavComponent, + children: [ + { + path: 'all', + data: { networks: ['liquid'] }, + component: AssetsComponent, + }, + { + path: 'asset/:id', + data: { networkSpecific: true }, + component: AssetComponent + }, + { + path: 'group/:id', + data: { networkSpecific: true }, + component: AssetGroupComponent + }, + { + path: '**', + redirectTo: 'all' + } + ] + }, + { + path: 'docs', + loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) + }, + { + path: 'api', + loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) + }, + ], + }, +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes) + ], + exports: [ + RouterModule + ] +}) +export class LiquidRoutingModule { } + +@NgModule({ + imports: [ + CommonModule, + LiquidRoutingModule, + SharedModule, + ], + declarations: [ + LiquidMasterPageComponent, + ] +}) +export class LiquidMasterPageModule { } \ No newline at end of file diff --git a/frontend/src/app/master-page.module.ts b/frontend/src/app/master-page.module.ts new file mode 100644 index 000000000..5bd808b94 --- /dev/null +++ b/frontend/src/app/master-page.module.ts @@ -0,0 +1,119 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Routes, RouterModule } from '@angular/router'; +import { MasterPageComponent } from './components/master-page/master-page.component'; +import { SharedModule } from './shared/shared.module'; + +import { StartComponent } from './components/start/start.component'; +import { AddressComponent } from './components/address/address.component'; +import { PushTransactionComponent } from './components/push-transaction/push-transaction.component'; +import { BlocksList } from './components/blocks-list/blocks-list.component'; +import { RbfList } from './components/rbf-list/rbf-list.component'; + +const browserWindow = window || {}; +// @ts-ignore +const browserWindowEnv = browserWindow.__env || {}; + +const routes: Routes = [ + { + component: MasterPageComponent, + children: [ + { + path: 'mining/blocks', + redirectTo: 'blocks', + pathMatch: 'full' + }, + { + path: 'tx/push', + component: PushTransactionComponent, + }, + { + path: 'about', + loadChildren: () => import('./components/about/about.module').then(m => m.AboutModule), + }, + { + path: 'blocks', + component: BlocksList, + }, + { + path: 'rbf', + component: RbfList, + }, + { + path: 'terms-of-service', + loadChildren: () => import('./components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule), + }, + { + path: 'privacy-policy', + loadChildren: () => import('./components/privacy-policy/privacy-policy.module').then(m => m.PrivacyPolicyModule), + }, + { + path: 'trademark-policy', + loadChildren: () => import('./components/trademark-policy/trademark-policy.module').then(m => m.TrademarkModule), + }, + { + path: 'address/:id', + children: [], + component: AddressComponent, + data: { + ogImage: true, + networkSpecific: true, + } + }, + { + path: 'tx', + component: StartComponent, + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('./components/transaction/transaction.module').then(m => m.TransactionModule), + }, + { + path: 'block', + component: StartComponent, + data: { preload: true, networkSpecific: true }, + loadChildren: () => import('./components/block/block.module').then(m => m.BlockModule), + }, + { + path: 'docs', + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule), + data: { preload: true }, + }, + { + path: 'api', + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule) + }, + { + path: 'lightning', + loadChildren: () => import('./lightning/lightning.module').then(m => m.LightningModule), + data: { preload: browserWindowEnv && browserWindowEnv.LIGHTNING === true, networks: ['bitcoin'] }, + }, + ], + } +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes) + ], + exports: [ + RouterModule + ] +}) +export class MasterPageRoutingModule { } + +@NgModule({ + imports: [ + CommonModule, + MasterPageRoutingModule, + SharedModule, + ], + declarations: [ + MasterPageComponent, + ] +}) +export class MasterPageModule { } + + + + + + diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 82f9b5d00..76dbc65f1 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -6,11 +6,8 @@ import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, fa faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faClock, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft, faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck, faCircleCheck, faUserCircle } from '@fortawesome/free-solid-svg-icons'; import { InfiniteScrollModule } from 'ngx-infinite-scroll'; -import { MasterPageComponent } from '../components/master-page/master-page.component'; import { MenuComponent } from '../components/menu/menu.component'; import { PreviewTitleComponent } from '../components/master-page-preview/preview-title.component'; -import { BisqMasterPageComponent } from '../components/bisq-master-page/bisq-master-page.component'; -import { LiquidMasterPageComponent } from '../components/liquid-master-page/liquid-master-page.component'; import { VbytesPipe } from './pipes/bytes-pipe/vbytes.pipe'; import { ShortenStringPipe } from './pipes/shorten-string-pipe/shorten-string.pipe'; import { CeilPipe } from './pipes/math-ceil/math-ceil.pipe'; @@ -133,11 +130,8 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir MempoolBlocksComponent, BlockchainBlocksComponent, AmountComponent, - MasterPageComponent, MenuComponent, PreviewTitleComponent, - BisqMasterPageComponent, - LiquidMasterPageComponent, StartComponent, BlockOverviewGraphComponent, BlockOverviewTooltipComponent, @@ -217,7 +211,6 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir AmountShortenerPipe, ], exports: [ - MasterPageComponent, MenuComponent, RouterModule, ReactiveFormsModule, @@ -297,6 +290,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir ConfirmationsComponent, ToggleComponent, GeolocationComponent, + TestnetAlertComponent, PreviewTitleComponent, GlobalFooterComponent, AcceleratePreviewComponent, From db8ed5b70521a843934c4c1b7baccde2a07c6335 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 10 Nov 2023 03:03:16 +0000 Subject: [PATCH 39/79] Split liquid, lightning & mainnet graphs --- frontend/src/app/app-routing.module.ts | 29 ++++------ frontend/src/app/bitcoin-graphs.module.ts | 36 ++++++++++++ .../src/app/graphs/graphs.routing.module.ts | 43 +------------- .../src/app/graphs/lightning-graphs.module.ts | 58 +++++++++++++++++++ .../src/app/liquid/liquid-graphs.module.ts | 36 ++++++++++++ frontend/src/app/master-page.module.ts | 1 + 6 files changed, 143 insertions(+), 60 deletions(-) create mode 100644 frontend/src/app/bitcoin-graphs.module.ts create mode 100644 frontend/src/app/graphs/lightning-graphs.module.ts create mode 100644 frontend/src/app/liquid/liquid-graphs.module.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 4159f4184..4ee5b81a2 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -1,19 +1,10 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { AppPreloadingStrategy } from './app.preloading-strategy' -import { StartComponent } from './components/start/start.component'; import { BlockViewComponent } from './components/block-view/block-view.component'; import { MempoolBlockViewComponent } from './components/mempool-block-view/mempool-block-view.component'; import { ClockComponent } from './components/clock/clock.component'; -import { AddressComponent } from './components/address/address.component'; import { StatusViewComponent } from './components/status-view/status-view.component'; -import { PushTransactionComponent } from './components/push-transaction/push-transaction.component'; -import { BlocksList } from './components/blocks-list/blocks-list.component'; -import { AssetGroupComponent } from './components/assets/asset-group/asset-group.component'; -import { AssetsFeaturedComponent } from './components/assets/assets-featured/assets-featured.component'; -import { AssetsComponent } from './components/assets/assets.component'; -import { AssetComponent } from './components/asset/asset.component'; -import { AssetsNavComponent } from './components/assets/assets-nav/assets-nav.component'; const browserWindow = window || {}; // @ts-ignore @@ -26,7 +17,7 @@ let routes: Routes = [ { path: '', pathMatch: 'full', - loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule), + loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), data: { preload: true }, }, { @@ -41,7 +32,7 @@ let routes: Routes = [ }, { path: '', - loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), }, { path: '**', @@ -60,7 +51,7 @@ let routes: Routes = [ { path: '', pathMatch: 'full', - loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), }, { path: '', @@ -74,7 +65,7 @@ let routes: Routes = [ }, { path: '', - loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), }, { path: '**', @@ -85,7 +76,7 @@ let routes: Routes = [ { path: '', pathMatch: 'full', - loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), }, { path: '', @@ -136,7 +127,7 @@ let routes: Routes = [ }, { path: '', - loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), }, { path: '**', @@ -159,7 +150,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { { path: '', pathMatch: 'full', - loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), }, { path: '', @@ -172,7 +163,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: '', - loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), }, { path: '**', @@ -183,7 +174,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { { path: '', pathMatch: 'full', - loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), }, { path: '', @@ -209,7 +200,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { }, { path: '', - loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), }, { path: '**', diff --git a/frontend/src/app/bitcoin-graphs.module.ts b/frontend/src/app/bitcoin-graphs.module.ts new file mode 100644 index 000000000..bff9bd632 --- /dev/null +++ b/frontend/src/app/bitcoin-graphs.module.ts @@ -0,0 +1,36 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Routes, RouterModule } from '@angular/router'; +import { MasterPageComponent } from './components/master-page/master-page.component'; + +const routes: Routes = [ + { + path: '', + component: MasterPageComponent, + loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + } +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes) + ], + exports: [ + RouterModule + ] +}) +export class BitcoinGraphsRoutingModule { } + +@NgModule({ + imports: [ + CommonModule, + BitcoinGraphsRoutingModule, + ], +}) +export class BitcoinGraphsModule { } + + + + + + diff --git a/frontend/src/app/graphs/graphs.routing.module.ts b/frontend/src/app/graphs/graphs.routing.module.ts index 03800dcfc..28a2ed260 100644 --- a/frontend/src/app/graphs/graphs.routing.module.ts +++ b/frontend/src/app/graphs/graphs.routing.module.ts @@ -8,8 +8,6 @@ import { BlockSizesWeightsGraphComponent } from '../components/block-sizes-weigh import { GraphsComponent } from '../components/graphs/graphs.component'; import { HashrateChartComponent } from '../components/hashrate-chart/hashrate-chart.component'; import { HashrateChartPoolsComponent } from '../components/hashrates-chart-pools/hashrate-chart-pools.component'; -import { LiquidMasterPageComponent } from '../components/liquid-master-page/liquid-master-page.component'; -import { MasterPageComponent } from '../components/master-page/master-page.component'; import { MempoolBlockComponent } from '../components/mempool-block/mempool-block.component'; import { MiningDashboardComponent } from '../components/mining-dashboard/mining-dashboard.component'; import { PoolRankingComponent } from '../components/pool-ranking/pool-ranking.component'; @@ -18,22 +16,10 @@ import { StartComponent } from '../components/start/start.component'; import { StatisticsComponent } from '../components/statistics/statistics.component'; import { TelevisionComponent } from '../components/television/television.component'; import { DashboardComponent } from '../dashboard/dashboard.component'; -import { NodesNetworksChartComponent } from '../lightning/nodes-networks-chart/nodes-networks-chart.component'; -import { LightningStatisticsChartComponent } from '../lightning/statistics-chart/lightning-statistics-chart.component'; -import { NodesPerISPChartComponent } from '../lightning/nodes-per-isp-chart/nodes-per-isp-chart.component'; -import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component'; -import { NodesMap } from '../lightning/nodes-map/nodes-map.component'; -import { NodesChannelsMap } from '../lightning/nodes-channels-map/nodes-channels-map.component'; - -const browserWindow = window || {}; -// @ts-ignore -const browserWindowEnv = browserWindow.__env || {}; -const isLiquid = browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid'; const routes: Routes = [ { path: '', - component: isLiquid ? LiquidMasterPageComponent : MasterPageComponent, children: [ { path: 'mining/pool/:slug', @@ -108,34 +94,9 @@ const routes: Routes = [ component: BlockSizesWeightsGraphComponent, }, { - path: 'lightning/nodes-networks', + path: 'lightning', data: { networks: ['bitcoin'] }, - component: NodesNetworksChartComponent, - }, - { - path: 'lightning/capacity', - data: { networks: ['bitcoin'] }, - component: LightningStatisticsChartComponent, - }, - { - path: 'lightning/nodes-per-isp', - data: { networks: ['bitcoin'] }, - component: NodesPerISPChartComponent, - }, - { - path: 'lightning/nodes-per-country', - data: { networks: ['bitcoin'] }, - component: NodesPerCountryChartComponent, - }, - { - path: 'lightning/nodes-map', - data: { networks: ['bitcoin'] }, - component: NodesMap, - }, - { - path: 'lightning/nodes-channels-map', - data: { networks: ['bitcoin'] }, - component: NodesChannelsMap, + loadChildren: () => import ('./lightning-graphs.module').then(m => m.LightningGraphsModule) }, { path: '', diff --git a/frontend/src/app/graphs/lightning-graphs.module.ts b/frontend/src/app/graphs/lightning-graphs.module.ts new file mode 100644 index 000000000..ac123be33 --- /dev/null +++ b/frontend/src/app/graphs/lightning-graphs.module.ts @@ -0,0 +1,58 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from '../shared/shared.module'; +import { CommonModule } from '@angular/common'; +import { RouterModule, Routes } from '@angular/router'; +import { NodesNetworksChartComponent } from '../lightning/nodes-networks-chart/nodes-networks-chart.component'; +import { LightningStatisticsChartComponent } from '../lightning/statistics-chart/lightning-statistics-chart.component'; +import { NodesPerISPChartComponent } from '../lightning/nodes-per-isp-chart/nodes-per-isp-chart.component'; +import { NodesPerCountryChartComponent } from '../lightning/nodes-per-country-chart/nodes-per-country-chart.component'; +import { NodesMap } from '../lightning/nodes-map/nodes-map.component'; +import { NodesChannelsMap } from '../lightning/nodes-channels-map/nodes-channels-map.component'; + +const routes: Routes = [ + { + path: 'nodes-networks', + data: { networks: ['bitcoin'] }, + component: NodesNetworksChartComponent, + }, + { + path: 'capacity', + data: { networks: ['bitcoin'] }, + component: LightningStatisticsChartComponent, + }, + { + path: 'nodes-per-isp', + data: { networks: ['bitcoin'] }, + component: NodesPerISPChartComponent, + }, + { + path: 'nodes-per-country', + data: { networks: ['bitcoin'] }, + component: NodesPerCountryChartComponent, + }, + { + path: 'nodes-map', + data: { networks: ['bitcoin'] }, + component: NodesMap, + }, + { + path: 'nodes-channels-map', + data: { networks: ['bitcoin'] }, + component: NodesChannelsMap, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class LightningGraphsRoutingModule { } + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + LightningGraphsRoutingModule, + ], +}) +export class LightningGraphsModule { } diff --git a/frontend/src/app/liquid/liquid-graphs.module.ts b/frontend/src/app/liquid/liquid-graphs.module.ts new file mode 100644 index 000000000..0878bb773 --- /dev/null +++ b/frontend/src/app/liquid/liquid-graphs.module.ts @@ -0,0 +1,36 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Routes, RouterModule } from '@angular/router'; +import { LiquidMasterPageComponent } from '../components/liquid-master-page/liquid-master-page.component'; + +const routes: Routes = [ + { + path: '', + component: LiquidMasterPageComponent, + loadChildren: () => import('../graphs/graphs.module').then(m => m.GraphsModule) + } +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes) + ], + exports: [ + RouterModule + ] +}) +export class LiquidGraphsRoutingModule { } + +@NgModule({ + imports: [ + CommonModule, + LiquidGraphsRoutingModule, + ], +}) +export class LiquidGraphsModule { } + + + + + + diff --git a/frontend/src/app/master-page.module.ts b/frontend/src/app/master-page.module.ts index 5bd808b94..bfc1aed53 100644 --- a/frontend/src/app/master-page.module.ts +++ b/frontend/src/app/master-page.module.ts @@ -16,6 +16,7 @@ const browserWindowEnv = browserWindow.__env || {}; const routes: Routes = [ { + path: '', component: MasterPageComponent, children: [ { From af7b9c0dc8915d05d2c099589f477308573b3f45 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 10 Nov 2023 04:58:07 +0000 Subject: [PATCH 40/79] Adjust webpack chunk preloading strategy --- frontend/src/app/app-routing.module.ts | 15 +++++++++++++-- frontend/src/app/bitcoin-graphs.module.ts | 3 ++- frontend/src/app/graphs/graphs.routing.module.ts | 4 ++-- frontend/src/app/liquid/liquid-graphs.module.ts | 3 ++- .../src/app/liquid/liquid-master-page.module.ts | 3 ++- frontend/src/app/previews.routing.module.ts | 3 ++- 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 4ee5b81a2..ce91019ff 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -33,6 +33,7 @@ let routes: Routes = [ { path: '', loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), + data: { preload: true }, }, { path: '**', @@ -52,6 +53,7 @@ let routes: Routes = [ path: '', pathMatch: 'full', loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), + data: { preload: true }, }, { path: '', @@ -66,6 +68,7 @@ let routes: Routes = [ { path: '', loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), + data: { preload: true }, }, { path: '**', @@ -77,6 +80,7 @@ let routes: Routes = [ path: '', pathMatch: 'full', loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), + data: { preload: true }, }, { path: '', @@ -128,6 +132,7 @@ let routes: Routes = [ { path: '', loadChildren: () => import('./bitcoin-graphs.module').then(m => m.BitcoinGraphsModule), + data: { preload: true }, }, { path: '**', @@ -151,10 +156,12 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { path: '', pathMatch: 'full', loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), + data: { preload: true }, }, { path: '', - loadChildren: () => import ('./liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule) + loadChildren: () => import ('./liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule), + data: { preload: true }, }, { path: 'status', @@ -164,6 +171,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { { path: '', loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), + data: { preload: true }, }, { path: '**', @@ -175,10 +183,12 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { path: '', pathMatch: 'full', loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), + data: { preload: true }, }, { path: '', - loadChildren: () => import ('./liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule) + loadChildren: () => import ('./liquid/liquid-master-page.module').then(m => m.LiquidMasterPageModule), + data: { preload: true }, }, { path: 'preview', @@ -201,6 +211,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') { { path: '', loadChildren: () => import('./liquid/liquid-graphs.module').then(m => m.LiquidGraphsModule), + data: { preload: true }, }, { path: '**', diff --git a/frontend/src/app/bitcoin-graphs.module.ts b/frontend/src/app/bitcoin-graphs.module.ts index bff9bd632..710743245 100644 --- a/frontend/src/app/bitcoin-graphs.module.ts +++ b/frontend/src/app/bitcoin-graphs.module.ts @@ -7,7 +7,8 @@ const routes: Routes = [ { path: '', component: MasterPageComponent, - loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule) + loadChildren: () => import('./graphs/graphs.module').then(m => m.GraphsModule), + data: { preload: true }, } ]; diff --git a/frontend/src/app/graphs/graphs.routing.module.ts b/frontend/src/app/graphs/graphs.routing.module.ts index 28a2ed260..346bcf7f1 100644 --- a/frontend/src/app/graphs/graphs.routing.module.ts +++ b/frontend/src/app/graphs/graphs.routing.module.ts @@ -95,8 +95,8 @@ const routes: Routes = [ }, { path: 'lightning', - data: { networks: ['bitcoin'] }, - loadChildren: () => import ('./lightning-graphs.module').then(m => m.LightningGraphsModule) + data: { preload: true, networks: ['bitcoin'] }, + loadChildren: () => import ('./lightning-graphs.module').then(m => m.LightningGraphsModule), }, { path: '', diff --git a/frontend/src/app/liquid/liquid-graphs.module.ts b/frontend/src/app/liquid/liquid-graphs.module.ts index 0878bb773..3da93fc9d 100644 --- a/frontend/src/app/liquid/liquid-graphs.module.ts +++ b/frontend/src/app/liquid/liquid-graphs.module.ts @@ -7,7 +7,8 @@ const routes: Routes = [ { path: '', component: LiquidMasterPageComponent, - loadChildren: () => import('../graphs/graphs.module').then(m => m.GraphsModule) + loadChildren: () => import('../graphs/graphs.module').then(m => m.GraphsModule), + data: { preload: true }, } ]; diff --git a/frontend/src/app/liquid/liquid-master-page.module.ts b/frontend/src/app/liquid/liquid-master-page.module.ts index 37c6e0708..10d87bc4b 100644 --- a/frontend/src/app/liquid/liquid-master-page.module.ts +++ b/frontend/src/app/liquid/liquid-master-page.module.ts @@ -91,7 +91,8 @@ const routes: Routes = [ }, { path: 'docs', - loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule) + loadChildren: () => import('../docs/docs.module').then(m => m.DocsModule), + data: { preload: true }, }, { path: 'api', diff --git a/frontend/src/app/previews.routing.module.ts b/frontend/src/app/previews.routing.module.ts index c2ad8db5f..6ac44a370 100644 --- a/frontend/src/app/previews.routing.module.ts +++ b/frontend/src/app/previews.routing.module.ts @@ -31,7 +31,8 @@ const routes: Routes = [ }, { path: 'lightning', - loadChildren: () => import('./lightning/lightning-previews.module').then(m => m.LightningPreviewsModule) + loadChildren: () => import('./lightning/lightning-previews.module').then(m => m.LightningPreviewsModule), + data: { preload: true }, }, ], } From e08665dee5cc92ca7ed68a31bd16ac35a8f5bc17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 Nov 2023 02:47:53 +0000 Subject: [PATCH 41/79] Bump cypress from 13.3.0 to 13.5.0 in /frontend Bumps [cypress](https://github.com/cypress-io/cypress) from 13.3.0 to 13.5.0. - [Release notes](https://github.com/cypress-io/cypress/releases) - [Changelog](https://github.com/cypress-io/cypress/blob/develop/CHANGELOG.md) - [Commits](https://github.com/cypress-io/cypress/compare/v13.3.0...v13.5.0) --- updated-dependencies: - dependency-name: cypress dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 47 +++++++------------------------------- frontend/package.json | 2 +- 2 files changed, 9 insertions(+), 40 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 49410de87..c29a0f316 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -31,9 +31,9 @@ "bootstrap": "~4.6.2", "browserify": "^17.0.0", "clipboard": "^2.0.11", + "cypress": "^13.5.0", "domino": "^2.1.6", "echarts": "~5.4.3", - "echarts-gl": "^2.0.9", "lightweight-charts": "~3.8.0", "ngx-echarts": "~16.0.0", "ngx-infinite-scroll": "^16.0.0", @@ -59,7 +59,7 @@ "optionalDependencies": { "@cypress/schematic": "^2.5.0", "@types/cypress": "^1.1.3", - "cypress": "^13.3.0", + "cypress": "^13.5.0", "cypress-fail-on-console-error": "~5.0.0", "cypress-wait-until": "^2.0.1", "mock-socket": "~9.2.1", @@ -6429,11 +6429,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/claygl": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz", - "integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==" - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -7153,9 +7148,9 @@ "peer": true }, "node_modules/cypress": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.3.0.tgz", - "integrity": "sha512-mpI8qcTwLGiA4zEQvTC/U1xGUezVV4V8HQCOYjlEOrVmU1etVvxOjkCXHGwrlYdZU/EPmUiWfsO3yt1o+Q2bgw==", + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.5.0.tgz", + "integrity": "sha512-oh6U7h9w8wwHfzNDJQ6wVcAeXu31DlIYlNOBvfd6U4CcB8oe4akawQmH+QJVOMZlM42eBoCne015+svVqdwdRQ==", "hasInstallScript": true, "optional": true, "dependencies": { @@ -7825,18 +7820,6 @@ "zrender": "5.4.4" } }, - "node_modules/echarts-gl": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-2.0.9.tgz", - "integrity": "sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==", - "dependencies": { - "claygl": "^1.2.1", - "zrender": "^5.1.1" - }, - "peerDependencies": { - "echarts": "^5.1.2" - } - }, "node_modules/echarts/node_modules/tslib": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", @@ -21550,11 +21533,6 @@ "safe-buffer": "^5.0.1" } }, - "claygl": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz", - "integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==" - }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -22118,9 +22096,9 @@ "peer": true }, "cypress": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.3.0.tgz", - "integrity": "sha512-mpI8qcTwLGiA4zEQvTC/U1xGUezVV4V8HQCOYjlEOrVmU1etVvxOjkCXHGwrlYdZU/EPmUiWfsO3yt1o+Q2bgw==", + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.5.0.tgz", + "integrity": "sha512-oh6U7h9w8wwHfzNDJQ6wVcAeXu31DlIYlNOBvfd6U4CcB8oe4akawQmH+QJVOMZlM42eBoCne015+svVqdwdRQ==", "optional": true, "requires": { "@cypress/request": "^3.0.0", @@ -22659,15 +22637,6 @@ } } }, - "echarts-gl": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-2.0.9.tgz", - "integrity": "sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==", - "requires": { - "claygl": "^1.2.1", - "zrender": "^5.1.1" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 0c7874c30..984cba3de 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -110,7 +110,7 @@ "optionalDependencies": { "@cypress/schematic": "^2.5.0", "@types/cypress": "^1.1.3", - "cypress": "^13.3.0", + "cypress": "^13.5.0", "cypress-fail-on-console-error": "~5.0.0", "cypress-wait-until": "^2.0.1", "mock-socket": "~9.2.1", From 0e420d819673e0816ea93d9e5975e7e804433b62 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 16 Oct 2023 01:00:18 +0000 Subject: [PATCH 42/79] Fix rbf tree leak, clean up stale trees in Redis --- backend/src/api/rbf-cache.ts | 25 ++++++++++++++++++++++--- backend/src/api/redis-cache.ts | 2 +- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/backend/src/api/rbf-cache.ts b/backend/src/api/rbf-cache.ts index b5592252c..6ca38eb15 100644 --- a/backend/src/api/rbf-cache.ts +++ b/backend/src/api/rbf-cache.ts @@ -53,6 +53,9 @@ class RbfCache { private expiring: Map = new Map(); private cacheQueue: CacheEvent[] = []; + private evictionCount = 0; + private staleCount = 0; + constructor() { setInterval(this.cleanup.bind(this), 1000 * 60 * 10); } @@ -245,6 +248,7 @@ class RbfCache { // flag a transaction as removed from the mempool public evict(txid: string, fast: boolean = false): void { + this.evictionCount++; if (this.txs.has(txid) && (fast || !this.expiring.has(txid))) { const expiryTime = fast ? Date.now() + (1000 * 60 * 10) : Date.now() + (1000 * 86400); // 24 hours this.addExpiration(txid, expiryTime); @@ -272,18 +276,23 @@ class RbfCache { this.remove(txid); } } - logger.debug(`rbf cache contains ${this.txs.size} txs, ${this.rbfTrees.size} trees, ${this.expiring.size} due to expire`); + logger.debug(`rbf cache contains ${this.txs.size} txs, ${this.rbfTrees.size} trees, ${this.expiring.size} due to expire (${this.evictionCount} newly expired)`); + this.evictionCount = 0; } // remove a transaction & all previous versions from the cache private remove(txid): void { // don't remove a transaction if a newer version remains in the mempool if (!this.replacedBy.has(txid)) { + const root = this.treeMap.get(txid); const replaces = this.replaces.get(txid); this.replaces.delete(txid); this.treeMap.delete(txid); this.removeTx(txid); this.removeExpiration(txid); + if (root === txid) { + this.removeTree(txid); + } for (const tx of (replaces || [])) { // recursively remove prior versions from the cache this.replacedBy.delete(tx); @@ -360,8 +369,9 @@ class RbfCache { public async load({ txs, trees, expiring }): Promise { txs.forEach(txEntry => { - this.txs.set(txEntry.key, txEntry.value); + this.txs.set(txEntry.value.txid, txEntry.value); }); + this.staleCount = 0; for (const deflatedTree of trees) { await this.importTree(deflatedTree.root, deflatedTree.root, deflatedTree, this.txs); } @@ -370,6 +380,8 @@ class RbfCache { this.expiring.set(expiringEntry.key, new Date(expiringEntry.value).getTime()); } }); + logger.debug(`loaded ${txs.length} txs, ${trees.length} trees into rbf cache, ${expiring.length} due to expire, ${this.staleCount} were stale`); + this.staleCount = 0; this.cleanup(); } @@ -398,6 +410,13 @@ class RbfCache { const treeInfo = deflated[txid]; const replaces: RbfTree[] = []; + // if the root tx is unknown, remove this tree and return early + if (root === txid && !txs.has(txid)) { + this.staleCount++; + this.removeTree(deflated.key); + return; + } + // check if any transactions in this tree have already been confirmed mined = mined || treeInfo.mined; let exists = mined; @@ -413,7 +432,7 @@ class RbfCache { this.evict(txid, true); } } catch (e) { - // most transactions do not exist + // most transactions only exist in our cache } } diff --git a/backend/src/api/redis-cache.ts b/backend/src/api/redis-cache.ts index fcde8013a..00b280274 100644 --- a/backend/src/api/redis-cache.ts +++ b/backend/src/api/redis-cache.ts @@ -219,7 +219,7 @@ class RedisCache { await memPool.$setMempool(loadedMempool); await rbfCache.load({ txs: rbfTxs, - trees: rbfTrees.map(loadedTree => loadedTree.value), + trees: rbfTrees.map(loadedTree => { loadedTree.value.key = loadedTree.key; return loadedTree.value; }), expiring: rbfExpirations, }); } From e3e248d601b03e6175a7fee3a5667ee00119d262 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 11 Nov 2023 05:52:37 +0000 Subject: [PATCH 43/79] Convert RBF disk cache data to match new format --- backend/src/api/disk-cache.ts | 6 +++++- backend/src/api/rbf-cache.ts | 32 ++++++++++++++++++-------------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/backend/src/api/disk-cache.ts b/backend/src/api/disk-cache.ts index 6f603489a..093f07f0d 100644 --- a/backend/src/api/disk-cache.ts +++ b/backend/src/api/disk-cache.ts @@ -252,7 +252,11 @@ class DiskCache { } if (rbfData?.rbf) { - rbfCache.load(rbfData.rbf); + rbfCache.load({ + txs: rbfData.rbf.txs.map(([txid, entry]) => ({ value: entry })), + trees: rbfData.rbf.trees, + expiring: rbfData.rbf.expiring.map(([txid, value]) => ({ key: txid, value })), + }); } } catch (e) { logger.warn('Failed to parse rbf cache. Skipping. Reason: ' + (e instanceof Error ? e.message : e)); diff --git a/backend/src/api/rbf-cache.ts b/backend/src/api/rbf-cache.ts index 6ca38eb15..6e1f37afb 100644 --- a/backend/src/api/rbf-cache.ts +++ b/backend/src/api/rbf-cache.ts @@ -368,21 +368,25 @@ class RbfCache { } public async load({ txs, trees, expiring }): Promise { - txs.forEach(txEntry => { - this.txs.set(txEntry.value.txid, txEntry.value); - }); - this.staleCount = 0; - for (const deflatedTree of trees) { - await this.importTree(deflatedTree.root, deflatedTree.root, deflatedTree, this.txs); - } - expiring.forEach(expiringEntry => { - if (this.txs.has(expiringEntry.key)) { - this.expiring.set(expiringEntry.key, new Date(expiringEntry.value).getTime()); + try { + txs.forEach(txEntry => { + this.txs.set(txEntry.value.txid, txEntry.value); + }); + this.staleCount = 0; + for (const deflatedTree of trees) { + await this.importTree(deflatedTree.root, deflatedTree.root, deflatedTree, this.txs); } - }); - logger.debug(`loaded ${txs.length} txs, ${trees.length} trees into rbf cache, ${expiring.length} due to expire, ${this.staleCount} were stale`); - this.staleCount = 0; - this.cleanup(); + expiring.forEach(expiringEntry => { + if (this.txs.has(expiringEntry.key)) { + this.expiring.set(expiringEntry.key, new Date(expiringEntry.value).getTime()); + } + }); + logger.debug(`loaded ${txs.length} txs, ${trees.length} trees into rbf cache, ${expiring.length} due to expire, ${this.staleCount} were stale`); + this.staleCount = 0; + this.cleanup(); + } catch (e) { + logger.err('failed to restore RBF cache: ' + (e instanceof Error ? e.message : e)); + } } exportTree(tree: RbfTree, deflated: any = null) { From 3f0c3c1952176cf98ea866666637d937aee41d12 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 11 Nov 2023 07:16:59 +0000 Subject: [PATCH 44/79] Add apiService cachedRequest function, apply to outspends requests --- .../transactions-list.component.ts | 10 ++++- .../tx-bowtie-graph.component.ts | 2 +- frontend/src/app/services/api.service.ts | 44 ++++++++++++++++++- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index c49ff0e3c..9541864fc 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -6,7 +6,7 @@ import { Outspend, Transaction, Vin, Vout } from '../../interfaces/electrs.inter import { ElectrsApiService } from '../../services/electrs-api.service'; import { environment } from '../../../environments/environment'; import { AssetsService } from '../../services/assets.service'; -import { filter, map, tap, switchMap, shareReplay } from 'rxjs/operators'; +import { filter, map, tap, switchMap, shareReplay, catchError } from 'rxjs/operators'; import { BlockExtended } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { PriceService } from '../../services/price.service'; @@ -53,6 +53,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { private assetsService: AssetsService, private ref: ChangeDetectorRef, private priceService: PriceService, + private cd: ChangeDetectorRef, ) { } ngOnInit(): void { @@ -75,7 +76,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { for (let i = 0; i < txIds.length; i += 50) { batches.push(txIds.slice(i, i + 50)); } - return forkJoin(batches.map(batch => this.apiService.getOutspendsBatched$(batch))); + return forkJoin(batches.map(batch => { return this.apiService.cachedRequest(this.apiService.getOutspendsBatched$, 5000, batch); })); } else { return of([]); } @@ -90,6 +91,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { outspends.forEach((outspend, i) => { transactions[i]._outspends = outspend; }); + this.cd.markForCheck(); }), ), this.stateService.utxoSpent$ @@ -108,6 +110,10 @@ export class TransactionsListComponent implements OnInit, OnChanges { .pipe( filter(() => this.stateService.env.LIGHTNING), switchMap((txIds) => this.apiService.getChannelByTxIds$(txIds)), + catchError((error) => { + // handle 404 + return of([]); + }), tap((channels) => { if (!this.transactions) { return; diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts index 97e74957e..1dafb327f 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts @@ -123,7 +123,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { .pipe( switchMap((txid) => { if (!this.cached) { - return this.apiService.getOutspendsBatched$([txid]); + return this.apiService.cachedRequest(this.apiService.getOutspendsBatched$, 5000, [txid]); } else { return of(null); } diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index 744474f9d..046b27812 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators, PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit } from '../interfaces/node-api.interface'; -import { Observable, of } from 'rxjs'; +import { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs'; import { StateService } from './state.service'; import { IBackendInfo, WebsocketResponse } from '../interfaces/websocket.interface'; import { Outspend, Transaction } from '../interfaces/electrs.interface'; @@ -20,6 +20,8 @@ export class ApiService { private apiBaseUrl: string; // base URL is protocol, hostname, and port private apiBasePath: string; // network path is /testnet, etc. or '' for mainnet + private requestCache = new Map, expiry: number }>; + constructor( private httpClient: HttpClient, private stateService: StateService, @@ -44,6 +46,46 @@ export class ApiService { } } + private generateCacheKey(functionName: string, params: any[]): string { + return functionName + JSON.stringify(params); + } + + // delete expired cache entries + private cleanExpiredCache(): void { + this.requestCache.forEach((value, key) => { + if (value.expiry < Date.now()) { + this.requestCache.delete(key); + } + }); + } + + cachedRequest Observable>( + apiFunction: F, + expireAfter: number, // in ms + ...params: Parameters + ): Observable { + this.cleanExpiredCache(); + + const cacheKey = this.generateCacheKey(apiFunction.name, params); + if (!this.requestCache.has(cacheKey)) { + const subject = new BehaviorSubject(null); + this.requestCache.set(cacheKey, { subject, expiry: Date.now() + expireAfter }); + + apiFunction.bind(this)(...params).pipe( + tap(data => { + subject.next(data as T); + }), + catchError((error) => { + subject.error(error); + return of(null); + }), + shareReplay(1), + ).subscribe(); + } + + return this.requestCache.get(cacheKey).subject.asObservable().pipe(filter(val => val !== null), take(1)); + } + list2HStatistics$(): Observable { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/statistics/2h'); } From 6740ab61f3509ed0d64d11bea5d3a83f3475b8b4 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 11 Nov 2023 07:46:25 +0000 Subject: [PATCH 45/79] Reduce outspend API cache expiry to 250ms --- .../transactions-list/transactions-list.component.ts | 5 ++--- .../components/tx-bowtie-graph/tx-bowtie-graph.component.ts | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index 9541864fc..05d74a75d 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -53,7 +53,6 @@ export class TransactionsListComponent implements OnInit, OnChanges { private assetsService: AssetsService, private ref: ChangeDetectorRef, private priceService: PriceService, - private cd: ChangeDetectorRef, ) { } ngOnInit(): void { @@ -76,7 +75,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { for (let i = 0; i < txIds.length; i += 50) { batches.push(txIds.slice(i, i + 50)); } - return forkJoin(batches.map(batch => { return this.apiService.cachedRequest(this.apiService.getOutspendsBatched$, 5000, batch); })); + return forkJoin(batches.map(batch => { return this.apiService.cachedRequest(this.apiService.getOutspendsBatched$, 250, batch); })); } else { return of([]); } @@ -91,7 +90,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { outspends.forEach((outspend, i) => { transactions[i]._outspends = outspend; }); - this.cd.markForCheck(); + this.ref.markForCheck(); }), ), this.stateService.utxoSpent$ diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts index 1dafb327f..3bc352a35 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts @@ -123,7 +123,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { .pipe( switchMap((txid) => { if (!this.cached) { - return this.apiService.cachedRequest(this.apiService.getOutspendsBatched$, 5000, [txid]); + return this.apiService.cachedRequest(this.apiService.getOutspendsBatched$, 250, [txid]); } else { return of(null); } From ffd2685922289807fb154ee44bfbb9e6cc0f3d5a Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 15 Oct 2023 23:23:49 +0000 Subject: [PATCH 46/79] Display fee distribution labels to 3 sig figs --- .../fee-distribution-graph.component.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts index 010466952..79d89ef46 100644 --- a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts +++ b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts @@ -135,7 +135,8 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr formatter: (value: number): string => { const unitValue = this.weightMode ? value / 4 : value; const selectedPowerOfTen = selectPowerOfTen(unitValue); - const newVal = Math.round(unitValue / selectedPowerOfTen.divider); + const scaledValue = unitValue / selectedPowerOfTen.divider; + const newVal = scaledValue >= 100 ? Math.round(scaledValue) : scaledValue.toPrecision(3); return `${newVal}${selectedPowerOfTen.unit}`; }, }, @@ -155,7 +156,8 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr const value = label.data[1]; const unitValue = this.weightMode ? value / 4 : value; const selectedPowerOfTen = selectPowerOfTen(unitValue); - const newVal = Math.round(unitValue / selectedPowerOfTen.divider); + const scaledValue = unitValue / selectedPowerOfTen.divider; + const newVal = scaledValue >= 100 ? Math.round(scaledValue) : scaledValue.toPrecision(3); return `${newVal}${selectedPowerOfTen.unit}`; } }, From b5a5f0f6080ba1e139bf47647a5d362e1a12edf1 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 15 Oct 2023 23:33:34 +0000 Subject: [PATCH 47/79] Add small margin above fee distribution graph --- .../fee-distribution-graph.component.scss | 3 +++ .../fee-distribution-graph/fee-distribution-graph.component.ts | 1 + 2 files changed, 4 insertions(+) create mode 100644 frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.scss diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.scss b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.scss new file mode 100644 index 000000000..e7150a720 --- /dev/null +++ b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.scss @@ -0,0 +1,3 @@ +.fee-distribution-chart { + margin-top: 0.75rem; +} \ No newline at end of file diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts index 79d89ef46..c73c20237 100644 --- a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts +++ b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts @@ -9,6 +9,7 @@ import { Subscription } from 'rxjs'; @Component({ selector: 'app-fee-distribution-graph', templateUrl: './fee-distribution-graph.component.html', + styleUrls: ['./fee-distribution-graph.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestroy { From 0abf4f415f32049476847fcc149de8cb837e4ab4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 Nov 2023 08:29:45 +0000 Subject: [PATCH 48/79] Bump get-func-name from 2.0.0 to 2.0.2 in /frontend Bumps [get-func-name](https://github.com/chaijs/get-func-name) from 2.0.0 to 2.0.2. - [Release notes](https://github.com/chaijs/get-func-name/releases) - [Commits](https://github.com/chaijs/get-func-name/commits/v2.0.2) --- updated-dependencies: - dependency-name: get-func-name dependency-type: indirect ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c29a0f316..5fc9a7964 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -31,7 +31,6 @@ "bootstrap": "~4.6.2", "browserify": "^17.0.0", "clipboard": "^2.0.11", - "cypress": "^13.5.0", "domino": "^2.1.6", "echarts": "~5.4.3", "lightweight-charts": "~3.8.0", @@ -9514,9 +9513,9 @@ } }, "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "optional": true, "engines": { "node": "*" @@ -23940,9 +23939,9 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "optional": true }, "get-intrinsic": { From 9ab85ab7999caef3be7d6b53aabafe93ddd3a87e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 Nov 2023 08:39:56 +0000 Subject: [PATCH 49/79] Bump the frontend-angular-dependencies group in /frontend with 1 update Bumps the frontend-angular-dependencies group in /frontend with 1 update: [ngx-echarts](https://github.com/xieziyu/ngx-echarts). - [Release notes](https://github.com/xieziyu/ngx-echarts/releases) - [Commits](https://github.com/xieziyu/ngx-echarts/commits) --- updated-dependencies: - dependency-name: ngx-echarts dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend-angular-dependencies ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 14 +++++++------- frontend/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5fc9a7964..7054b7385 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -34,7 +34,7 @@ "domino": "^2.1.6", "echarts": "~5.4.3", "lightweight-charts": "~3.8.0", - "ngx-echarts": "~16.0.0", + "ngx-echarts": "~16.2.0", "ngx-infinite-scroll": "^16.0.0", "qrcode": "1.5.1", "rxjs": "~7.8.1", @@ -12442,9 +12442,9 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, "node_modules/ngx-echarts": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/ngx-echarts/-/ngx-echarts-16.0.0.tgz", - "integrity": "sha512-hdM7/CL29bY3sF3V5ihb7H1NeUsQlhijp8tVxT23+vkNTf9SJrUHPjs9oHOMkbTlr2Q8HB+eVpckYAL/tuK0CQ==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/ngx-echarts/-/ngx-echarts-16.2.0.tgz", + "integrity": "sha512-yhuDbp6qdkmR4kRVLS06Z0Iumod7xOj5n/Z++clRiKM24OQ4sM8WuOTicdfWy6eeYDNywdGSrri4Y5SUGRD8bg==", "dependencies": { "tslib": "^2.3.0" }, @@ -26109,9 +26109,9 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, "ngx-echarts": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/ngx-echarts/-/ngx-echarts-16.0.0.tgz", - "integrity": "sha512-hdM7/CL29bY3sF3V5ihb7H1NeUsQlhijp8tVxT23+vkNTf9SJrUHPjs9oHOMkbTlr2Q8HB+eVpckYAL/tuK0CQ==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/ngx-echarts/-/ngx-echarts-16.2.0.tgz", + "integrity": "sha512-yhuDbp6qdkmR4kRVLS06Z0Iumod7xOj5n/Z++clRiKM24OQ4sM8WuOTicdfWy6eeYDNywdGSrri4Y5SUGRD8bg==", "requires": { "tslib": "^2.3.0" } diff --git a/frontend/package.json b/frontend/package.json index 984cba3de..18640c793 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -86,7 +86,7 @@ "domino": "^2.1.6", "echarts": "~5.4.3", "lightweight-charts": "~3.8.0", - "ngx-echarts": "~16.0.0", + "ngx-echarts": "~16.2.0", "ngx-infinite-scroll": "^16.0.0", "qrcode": "1.5.1", "rxjs": "~7.8.1", From 37605d57324fb9ca3910ee185cda79c9d6606820 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 11 Nov 2023 08:57:55 +0000 Subject: [PATCH 50/79] Improve fee distribution legibility on small screens --- .../fee-distribution-graph.component.ts | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts index c73c20237..178d87897 100644 --- a/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts +++ b/frontend/src/app/components/fee-distribution-graph/fee-distribution-graph.component.ts @@ -1,4 +1,4 @@ -import { OnChanges, OnDestroy } from '@angular/core'; +import { HostListener, OnChanges, OnDestroy } from '@angular/core'; import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { TransactionStripped } from '../../interfaces/websocket.interface'; import { StateService } from '../../services/state.service'; @@ -26,6 +26,7 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr simple: boolean = false; data: number[][]; labelInterval: number = 50; + smallScreen: boolean = window.innerWidth < 450; rateUnitSub: Subscription; weightMode: boolean = false; @@ -96,9 +97,9 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr this.mempoolVsizeFeesOptions = { grid: { height: '210', - right: '20', + right: this.smallScreen ? '10' : '20', top: '22', - left: '40', + left: this.smallScreen ? '10' : '40', }, xAxis: { type: 'category', @@ -132,7 +133,7 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr } }, axisLabel: { - show: true, + show: !this.smallScreen, formatter: (value: number): string => { const unitValue = this.weightMode ? value / 4 : value; const selectedPowerOfTen = selectPowerOfTen(unitValue); @@ -142,7 +143,7 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr }, }, axisTick: { - show: true, + show: !this.smallScreen, } }, series: [{ @@ -153,6 +154,7 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr position: 'top', color: '#ffffff', textShadowBlur: 0, + fontSize: this.smallScreen ? 10 : 12, formatter: (label: { data: number[] }): string => { const value = label.data[1]; const unitValue = this.weightMode ? value / 4 : value; @@ -182,6 +184,16 @@ export class FeeDistributionGraphComponent implements OnInit, OnChanges, OnDestr }; } + @HostListener('window:resize', ['$event']) + onResize(): void { + const isSmallScreen = window.innerWidth < 450; + if (this.smallScreen !== isSmallScreen) { + this.smallScreen = isSmallScreen; + this.prepareChart(); + this.mountChart(); + } + } + ngOnDestroy(): void { this.rateUnitSub.unsubscribe(); } From 42732764227af44dea060f61d31112e10273b996 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 Nov 2023 09:37:11 +0000 Subject: [PATCH 51/79] Bump mock-socket from 9.2.1 to 9.3.1 in /frontend Bumps [mock-socket](https://github.com/thoov/mock-socket) from 9.2.1 to 9.3.1. - [Release notes](https://github.com/thoov/mock-socket/releases) - [Changelog](https://github.com/thoov/mock-socket/blob/master/CHANGELOG.md) - [Commits](https://github.com/thoov/mock-socket/commits) --- updated-dependencies: - dependency-name: mock-socket dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 15 ++++++++------- frontend/package.json | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7054b7385..e4cfb1fc4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -34,6 +34,7 @@ "domino": "^2.1.6", "echarts": "~5.4.3", "lightweight-charts": "~3.8.0", + "mock-socket": "~9.3.1", "ngx-echarts": "~16.2.0", "ngx-infinite-scroll": "^16.0.0", "qrcode": "1.5.1", @@ -61,7 +62,7 @@ "cypress": "^13.5.0", "cypress-fail-on-console-error": "~5.0.0", "cypress-wait-until": "^2.0.1", - "mock-socket": "~9.2.1", + "mock-socket": "~9.3.1", "start-server-and-test": "~2.0.0" } }, @@ -12191,9 +12192,9 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "node_modules/mock-socket": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.2.1.tgz", - "integrity": "sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", + "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==", "optional": true, "engines": { "node": ">= 8" @@ -25913,9 +25914,9 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "mock-socket": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.2.1.tgz", - "integrity": "sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", + "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==", "optional": true }, "module-deps": { diff --git a/frontend/package.json b/frontend/package.json index 18640c793..e549eb122 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -113,7 +113,7 @@ "cypress": "^13.5.0", "cypress-fail-on-console-error": "~5.0.0", "cypress-wait-until": "^2.0.1", - "mock-socket": "~9.2.1", + "mock-socket": "~9.3.1", "start-server-and-test": "~2.0.0" }, "scarfSettings": { From 511b827bf56b7d099afdba1db25d047bea24e050 Mon Sep 17 00:00:00 2001 From: junderw Date: Wed, 16 Aug 2023 00:44:06 -0700 Subject: [PATCH 52/79] Nginx: Ignore all internal-api paths --- nginx-mempool.conf | 5 +++++ production/nginx/location-api.conf | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/nginx-mempool.conf b/nginx-mempool.conf index 67cb15939..bc8efc59e 100644 --- a/nginx-mempool.conf +++ b/nginx-mempool.conf @@ -39,6 +39,11 @@ try_files $uri $uri/ /$1/index.html =404; } + # any path containing .*/internal-api/.* anywhere is ignored + location ~ ^/.*?/internal-api/ { + return 404; + } + # static API docs location = /api { try_files $uri $uri/ /en-US/index.html =404; diff --git a/production/nginx/location-api.conf b/production/nginx/location-api.conf index 2b2b85411..dba798719 100644 --- a/production/nginx/location-api.conf +++ b/production/nginx/location-api.conf @@ -2,6 +2,11 @@ # mempool # ########### +# any path containing .*/internal-api/.* anywhere is ignored +location ~ ^/.*?/internal-api/ { + return 404; +} + # websocket has special HTTP headers location /api/v1/ws { try_files /dev/null @mempool-api-v1-websocket; From d16773bfa02fa3a8ea2d34c820a95bf9723771a3 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 16 Aug 2023 17:54:08 +0900 Subject: [PATCH 53/79] Switch backend to use internal-api paths --- backend/src/api/bitcoin/esplora-api.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index 0f3c6290d..368ff1978 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -214,11 +214,11 @@ class ElectrsApi implements AbstractBitcoinApi { } async $getMempoolTransactions(txids: string[]): Promise { - return this.failoverRouter.$post('/mempool/txs', txids, 'json'); + return this.failoverRouter.$post('/internal-api/mempool/txs', txids, 'json'); } async $getAllMempoolTransactions(lastSeenTxid?: string): Promise { - return this.failoverRouter.$get('/mempool/txs' + (lastSeenTxid ? '/' + lastSeenTxid : '')); + return this.failoverRouter.$get('/internal-api/mempool/txs' + (lastSeenTxid ? '/' + lastSeenTxid : '')); } $getTransactionHex(txId: string): Promise { From 502a1c021e9df76aae5ab281d98a5af1cf213a2b Mon Sep 17 00:00:00 2001 From: Jonathan Underwood Date: Mon, 28 Aug 2023 02:18:59 +0900 Subject: [PATCH 54/79] Add suggestions from wiz Co-authored-by: wiz --- backend/src/api/bitcoin/esplora-api.ts | 4 ++-- nginx-mempool.conf | 5 ----- production/nginx/location-api.conf | 7 ++++--- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index 368ff1978..90da93d7e 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -214,11 +214,11 @@ class ElectrsApi implements AbstractBitcoinApi { } async $getMempoolTransactions(txids: string[]): Promise { - return this.failoverRouter.$post('/internal-api/mempool/txs', txids, 'json'); + return this.failoverRouter.$post('/internal/mempool/txs', txids, 'json'); } async $getAllMempoolTransactions(lastSeenTxid?: string): Promise { - return this.failoverRouter.$get('/internal-api/mempool/txs' + (lastSeenTxid ? '/' + lastSeenTxid : '')); + return this.failoverRouter.$get('/internal/mempool/txs' + (lastSeenTxid ? '/' + lastSeenTxid : '')); } $getTransactionHex(txId: string): Promise { diff --git a/nginx-mempool.conf b/nginx-mempool.conf index bc8efc59e..67cb15939 100644 --- a/nginx-mempool.conf +++ b/nginx-mempool.conf @@ -39,11 +39,6 @@ try_files $uri $uri/ /$1/index.html =404; } - # any path containing .*/internal-api/.* anywhere is ignored - location ~ ^/.*?/internal-api/ { - return 404; - } - # static API docs location = /api { try_files $uri $uri/ /en-US/index.html =404; diff --git a/production/nginx/location-api.conf b/production/nginx/location-api.conf index dba798719..333877630 100644 --- a/production/nginx/location-api.conf +++ b/production/nginx/location-api.conf @@ -2,11 +2,12 @@ # mempool # ########### -# any path containing .*/internal-api/.* anywhere is ignored -location ~ ^/.*?/internal-api/ { +location /api/internal/ { + return 404; +} +location /api/v1/internal/ { return 404; } - # websocket has special HTTP headers location /api/v1/ws { try_files /dev/null @mempool-api-v1-websocket; From 4972f00a9690c639f3b476212e15460e4a605570 Mon Sep 17 00:00:00 2001 From: junderw Date: Sun, 27 Aug 2023 10:28:51 -0700 Subject: [PATCH 55/79] Add internal endpoint blocking to all Nginx configs --- production/nginx/location-api.conf | 2 ++ production/nginx/location-liquid-api.conf | 8 ++++++++ production/nginx/location-liquidtestnet-api.conf | 8 ++++++++ production/nginx/location-signet-api.conf | 8 ++++++++ production/nginx/location-testnet-api.conf | 8 ++++++++ 5 files changed, 34 insertions(+) diff --git a/production/nginx/location-api.conf b/production/nginx/location-api.conf index 333877630..71afa295a 100644 --- a/production/nginx/location-api.conf +++ b/production/nginx/location-api.conf @@ -2,12 +2,14 @@ # mempool # ########### +# Block the internal APIs of esplora location /api/internal/ { return 404; } location /api/v1/internal/ { return 404; } + # websocket has special HTTP headers location /api/v1/ws { try_files /dev/null @mempool-api-v1-websocket; diff --git a/production/nginx/location-liquid-api.conf b/production/nginx/location-liquid-api.conf index e438d1cdc..6c222c469 100644 --- a/production/nginx/location-liquid-api.conf +++ b/production/nginx/location-liquid-api.conf @@ -2,6 +2,14 @@ # mempool # ########### +# Block the internal APIs of esplora +location /liquid/api/internal/ { + return 404; +} +location /liquid/api/v1/internal/ { + return 404; +} + # websocket has special HTTP headers location /liquid/api/v1/ws { rewrite ^/liquid/(.*) /$1 break; diff --git a/production/nginx/location-liquidtestnet-api.conf b/production/nginx/location-liquidtestnet-api.conf index 329b7e2e9..5d5be5d43 100644 --- a/production/nginx/location-liquidtestnet-api.conf +++ b/production/nginx/location-liquidtestnet-api.conf @@ -2,6 +2,14 @@ # mempool # ########### +# Block the internal APIs of esplora +location /liquidtestnet/api/internal/ { + return 404; +} +location /liquidtestnet/api/v1/internal/ { + return 404; +} + # websocket has special HTTP headers location /liquidtestnet/api/v1/ws { rewrite ^/liquidtestnet/(.*) /$1 break; diff --git a/production/nginx/location-signet-api.conf b/production/nginx/location-signet-api.conf index 54bdc3648..8469043a8 100644 --- a/production/nginx/location-signet-api.conf +++ b/production/nginx/location-signet-api.conf @@ -2,6 +2,14 @@ # mempool # ########### +# Block the internal APIs of esplora +location /signet/api/internal/ { + return 404; +} +location /signet/api/v1/internal/ { + return 404; +} + # websocket has special HTTP headers location /signet/api/v1/ws { rewrite ^/signet/(.*) /$1 break; diff --git a/production/nginx/location-testnet-api.conf b/production/nginx/location-testnet-api.conf index 656a705ff..9f0c41147 100644 --- a/production/nginx/location-testnet-api.conf +++ b/production/nginx/location-testnet-api.conf @@ -2,6 +2,14 @@ # mempool # ########### +# Block the internal APIs of esplora +location /testnet/api/internal/ { + return 404; +} +location /testnet/api/v1/internal/ { + return 404; +} + # websocket has special HTTP headers location /testnet/api/v1/ws { rewrite ^/testnet/(.*) /$1 break; From 2339a0771effdcb7efe446a6785d6a3cf64ed4d1 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 6 Sep 2023 08:24:30 +0900 Subject: [PATCH 56/79] Use internal /block/:hash/txs endpoint --- backend/src/api/bitcoin/esplora-api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index 90da93d7e..af021bf2e 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -238,7 +238,7 @@ class ElectrsApi implements AbstractBitcoinApi { } $getTxsForBlock(hash: string): Promise { - return this.failoverRouter.$get('/block/' + hash + '/txs'); + return this.failoverRouter.$get('/internal/block/' + hash + '/txs'); } $getBlockHash(height: number): Promise { From 38909cfc4253ca0d62e0c4b1a374e4b0769728d6 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 5 Aug 2023 16:08:54 +0900 Subject: [PATCH 57/79] use bulk /txs endpoint to check cached rbf tx status --- .../bitcoin/bitcoin-api-abstract-factory.ts | 1 + backend/src/api/bitcoin/bitcoin-api.ts | 4 ++ backend/src/api/bitcoin/esplora-api.ts | 4 ++ backend/src/api/rbf-cache.ts | 53 +++++++++++++++++++ 4 files changed, 62 insertions(+) diff --git a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts index 9407a5441..a76b93e8d 100644 --- a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts +++ b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts @@ -3,6 +3,7 @@ import { IEsploraApi } from './esplora-api.interface'; export interface AbstractBitcoinApi { $getRawMempool(): Promise; $getRawTransaction(txId: string, skipConversion?: boolean, addPrevout?: boolean, lazyPrevouts?: boolean): Promise; + $getRawTransactions(txids: string[]): Promise; $getMempoolTransactions(txids: string[]): Promise; $getAllMempoolTransactions(lastTxid: string); $getTransactionHex(txId: string): Promise; diff --git a/backend/src/api/bitcoin/bitcoin-api.ts b/backend/src/api/bitcoin/bitcoin-api.ts index 807baae2e..1be7993b8 100644 --- a/backend/src/api/bitcoin/bitcoin-api.ts +++ b/backend/src/api/bitcoin/bitcoin-api.ts @@ -60,6 +60,10 @@ class BitcoinApi implements AbstractBitcoinApi { }); } + $getRawTransactions(txids: string[]): Promise { + throw new Error('Method getRawTransactions not supported by the Bitcoin RPC API.'); + } + $getMempoolTransactions(txids: string[]): Promise { throw new Error('Method getMempoolTransactions not supported by the Bitcoin RPC API.'); } diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index af021bf2e..1ebfef8c8 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -213,6 +213,10 @@ class ElectrsApi implements AbstractBitcoinApi { return this.failoverRouter.$get('/tx/' + txId); } + async $getRawTransactions(txids: string[]): Promise { + return this.$postWrapper(config.ESPLORA.REST_API_URL + '/txs', txids, 'json'); + } + async $getMempoolTransactions(txids: string[]): Promise { return this.failoverRouter.$post('/internal/mempool/txs', txids, 'json'); } diff --git a/backend/src/api/rbf-cache.ts b/backend/src/api/rbf-cache.ts index 6e1f37afb..7d95df27b 100644 --- a/backend/src/api/rbf-cache.ts +++ b/backend/src/api/rbf-cache.ts @@ -2,6 +2,7 @@ import config from "../config"; import logger from "../logger"; import { MempoolTransactionExtended, TransactionStripped } from "../mempool.interfaces"; import bitcoinApi from './bitcoin/bitcoin-api-factory'; +import { IEsploraApi } from "./bitcoin/esplora-api.interface"; import { Common } from "./common"; import redisCache from "./redis-cache"; @@ -383,6 +384,7 @@ class RbfCache { }); logger.debug(`loaded ${txs.length} txs, ${trees.length} trees into rbf cache, ${expiring.length} due to expire, ${this.staleCount} were stale`); this.staleCount = 0; + await this.checkTrees(); this.cleanup(); } catch (e) { logger.err('failed to restore RBF cache: ' + (e instanceof Error ? e.message : e)); @@ -481,6 +483,57 @@ class RbfCache { return tree; } + private async checkTrees(): Promise { + const found: { [txid: string]: boolean } = {}; + const txids = Array.from(this.txs.values()).map(tx => tx.txid).filter(txid => { + return !this.expiring.has(txid) && !this.getRbfTree(txid)?.mined; + }); + + const processTxs = (txs: IEsploraApi.Transaction[]): void => { + for (const tx of txs) { + found[tx.txid] = true; + if (tx.status?.confirmed) { + const tree = this.getRbfTree(tx.txid); + if (tree) { + this.setTreeMined(tree, tx.txid); + tree.mined = true; + this.evict(tx.txid, false); + } + } + } + }; + + if (config.MEMPOOL.BACKEND === 'esplora') { + const sliceLength = 10000; + for (let i = 0; i < Math.ceil(txids.length / sliceLength); i++) { + const slice = txids.slice(i * sliceLength, (i + 1) * sliceLength); + try { + const txs = await bitcoinApi.$getRawTransactions(slice); + processTxs(txs); + } catch (err) { + logger.err('failed to fetch some cached rbf transactions'); + } + } + } else { + const txs: IEsploraApi.Transaction[] = []; + for (const txid of txids) { + try { + const tx = await bitcoinApi.$getRawTransaction(txid, true, false); + txs.push(tx); + } catch (err) { + // some 404s are expected, so continue quietly + } + } + processTxs(txs); + } + + for (const txid of txids) { + if (!found[txid]) { + this.evict(txid, false); + } + } + } + public getLatestRbfSummary(): ReplacementInfo[] { const rbfList = this.getRbfTrees(false); return rbfList.slice(0, 6).map(rbfTree => { From 156b5d0b3c33e775eeb158f16a4d4d4be48ed710 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 16 Aug 2023 18:30:18 +0900 Subject: [PATCH 58/79] Update bulk /txs to use new failover router, internal-api path --- backend/src/api/bitcoin/esplora-api.ts | 2 +- backend/src/api/rbf-cache.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index 1ebfef8c8..3264e8725 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -214,7 +214,7 @@ class ElectrsApi implements AbstractBitcoinApi { } async $getRawTransactions(txids: string[]): Promise { - return this.$postWrapper(config.ESPLORA.REST_API_URL + '/txs', txids, 'json'); + return this.failoverRouter.$post('/internal-api/txs', txids, 'json'); } async $getMempoolTransactions(txids: string[]): Promise { diff --git a/backend/src/api/rbf-cache.ts b/backend/src/api/rbf-cache.ts index 7d95df27b..b7d6f11a6 100644 --- a/backend/src/api/rbf-cache.ts +++ b/backend/src/api/rbf-cache.ts @@ -505,10 +505,13 @@ class RbfCache { if (config.MEMPOOL.BACKEND === 'esplora') { const sliceLength = 10000; + let count = 0; for (let i = 0; i < Math.ceil(txids.length / sliceLength); i++) { const slice = txids.slice(i * sliceLength, (i + 1) * sliceLength); try { const txs = await bitcoinApi.$getRawTransactions(slice); + count += txs.length; + logger.info(`Fetched ${count} of ${txids.length} unknown-status RBF transactions from esplora`); processTxs(txs); } catch (err) { logger.err('failed to fetch some cached rbf transactions'); From 031e14f30277ca74f65abec1bd81bc56b60aea81 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 28 Aug 2023 16:55:50 +0900 Subject: [PATCH 59/79] Update internal getRawTransactions to use new prefix --- backend/src/api/bitcoin/esplora-api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index 3264e8725..8f47921c2 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -214,7 +214,7 @@ class ElectrsApi implements AbstractBitcoinApi { } async $getRawTransactions(txids: string[]): Promise { - return this.failoverRouter.$post('/internal-api/txs', txids, 'json'); + return this.failoverRouter.$post('/internal/txs', txids, 'json'); } async $getMempoolTransactions(txids: string[]): Promise { From 9d60c39aebf638f04b5efa84dce9ea92acd0ebb2 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 12 Nov 2023 06:19:46 +0000 Subject: [PATCH 60/79] Resolve rbf cache merge conflicts --- backend/src/api/rbf-cache.ts | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/backend/src/api/rbf-cache.ts b/backend/src/api/rbf-cache.ts index b7d6f11a6..950e3a4e5 100644 --- a/backend/src/api/rbf-cache.ts +++ b/backend/src/api/rbf-cache.ts @@ -382,10 +382,11 @@ class RbfCache { this.expiring.set(expiringEntry.key, new Date(expiringEntry.value).getTime()); } }); - logger.debug(`loaded ${txs.length} txs, ${trees.length} trees into rbf cache, ${expiring.length} due to expire, ${this.staleCount} were stale`); this.staleCount = 0; await this.checkTrees(); + logger.debug(`loaded ${txs.length} txs, ${trees.length} trees into rbf cache, ${expiring.length} due to expire, ${this.staleCount} were stale`); this.cleanup(); + } catch (e) { logger.err('failed to restore RBF cache: ' + (e instanceof Error ? e.message : e)); } @@ -423,31 +424,6 @@ class RbfCache { return; } - // check if any transactions in this tree have already been confirmed - mined = mined || treeInfo.mined; - let exists = mined; - if (!mined) { - try { - const apiTx = await bitcoinApi.$getRawTransaction(txid); - if (apiTx) { - exists = true; - } - if (apiTx?.status?.confirmed) { - mined = true; - treeInfo.txMined = true; - this.evict(txid, true); - } - } catch (e) { - // most transactions only exist in our cache - } - } - - // if the root tx is not in the mempool or the blockchain - // evict this tree as soon as possible - if (root === txid && !exists) { - this.evict(txid, true); - } - // recursively reconstruct child trees for (const childId of treeInfo.replaces) { const replaced = await this.importTree(root, childId, deflated, txs, mined); @@ -505,13 +481,10 @@ class RbfCache { if (config.MEMPOOL.BACKEND === 'esplora') { const sliceLength = 10000; - let count = 0; for (let i = 0; i < Math.ceil(txids.length / sliceLength); i++) { const slice = txids.slice(i * sliceLength, (i + 1) * sliceLength); try { const txs = await bitcoinApi.$getRawTransactions(slice); - count += txs.length; - logger.info(`Fetched ${count} of ${txids.length} unknown-status RBF transactions from esplora`); processTxs(txs); } catch (err) { logger.err('failed to fetch some cached rbf transactions'); From 823f06451c9a9ec00c91d6bac0023d3929d400ab Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 11 Sep 2023 11:04:01 +0900 Subject: [PATCH 61/79] Send correct tx conf status in websocket msgs --- backend/src/api/blocks.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/backend/src/api/blocks.ts b/backend/src/api/blocks.ts index 73b010b91..64dc1d5ba 100644 --- a/backend/src/api/blocks.ts +++ b/backend/src/api/blocks.ts @@ -81,6 +81,7 @@ class Blocks { private async $getTransactionsExtended( blockHash: string, blockHeight: number, + blockTime: number, onlyCoinbase: boolean, txIds: string[] | null = null, quiet: boolean = false, @@ -101,6 +102,12 @@ class Blocks { if (!onlyCoinbase) { for (const txid of txIds) { if (mempool[txid]) { + mempool[txid].status = { + confirmed: true, + block_height: blockHeight, + block_hash: blockHash, + block_time: blockTime, + }; transactionMap[txid] = mempool[txid]; foundInMempool++; totalFound++; @@ -608,7 +615,7 @@ class Blocks { } const blockHash = await bitcoinApi.$getBlockHash(blockHeight); const block: IEsploraApi.Block = await bitcoinApi.$getBlock(blockHash); - const transactions = await this.$getTransactionsExtended(blockHash, block.height, true, null, true); + const transactions = await this.$getTransactionsExtended(blockHash, block.height, block.timestamp, true, null, true); const blockExtended = await this.$getBlockExtended(block, transactions); newlyIndexed++; @@ -701,7 +708,7 @@ class Blocks { const verboseBlock = await bitcoinClient.getBlock(blockHash, 2); const block = BitcoinApi.convertBlock(verboseBlock); const txIds: string[] = verboseBlock.tx.map(tx => tx.txid); - const transactions = await this.$getTransactionsExtended(blockHash, block.height, false, txIds, false, true) as MempoolTransactionExtended[]; + const transactions = await this.$getTransactionsExtended(blockHash, block.height, block.timestamp, false, txIds, false, true) as MempoolTransactionExtended[]; // fill in missing transaction fee data from verboseBlock for (let i = 0; i < transactions.length; i++) { @@ -890,7 +897,7 @@ class Blocks { const blockHash = await bitcoinApi.$getBlockHash(height); const block: IEsploraApi.Block = await bitcoinApi.$getBlock(blockHash); - const transactions = await this.$getTransactionsExtended(blockHash, block.height, true); + const transactions = await this.$getTransactionsExtended(blockHash, block.height, block.timestamp, true); const blockExtended = await this.$getBlockExtended(block, transactions); if (Common.indexingEnabled()) { @@ -902,7 +909,7 @@ class Blocks { public async $indexStaleBlock(hash: string): Promise { const block: IEsploraApi.Block = await bitcoinApi.$getBlock(hash); - const transactions = await this.$getTransactionsExtended(hash, block.height, true); + const transactions = await this.$getTransactionsExtended(hash, block.height, block.timestamp, true); const blockExtended = await this.$getBlockExtended(block, transactions); blockExtended.canonical = await bitcoinApi.$getBlockHash(block.height); From 8aa51c4e802f8069e285bd496455e07b0472b455 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 16 Aug 2023 02:25:48 +0900 Subject: [PATCH 62/79] Batch esplora outspends requests --- backend/src/api/bitcoin/bitcoin.routes.ts | 21 +++++++++++++++++++ backend/src/api/bitcoin/esplora-api.ts | 16 +++++++------- .../transactions-list.component.ts | 2 +- .../tx-bowtie-graph.component.ts | 5 +++-- frontend/src/app/services/api.service.ts | 8 ------- .../src/app/services/electrs-api.service.ts | 6 ++++++ 6 files changed, 38 insertions(+), 20 deletions(-) diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index 240fb07ce..cc912877c 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -112,6 +112,7 @@ class BitcoinRoutes { .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/hex', this.getRawTransaction) .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/status', this.getTransactionStatus) .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/outspends', this.getTransactionOutspends) + .get(config.MEMPOOL.API_URL_PREFIX + 'txs/outspends', this.$getOutspends) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/header', this.getBlockHeader) .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/tip/hash', this.getBlockTipHash) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/raw', this.getRawBlock) @@ -198,6 +199,26 @@ class BitcoinRoutes { } } + private async $getOutspends(req: Request, res: Response) { + const txids_csv = req.query.txids; + if (!txids_csv || typeof txids_csv !== 'string') { + res.status(500).send('Invalid txids format'); + return; + } + const txids = txids_csv.split(','); + if (txids.length > 50) { + res.status(400).send('Too many txids requested'); + return; + } + + try { + const batchedOutspends = await bitcoinApi.$getBatchedOutspends(txids); + res.json(batchedOutspends); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + private async $getCpfpInfo(req: Request, res: Response) { if (!/^[a-fA-F0-9]{64}$/.test(req.params.txId)) { res.status(501).send(`Invalid transaction ID.`); diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index 8f47921c2..3ccb5693d 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -174,6 +174,9 @@ class FailoverRouter { axiosConfig = { timeout: config.ESPLORA.REQUEST_TIMEOUT, responseType }; url = host.host + path; } + if (data?.params) { + axiosConfig.params = data.params; + } return (method === 'post' ? this.requestConnection.post(url, data, axiosConfig) : this.requestConnection.get(url, axiosConfig) @@ -193,8 +196,8 @@ class FailoverRouter { }); } - public async $get(path, responseType = 'json'): Promise { - return this.$query('get', path, null, responseType); + public async $get(path, responseType = 'json', params: any = null): Promise { + return this.$query('get', path, params ? { params } : null, responseType); } public async $post(path, data: any, responseType = 'json'): Promise { @@ -294,13 +297,8 @@ class ElectrsApi implements AbstractBitcoinApi { return this.failoverRouter.$get('/tx/' + txId + '/outspends'); } - async $getBatchedOutspends(txId: string[]): Promise { - const outspends: IEsploraApi.Outspend[][] = []; - for (const tx of txId) { - const outspend = await this.$getOutspends(tx); - outspends.push(outspend); - } - return outspends; + async $getBatchedOutspends(txids: string[]): Promise { + return this.failoverRouter.$get('/txs/outspends', 'json', { txids: txids.join(',') }); } public startHealthChecks(): void { diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index 05d74a75d..19eb2a6d7 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -75,7 +75,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { for (let i = 0; i < txIds.length; i += 50) { batches.push(txIds.slice(i, i + 50)); } - return forkJoin(batches.map(batch => { return this.apiService.cachedRequest(this.apiService.getOutspendsBatched$, 250, batch); })); + return forkJoin(batches.map(batch => this.electrsApiService.getOutspendsBatched$(batch))); } else { return of([]); } diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts index 3bc352a35..d22f5705b 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts @@ -8,6 +8,7 @@ import { ApiService } from '../../services/api.service'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; import { AssetsService } from '../../services/assets.service'; import { environment } from '../../../environments/environment'; +import { ElectrsApiService } from '../../services/electrs-api.service'; interface SvgLine { path: string; @@ -100,7 +101,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { private router: Router, private relativeUrlPipe: RelativeUrlPipe, private stateService: StateService, - private apiService: ApiService, + private electrsApiService: ElectrsApiService, private assetsService: AssetsService, @Inject(LOCALE_ID) private locale: string, ) { @@ -123,7 +124,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { .pipe( switchMap((txid) => { if (!this.cached) { - return this.apiService.cachedRequest(this.apiService.getOutspendsBatched$, 250, [txid]); + return this.electrsApiService.getOutspendsBatched$([txid]); } else { return of(null); } diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index 046b27812..cd5dd1ae6 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -138,14 +138,6 @@ export class ApiService { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/transaction-times', { params }); } - getOutspendsBatched$(txIds: string[]): Observable { - let params = new HttpParams(); - txIds.forEach((txId: string) => { - params = params.append('txId[]', txId); - }); - return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/outspends', { params }); - } - getAboutPageProfiles$(): Observable { return this.httpClient.get(this.apiBaseUrl + '/api/v1/services/sponsors'); } diff --git a/frontend/src/app/services/electrs-api.service.ts b/frontend/src/app/services/electrs-api.service.ts index d63d49f68..c3efcbf46 100644 --- a/frontend/src/app/services/electrs-api.service.ts +++ b/frontend/src/app/services/electrs-api.service.ts @@ -54,6 +54,12 @@ export class ElectrsApiService { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/tx/' + hash + '/outspends'); } + getOutspendsBatched$(txids: string[]): Observable { + let params = new HttpParams(); + params = params.append('txids', txids.join(',')); + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/txs/outspends', { params }); + } + getBlockTransactions$(hash: string, index: number = 0): Observable { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/block/' + hash + '/txs/' + index); } From 09f208484a19512d5d8188f45c382e44dedda2c8 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 12 Nov 2023 06:38:18 +0000 Subject: [PATCH 63/79] Add electrsApiService cachedRequest function, switch outspends --- .../transactions-list.component.ts | 2 +- .../tx-bowtie-graph.component.ts | 2 +- .../src/app/services/electrs-api.service.ts | 44 ++++++++++++++++++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index 19eb2a6d7..d066d3583 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -75,7 +75,7 @@ export class TransactionsListComponent implements OnInit, OnChanges { for (let i = 0; i < txIds.length; i += 50) { batches.push(txIds.slice(i, i + 50)); } - return forkJoin(batches.map(batch => this.electrsApiService.getOutspendsBatched$(batch))); + return forkJoin(batches.map(batch => { return this.electrsApiService.cachedRequest(this.electrsApiService.getOutspendsBatched$, 250, batch); })); } else { return of([]); } diff --git a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts index d22f5705b..043c9ea3b 100644 --- a/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts +++ b/frontend/src/app/components/tx-bowtie-graph/tx-bowtie-graph.component.ts @@ -124,7 +124,7 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges { .pipe( switchMap((txid) => { if (!this.cached) { - return this.electrsApiService.getOutspendsBatched$([txid]); + return this.electrsApiService.cachedRequest(this.electrsApiService.getOutspendsBatched$, 250, [txid]); } else { return of(null); } diff --git a/frontend/src/app/services/electrs-api.service.ts b/frontend/src/app/services/electrs-api.service.ts index c3efcbf46..eaa1ab52d 100644 --- a/frontend/src/app/services/electrs-api.service.ts +++ b/frontend/src/app/services/electrs-api.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; -import { Observable, from, of, switchMap } from 'rxjs'; +import { BehaviorSubject, Observable, catchError, filter, from, of, shareReplay, switchMap, take, tap } from 'rxjs'; import { Transaction, Address, Outspend, Recent, Asset, ScriptHash } from '../interfaces/electrs.interface'; import { StateService } from './state.service'; import { BlockExtended } from '../interfaces/node-api.interface'; @@ -13,6 +13,8 @@ export class ElectrsApiService { private apiBaseUrl: string; // base URL is protocol, hostname, and port private apiBasePath: string; // network path is /testnet, etc. or '' for mainnet + private requestCache = new Map, expiry: number }>; + constructor( private httpClient: HttpClient, private stateService: StateService, @@ -30,6 +32,46 @@ export class ElectrsApiService { }); } + private generateCacheKey(functionName: string, params: any[]): string { + return functionName + JSON.stringify(params); + } + + // delete expired cache entries + private cleanExpiredCache(): void { + this.requestCache.forEach((value, key) => { + if (value.expiry < Date.now()) { + this.requestCache.delete(key); + } + }); + } + + cachedRequest Observable>( + apiFunction: F, + expireAfter: number, // in ms + ...params: Parameters + ): Observable { + this.cleanExpiredCache(); + + const cacheKey = this.generateCacheKey(apiFunction.name, params); + if (!this.requestCache.has(cacheKey)) { + const subject = new BehaviorSubject(null); + this.requestCache.set(cacheKey, { subject, expiry: Date.now() + expireAfter }); + + apiFunction.bind(this)(...params).pipe( + tap(data => { + subject.next(data as T); + }), + catchError((error) => { + subject.error(error); + return of(null); + }), + shareReplay(1), + ).subscribe(); + } + + return this.requestCache.get(cacheKey).subject.asObservable().pipe(filter(val => val !== null), take(1)); + } + getBlock$(hash: string): Observable { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/block/' + hash); } From 7ec7ae7b955efdf5250b9da942614be41a50693a Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 12 Nov 2023 06:51:48 +0000 Subject: [PATCH 64/79] Remove old batched outspends API --- backend/src/api/bitcoin/bitcoin.routes.ts | 29 ++--------------------- backend/src/api/bitcoin/esplora-api.ts | 2 +- 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index cc912877c..d62f3da72 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -24,7 +24,6 @@ class BitcoinRoutes { public initRoutes(app: Application) { app .get(config.MEMPOOL.API_URL_PREFIX + 'transaction-times', this.getTransactionTimes) - .get(config.MEMPOOL.API_URL_PREFIX + 'outspends', this.$getBatchedOutspends) .get(config.MEMPOOL.API_URL_PREFIX + 'cpfp/:txId', this.$getCpfpInfo) .get(config.MEMPOOL.API_URL_PREFIX + 'difficulty-adjustment', this.getDifficultyChange) .get(config.MEMPOOL.API_URL_PREFIX + 'fees/recommended', this.getRecommendedFees) @@ -112,7 +111,7 @@ class BitcoinRoutes { .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/hex', this.getRawTransaction) .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/status', this.getTransactionStatus) .get(config.MEMPOOL.API_URL_PREFIX + 'tx/:txId/outspends', this.getTransactionOutspends) - .get(config.MEMPOOL.API_URL_PREFIX + 'txs/outspends', this.$getOutspends) + .get(config.MEMPOOL.API_URL_PREFIX + 'txs/outspends', this.$getBatchedOutspends) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/header', this.getBlockHeader) .get(config.MEMPOOL.API_URL_PREFIX + 'blocks/tip/hash', this.getBlockTipHash) .get(config.MEMPOOL.API_URL_PREFIX + 'block/:hash/raw', this.getRawBlock) @@ -175,31 +174,7 @@ class BitcoinRoutes { res.json(times); } - private async $getBatchedOutspends(req: Request, res: Response) { - if (!Array.isArray(req.query.txId)) { - res.status(500).send('Not an array'); - return; - } - if (req.query.txId.length > 50) { - res.status(400).send('Too many txids requested'); - return; - } - const txIds: string[] = []; - for (const _txId in req.query.txId) { - if (typeof req.query.txId[_txId] === 'string') { - txIds.push(req.query.txId[_txId].toString()); - } - } - - try { - const batchedOutspends = await bitcoinApi.$getBatchedOutspends(txIds); - res.json(batchedOutspends); - } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); - } - } - - private async $getOutspends(req: Request, res: Response) { + private async $getBatchedOutspends(req: Request, res: Response): Promise { const txids_csv = req.query.txids; if (!txids_csv || typeof txids_csv !== 'string') { res.status(500).send('Invalid txids format'); diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index 3ccb5693d..d400dad7a 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -298,7 +298,7 @@ class ElectrsApi implements AbstractBitcoinApi { } async $getBatchedOutspends(txids: string[]): Promise { - return this.failoverRouter.$get('/txs/outspends', 'json', { txids: txids.join(',') }); + throw new Error('Method not implemented.'); } public startHealthChecks(): void { From 5bee54a2bf9f5dfdd56ddf3cc7799329319b369e Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 17 Aug 2023 02:42:59 +0900 Subject: [PATCH 65/79] Use new bulk endpoints to speed up forensics --- .../bitcoin/bitcoin-api-abstract-factory.ts | 1 + backend/src/api/bitcoin/bitcoin-api.ts | 17 +- backend/src/api/bitcoin/esplora-api.ts | 13 ++ .../src/tasks/lightning/forensics.service.ts | 193 ++++++++++++------ 4 files changed, 155 insertions(+), 69 deletions(-) diff --git a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts index a76b93e8d..6f20dad92 100644 --- a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts +++ b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts @@ -24,6 +24,7 @@ export interface AbstractBitcoinApi { $getOutspend(txId: string, vout: number): Promise; $getOutspends(txId: string): Promise; $getBatchedOutspends(txId: string[]): Promise; + $getBatchedOutspendsInternal(txId: string[]): Promise; startHealthChecks(): void; } diff --git a/backend/src/api/bitcoin/bitcoin-api.ts b/backend/src/api/bitcoin/bitcoin-api.ts index 1be7993b8..9e4cbdd8b 100644 --- a/backend/src/api/bitcoin/bitcoin-api.ts +++ b/backend/src/api/bitcoin/bitcoin-api.ts @@ -60,8 +60,17 @@ class BitcoinApi implements AbstractBitcoinApi { }); } - $getRawTransactions(txids: string[]): Promise { - throw new Error('Method getRawTransactions not supported by the Bitcoin RPC API.'); + async $getRawTransactions(txids: string[]): Promise { + const txs: IEsploraApi.Transaction[] = []; + for (const txid of txids) { + try { + const tx = await this.$getRawTransaction(txid, false, true); + txs.push(tx); + } catch (err) { + // skip failures + } + } + return txs; } $getMempoolTransactions(txids: string[]): Promise { @@ -202,6 +211,10 @@ class BitcoinApi implements AbstractBitcoinApi { return outspends; } + async $getBatchedOutspendsInternal(txId: string[]): Promise { + return this.$getBatchedOutspends(txId); + } + $getEstimatedHashrate(blockHeight: number): Promise { // 120 is the default block span in Core return this.bitcoindClient.getNetworkHashPs(120, blockHeight); diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index d400dad7a..574113ae6 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -301,6 +301,19 @@ class ElectrsApi implements AbstractBitcoinApi { throw new Error('Method not implemented.'); } + async $getBatchedOutspendsInternal(txids: string[]): Promise { + const allOutspends: IEsploraApi.Outspend[][] = []; + const sliceLength = 50; + for (let i = 0; i < Math.ceil(txids.length / sliceLength); i++) { + const slice = txids.slice(i * sliceLength, (i + 1) * sliceLength); + const sliceOutspends = await this.failoverRouter.$get('/txs/outspends', 'json', { txids: slice.join(',') }); + for (const outspends of sliceOutspends) { + allOutspends.push(outspends); + } + } + return allOutspends; + } + public startHealthChecks(): void { this.failoverRouter.startHealthChecks(); } diff --git a/backend/src/tasks/lightning/forensics.service.ts b/backend/src/tasks/lightning/forensics.service.ts index 65ea61dc1..8a9bb825f 100644 --- a/backend/src/tasks/lightning/forensics.service.ts +++ b/backend/src/tasks/lightning/forensics.service.ts @@ -15,8 +15,6 @@ class ForensicsService { txCache: { [txid: string]: IEsploraApi.Transaction } = {}; tempCached: string[] = []; - constructor() {} - public async $startService(): Promise { logger.info('Starting lightning network forensics service'); @@ -66,93 +64,154 @@ class ForensicsService { */ public async $runClosedChannelsForensics(onlyNewChannels: boolean = false): Promise { + // Only Esplora backend can retrieve spent transaction outputs if (config.MEMPOOL.BACKEND !== 'esplora') { return; } - let progress = 0; - try { logger.debug(`Started running closed channel forensics...`); - let channels; + let remainingChannels; if (onlyNewChannels) { - channels = await channelsApi.$getClosedChannelsWithoutReason(); + remainingChannels = await channelsApi.$getClosedChannelsWithoutReason(); } else { - channels = await channelsApi.$getUnresolvedClosedChannels(); + remainingChannels = await channelsApi.$getUnresolvedClosedChannels(); } - for (const channel of channels) { - let reason = 0; - let resolvedForceClose = false; - // Only Esplora backend can retrieve spent transaction outputs - const cached: string[] = []; + let progress = 0; + const sliceLength = 1000; + // process batches of 1000 channels + for (let i = 0; i < Math.ceil(remainingChannels.length / sliceLength); i++) { + const channels = remainingChannels.slice(i * sliceLength, (i + 1) * sliceLength); + + let allOutspends: IEsploraApi.Outspend[][] = []; + const forceClosedChannels: { channel: any, cachedSpends: string[] }[] = []; + + // fetch outspends in bulk try { - let outspends: IEsploraApi.Outspend[] | undefined; - try { - outspends = await bitcoinApi.$getOutspends(channel.closing_transaction_id); - await Common.sleep$(config.LIGHTNING.FORENSICS_RATE_LIMIT); - } catch (e) { - logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/tx/' + channel.closing_transaction_id + '/outspends'}. Reason ${e instanceof Error ? e.message : e}`); - continue; - } - const lightningScriptReasons: number[] = []; - for (const outspend of outspends) { - if (outspend.spent && outspend.txid) { - let spendingTx = await this.fetchTransaction(outspend.txid); - if (!spendingTx) { - continue; + const outspendTxids = channels.map(channel => channel.closing_transaction_id); + allOutspends = await bitcoinApi.$getBatchedOutspendsInternal(outspendTxids); + logger.info(`Fetched outspends for ${allOutspends.length} txs from esplora for lightning forensics`); + await Common.sleep$(config.LIGHTNING.FORENSICS_RATE_LIMIT); + } catch (e) { + logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/txs/outspends'}. Reason ${e instanceof Error ? e.message : e}`); + } + // fetch spending transactions in bulk and load into txCache + try { + const newSpendingTxids: { [txid: string]: boolean } = {}; + for (const outspends of allOutspends) { + for (const outspend of outspends) { + if (outspend.spent && outspend.txid) { + if (!this.txCache[outspend.txid]) { + newSpendingTxids[outspend.txid] = true; + } } - cached.push(spendingTx.txid); - const lightningScript = this.findLightningScript(spendingTx.vin[outspend.vin || 0]); - lightningScriptReasons.push(lightningScript); } } - const filteredReasons = lightningScriptReasons.filter((r) => r !== 1); - if (filteredReasons.length) { - if (filteredReasons.some((r) => r === 2 || r === 4)) { - reason = 3; - } else { - reason = 2; - resolvedForceClose = true; - } - } else { - /* - We can detect a commitment transaction (force close) by reading Sequence and Locktime - https://github.com/lightning/bolts/blob/master/03-transactions.md#commitment-transaction - */ - let closingTx = await this.fetchTransaction(channel.closing_transaction_id, true); - if (!closingTx) { + const allOutspendTxs = await bitcoinApi.$getRawTransactions(Object.keys(newSpendingTxids)); + logger.info(`Fetched ${allOutspendTxs.length} out-spending txs from esplora for lightning forensics`); + for (const tx of allOutspendTxs) { + this.txCache[tx.txid] = tx; + } + } catch (e) { + logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/txs'}. Reason ${e instanceof Error ? e.message : e}`); + } + + // process each outspend + for (const [index, channel] of channels.entries()) { + let reason = 0; + const cached: string[] = []; + try { + const outspends = allOutspends[index]; + if (!outspends || !outspends.length) { + // outspends are missing continue; } - cached.push(closingTx.txid); - const sequenceHex: string = closingTx.vin[0].sequence.toString(16); - const locktimeHex: string = closingTx.locktime.toString(16); - if (sequenceHex.substring(0, 2) === '80' && locktimeHex.substring(0, 2) === '20') { - reason = 2; // Here we can't be sure if it's a penalty or not - } else { - reason = 1; + const lightningScriptReasons: number[] = []; + for (const outspend of outspends) { + if (outspend.spent && outspend.txid) { + const spendingTx = this.txCache[outspend.txid]; + if (!spendingTx) { + continue; + } + cached.push(spendingTx.txid); + const lightningScript = this.findLightningScript(spendingTx.vin[outspend.vin || 0]); + lightningScriptReasons.push(lightningScript); + } } - } - if (reason) { - logger.debug('Setting closing reason ' + reason + ' for channel: ' + channel.id + '.'); - await DB.query(`UPDATE channels SET closing_reason = ? WHERE id = ?`, [reason, channel.id]); - if (reason === 2 && resolvedForceClose) { - await DB.query(`UPDATE channels SET closing_resolved = ? WHERE id = ?`, [true, channel.id]); - } - if (reason !== 2 || resolvedForceClose) { + const filteredReasons = lightningScriptReasons.filter((r) => r !== 1); + if (filteredReasons.length) { + if (filteredReasons.some((r) => r === 2 || r === 4)) { + // Force closed with penalty + reason = 3; + } else { + // Force closed without penalty + reason = 2; + await DB.query(`UPDATE channels SET closing_resolved = ? WHERE id = ?`, [true, channel.id]); + } + await DB.query(`UPDATE channels SET closing_reason = ? WHERE id = ?`, [reason, channel.id]); + // clean up cached transactions cached.forEach(txid => { delete this.txCache[txid]; }); + } else { + forceClosedChannels.push({ channel, cachedSpends: cached }); } + } catch (e) { + logger.err(`$runClosedChannelsForensics() failed for channel ${channel.short_id}. Reason: ${e instanceof Error ? e.message : e}`); } - } catch (e) { - logger.err(`$runClosedChannelsForensics() failed for channel ${channel.short_id}. Reason: ${e instanceof Error ? e.message : e}`); } - ++progress; + // fetch force-closing transactions in bulk + try { + const newClosingTxids: { [txid: string]: boolean } = {}; + for (const { channel } of forceClosedChannels) { + if (!this.txCache[channel.closing_transaction_id]) { + newClosingTxids[channel.closing_transaction_id] = true; + } + } + const closingTxs = await bitcoinApi.$getRawTransactions(Object.keys(newClosingTxids)); + logger.info(`Fetched ${closingTxs.length} closing txs from esplora for lightning forensics`); + for (const tx of closingTxs) { + this.txCache[tx.txid] = tx; + } + } catch (e) { + logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/txs'}. Reason ${e instanceof Error ? e.message : e}`); + } + + // process channels with no lightning script reasons + for (const { channel, cachedSpends } of forceClosedChannels) { + const closingTx = this.txCache[channel.closing_transaction_id]; + if (!closingTx) { + // no channel close transaction found yet + continue; + } + /* + We can detect a commitment transaction (force close) by reading Sequence and Locktime + https://github.com/lightning/bolts/blob/master/03-transactions.md#commitment-transaction + */ + const sequenceHex: string = closingTx.vin[0].sequence.toString(16); + const locktimeHex: string = closingTx.locktime.toString(16); + let reason; + if (sequenceHex.substring(0, 2) === '80' && locktimeHex.substring(0, 2) === '20') { + // Force closed, but we can't be sure if it's a penalty or not + reason = 2; + } else { + // Mutually closed + reason = 1; + // clean up cached transactions + delete this.txCache[closingTx.txid]; + for (const txid of cachedSpends) { + delete this.txCache[txid]; + } + } + await DB.query(`UPDATE channels SET closing_reason = ? WHERE id = ?`, [reason, channel.id]); + } + + progress += channels.length; const elapsedSeconds = Math.round((new Date().getTime() / 1000) - this.loggerTimer); if (elapsedSeconds > 10) { - logger.debug(`Updating channel closed channel forensics ${progress}/${channels.length}`); + logger.debug(`Updating channel closed channel forensics ${progress}/${remainingChannels.length}`); this.loggerTimer = new Date().getTime() / 1000; } } @@ -221,7 +280,7 @@ class ForensicsService { const channels = await channelsApi.$getChannelsWithoutSourceChecked(); for (const openChannel of channels) { - let openTx = await this.fetchTransaction(openChannel.transaction_id, true); + const openTx = await this.fetchTransaction(openChannel.transaction_id, true); if (!openTx) { continue; } @@ -276,7 +335,7 @@ class ForensicsService { // Check if a channel open tx input spends the result of a swept channel close output private async $attributeSweptChannelCloses(openChannel: ILightningApi.Channel, input: IEsploraApi.Vin): Promise { - let sweepTx = await this.fetchTransaction(input.txid, true); + const sweepTx = await this.fetchTransaction(input.txid, true); if (!sweepTx) { logger.err(`couldn't find input transaction for channel forensics ${openChannel.channel_id} ${input.txid}`); return; @@ -335,7 +394,7 @@ class ForensicsService { if (matched && !ambiguous) { // fetch closing channel transaction and perform forensics on the outputs - let prevChannelTx = await this.fetchTransaction(input.txid, true); + const prevChannelTx = await this.fetchTransaction(input.txid, true); let outspends: IEsploraApi.Outspend[] | undefined; try { outspends = await bitcoinApi.$getOutspends(input.txid); @@ -430,7 +489,7 @@ class ForensicsService { } await Common.sleep$(config.LIGHTNING.FORENSICS_RATE_LIMIT); } catch (e) { - logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/tx/' + txid + '/outspends'}. Reason ${e instanceof Error ? e.message : e}`); + logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/tx/' + txid}. Reason ${e instanceof Error ? e.message : e}`); return null; } } From 995acb238df795d2f041787ddac8a5c8fa809e77 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 18 Aug 2023 00:19:02 +0900 Subject: [PATCH 66/79] Refactor forensics batching, speed up opened channel forensics --- .../src/tasks/lightning/forensics.service.ts | 102 ++++++++++-------- 1 file changed, 56 insertions(+), 46 deletions(-) diff --git a/backend/src/tasks/lightning/forensics.service.ts b/backend/src/tasks/lightning/forensics.service.ts index 8a9bb825f..c83f1720c 100644 --- a/backend/src/tasks/lightning/forensics.service.ts +++ b/backend/src/tasks/lightning/forensics.service.ts @@ -71,18 +71,18 @@ class ForensicsService { try { logger.debug(`Started running closed channel forensics...`); - let remainingChannels; + let allChannels; if (onlyNewChannels) { - remainingChannels = await channelsApi.$getClosedChannelsWithoutReason(); + allChannels = await channelsApi.$getClosedChannelsWithoutReason(); } else { - remainingChannels = await channelsApi.$getUnresolvedClosedChannels(); + allChannels = await channelsApi.$getUnresolvedClosedChannels(); } let progress = 0; const sliceLength = 1000; // process batches of 1000 channels - for (let i = 0; i < Math.ceil(remainingChannels.length / sliceLength); i++) { - const channels = remainingChannels.slice(i * sliceLength, (i + 1) * sliceLength); + for (let i = 0; i < Math.ceil(allChannels.length / sliceLength); i++) { + const channels = allChannels.slice(i * sliceLength, (i + 1) * sliceLength); let allOutspends: IEsploraApi.Outspend[][] = []; const forceClosedChannels: { channel: any, cachedSpends: string[] }[] = []; @@ -91,31 +91,28 @@ class ForensicsService { try { const outspendTxids = channels.map(channel => channel.closing_transaction_id); allOutspends = await bitcoinApi.$getBatchedOutspendsInternal(outspendTxids); - logger.info(`Fetched outspends for ${allOutspends.length} txs from esplora for lightning forensics`); + logger.info(`Fetched outspends for ${allOutspends.length} txs from esplora for LN forensics`); await Common.sleep$(config.LIGHTNING.FORENSICS_RATE_LIMIT); } catch (e) { logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/txs/outspends'}. Reason ${e instanceof Error ? e.message : e}`); } // fetch spending transactions in bulk and load into txCache - try { - const newSpendingTxids: { [txid: string]: boolean } = {}; - for (const outspends of allOutspends) { - for (const outspend of outspends) { - if (outspend.spent && outspend.txid) { - if (!this.txCache[outspend.txid]) { - newSpendingTxids[outspend.txid] = true; - } - } + const newSpendingTxids: { [txid: string]: boolean } = {}; + for (const outspends of allOutspends) { + for (const outspend of outspends) { + if (outspend.spent && outspend.txid) { + newSpendingTxids[outspend.txid] = true; } } - const allOutspendTxs = await bitcoinApi.$getRawTransactions(Object.keys(newSpendingTxids)); - logger.info(`Fetched ${allOutspendTxs.length} out-spending txs from esplora for lightning forensics`); - for (const tx of allOutspendTxs) { - this.txCache[tx.txid] = tx; - } - } catch (e) { - logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/txs'}. Reason ${e instanceof Error ? e.message : e}`); } + const allOutspendTxs = await this.fetchTransactions( + allOutspends.flatMap(outspends => + outspends + .filter(outspend => outspend.spent && outspend.txid) + .map(outspend => outspend.txid) + ) + ); + logger.info(`Fetched ${allOutspendTxs.length} out-spending txs from esplora for LN forensics`); // process each outspend for (const [index, channel] of channels.entries()) { @@ -163,21 +160,8 @@ class ForensicsService { } // fetch force-closing transactions in bulk - try { - const newClosingTxids: { [txid: string]: boolean } = {}; - for (const { channel } of forceClosedChannels) { - if (!this.txCache[channel.closing_transaction_id]) { - newClosingTxids[channel.closing_transaction_id] = true; - } - } - const closingTxs = await bitcoinApi.$getRawTransactions(Object.keys(newClosingTxids)); - logger.info(`Fetched ${closingTxs.length} closing txs from esplora for lightning forensics`); - for (const tx of closingTxs) { - this.txCache[tx.txid] = tx; - } - } catch (e) { - logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/txs'}. Reason ${e instanceof Error ? e.message : e}`); - } + const closingTxs = await this.fetchTransactions(forceClosedChannels.map(x => x.channel.closing_transaction_id)); + logger.info(`Fetched ${closingTxs.length} closing txs from esplora for LN forensics`); // process channels with no lightning script reasons for (const { channel, cachedSpends } of forceClosedChannels) { @@ -211,7 +195,7 @@ class ForensicsService { progress += channels.length; const elapsedSeconds = Math.round((new Date().getTime() / 1000) - this.loggerTimer); if (elapsedSeconds > 10) { - logger.debug(`Updating channel closed channel forensics ${progress}/${remainingChannels.length}`); + logger.debug(`Updating channel closed channel forensics ${progress}/${allChannels.length}`); this.loggerTimer = new Date().getTime() / 1000; } } @@ -279,8 +263,11 @@ class ForensicsService { logger.debug(`Started running open channel forensics...`); const channels = await channelsApi.$getChannelsWithoutSourceChecked(); + // preload open channel transactions + await this.fetchTransactions(channels.map(channel => channel.transaction_id), true); + for (const openChannel of channels) { - const openTx = await this.fetchTransaction(openChannel.transaction_id, true); + const openTx = this.txCache[openChannel.transaction_id]; if (!openTx) { continue; } @@ -414,17 +401,17 @@ class ForensicsService { }; }); } + + // preload outspend transactions + await this.fetchTransactions(outspends.filter(o => o.spent && o.txid).map(o => o.txid), true); + for (let i = 0; i < outspends?.length; i++) { const outspend = outspends[i]; const output = prevChannel.outputs[i]; if (outspend.spent && outspend.txid) { - try { - const spendingTx = await this.fetchTransaction(outspend.txid, true); - if (spendingTx) { - output.type = this.findLightningScript(spendingTx.vin[outspend.vin || 0]); - } - } catch (e) { - logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/tx/' + outspend.txid}. Reason ${e instanceof Error ? e.message : e}`); + const spendingTx = this.txCache[outspend.txid]; + if (spendingTx) { + output.type = this.findLightningScript(spendingTx.vin[outspend.vin || 0]); } } else { output.type = 0; @@ -496,6 +483,29 @@ class ForensicsService { return tx; } + // fetches a batch of transactions and adds them to the txCache + // the returned list of txs does *not* preserve ordering or number + async fetchTransactions(txids, temp: boolean = false): Promise<(IEsploraApi.Transaction | null)[]> { + // deduplicate txids + const uniqueTxids = [...new Set(txids)]; + // filter out any transactions we already have in the cache + const needToFetch: string[] = uniqueTxids.filter(txid => !this.txCache[txid]); + try { + const txs = await bitcoinApi.$getRawTransactions(needToFetch); + for (const tx of txs) { + this.txCache[tx.txid] = tx; + if (temp) { + this.tempCached.push(tx.txid); + } + } + await Common.sleep$(config.LIGHTNING.FORENSICS_RATE_LIMIT); + } catch (e) { + logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/txs'}. Reason ${e instanceof Error ? e.message : e}`); + return []; + } + return txids.map(txid => this.txCache[txid]); + } + clearTempCache(): void { for (const txid of this.tempCached) { delete this.txCache[txid]; From 70badaf4615e3d258a6cd15d4b12a09f085cbd6c Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 18 Aug 2023 02:47:32 +0900 Subject: [PATCH 67/79] Speed up $scanForClosedChannels, use internal outspends apis --- .../bitcoin/bitcoin-api-abstract-factory.ts | 1 + backend/src/api/bitcoin/bitcoin-api.ts | 9 ++++++ backend/src/api/bitcoin/esplora-api.ts | 15 +++------ .../src/tasks/lightning/forensics.service.ts | 2 +- .../tasks/lightning/network-sync.service.ts | 32 ++++++++++++------- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts index 6f20dad92..f008e5ed8 100644 --- a/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts +++ b/backend/src/api/bitcoin/bitcoin-api-abstract-factory.ts @@ -25,6 +25,7 @@ export interface AbstractBitcoinApi { $getOutspends(txId: string): Promise; $getBatchedOutspends(txId: string[]): Promise; $getBatchedOutspendsInternal(txId: string[]): Promise; + $getOutSpendsByOutpoint(outpoints: { txid: string, vout: number }[]): Promise; startHealthChecks(): void; } diff --git a/backend/src/api/bitcoin/bitcoin-api.ts b/backend/src/api/bitcoin/bitcoin-api.ts index 9e4cbdd8b..1722334df 100644 --- a/backend/src/api/bitcoin/bitcoin-api.ts +++ b/backend/src/api/bitcoin/bitcoin-api.ts @@ -215,6 +215,15 @@ class BitcoinApi implements AbstractBitcoinApi { return this.$getBatchedOutspends(txId); } + async $getOutSpendsByOutpoint(outpoints: { txid: string, vout: number }[]): Promise { + const outspends: IEsploraApi.Outspend[] = []; + for (const outpoint of outpoints) { + const outspend = await this.$getOutspend(outpoint.txid, outpoint.vout); + outspends.push(outspend); + } + return outspends; + } + $getEstimatedHashrate(blockHeight: number): Promise { // 120 is the default block span in Core return this.bitcoindClient.getNetworkHashPs(120, blockHeight); diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index 574113ae6..2beebe270 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -302,16 +302,11 @@ class ElectrsApi implements AbstractBitcoinApi { } async $getBatchedOutspendsInternal(txids: string[]): Promise { - const allOutspends: IEsploraApi.Outspend[][] = []; - const sliceLength = 50; - for (let i = 0; i < Math.ceil(txids.length / sliceLength); i++) { - const slice = txids.slice(i * sliceLength, (i + 1) * sliceLength); - const sliceOutspends = await this.failoverRouter.$get('/txs/outspends', 'json', { txids: slice.join(',') }); - for (const outspends of sliceOutspends) { - allOutspends.push(outspends); - } - } - return allOutspends; + return this.failoverRouter.$post('/internal-api/txs/outspends/by-txid', txids, 'json'); + } + + async $getOutSpendsByOutpoint(outpoints: { txid: string, vout: number }[]): Promise { + return this.failoverRouter.$post('/internal-api/txs/outspends/by-outpoint', outpoints.map(out => `${out.txid}:${out.vout}`), 'json'); } public startHealthChecks(): void { diff --git a/backend/src/tasks/lightning/forensics.service.ts b/backend/src/tasks/lightning/forensics.service.ts index c83f1720c..1cbf7d647 100644 --- a/backend/src/tasks/lightning/forensics.service.ts +++ b/backend/src/tasks/lightning/forensics.service.ts @@ -94,7 +94,7 @@ class ForensicsService { logger.info(`Fetched outspends for ${allOutspends.length} txs from esplora for LN forensics`); await Common.sleep$(config.LIGHTNING.FORENSICS_RATE_LIMIT); } catch (e) { - logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/txs/outspends'}. Reason ${e instanceof Error ? e.message : e}`); + logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/internal-api/txs/outspends/by-txid'}. Reason ${e instanceof Error ? e.message : e}`); } // fetch spending transactions in bulk and load into txCache const newSpendingTxids: { [txid: string]: boolean } = {}; diff --git a/backend/src/tasks/lightning/network-sync.service.ts b/backend/src/tasks/lightning/network-sync.service.ts index 963b9e8c2..dc0d609fa 100644 --- a/backend/src/tasks/lightning/network-sync.service.ts +++ b/backend/src/tasks/lightning/network-sync.service.ts @@ -288,22 +288,32 @@ class NetworkSyncService { } logger.debug(`${log}`, logger.tags.ln); - const channels = await channelsApi.$getChannelsByStatus([0, 1]); - for (const channel of channels) { - const spendingTx = await bitcoinApi.$getOutspend(channel.transaction_id, channel.transaction_vout); - if (spendingTx.spent === true && spendingTx.status?.confirmed === true) { - logger.debug(`Marking channel: ${channel.id} as closed.`, logger.tags.ln); - await DB.query(`UPDATE channels SET status = 2, closing_date = FROM_UNIXTIME(?) WHERE id = ?`, - [spendingTx.status.block_time, channel.id]); - if (spendingTx.txid && !channel.closing_transaction_id) { - await DB.query(`UPDATE channels SET closing_transaction_id = ? WHERE id = ?`, [spendingTx.txid, channel.id]); + const allChannels = await channelsApi.$getChannelsByStatus([0, 1]); + + const sliceLength = 5000; + // process batches of 5000 channels + for (let i = 0; i < Math.ceil(allChannels.length / sliceLength); i++) { + const channels = allChannels.slice(i * sliceLength, (i + 1) * sliceLength); + const outspends = await bitcoinApi.$getOutSpendsByOutpoint(channels.map(channel => { + return { txid: channel.transaction_id, vout: channel.transaction_vout }; + })); + + for (const [index, channel] of channels.entries()) { + const spendingTx = outspends[index]; + if (spendingTx.spent === true && spendingTx.status?.confirmed === true) { + // logger.debug(`Marking channel: ${channel.id} as closed.`, logger.tags.ln); + await DB.query(`UPDATE channels SET status = 2, closing_date = FROM_UNIXTIME(?) WHERE id = ?`, + [spendingTx.status.block_time, channel.id]); + if (spendingTx.txid && !channel.closing_transaction_id) { + await DB.query(`UPDATE channels SET closing_transaction_id = ? WHERE id = ?`, [spendingTx.txid, channel.id]); + } } } - ++progress; + progress += channels.length; const elapsedSeconds = Math.round((new Date().getTime() / 1000) - this.loggerTimer); if (elapsedSeconds > config.LIGHTNING.LOGGER_UPDATE_INTERVAL) { - logger.debug(`Checking if channel has been closed ${progress}/${channels.length}`, logger.tags.ln); + logger.debug(`Checking if channel has been closed ${progress}/${allChannels.length}`, logger.tags.ln); this.loggerTimer = new Date().getTime() / 1000; } } From b9217da45301f3c4d752d9d5214b30fb018cd4cd Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 28 Aug 2023 16:52:28 +0900 Subject: [PATCH 68/79] Change internal API prefix to "internal" --- backend/src/api/bitcoin/esplora-api.ts | 4 ++-- backend/src/tasks/lightning/forensics.service.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index 2beebe270..dda584ec4 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -302,11 +302,11 @@ class ElectrsApi implements AbstractBitcoinApi { } async $getBatchedOutspendsInternal(txids: string[]): Promise { - return this.failoverRouter.$post('/internal-api/txs/outspends/by-txid', txids, 'json'); + return this.failoverRouter.$post('/internal/txs/outspends/by-txid', txids, 'json'); } async $getOutSpendsByOutpoint(outpoints: { txid: string, vout: number }[]): Promise { - return this.failoverRouter.$post('/internal-api/txs/outspends/by-outpoint', outpoints.map(out => `${out.txid}:${out.vout}`), 'json'); + return this.failoverRouter.$post('/internal/txs/outspends/by-outpoint', outpoints.map(out => `${out.txid}:${out.vout}`), 'json'); } public startHealthChecks(): void { diff --git a/backend/src/tasks/lightning/forensics.service.ts b/backend/src/tasks/lightning/forensics.service.ts index 1cbf7d647..584dd3c79 100644 --- a/backend/src/tasks/lightning/forensics.service.ts +++ b/backend/src/tasks/lightning/forensics.service.ts @@ -94,7 +94,7 @@ class ForensicsService { logger.info(`Fetched outspends for ${allOutspends.length} txs from esplora for LN forensics`); await Common.sleep$(config.LIGHTNING.FORENSICS_RATE_LIMIT); } catch (e) { - logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/internal-api/txs/outspends/by-txid'}. Reason ${e instanceof Error ? e.message : e}`); + logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/internal/txs/outspends/by-txid'}. Reason ${e instanceof Error ? e.message : e}`); } // fetch spending transactions in bulk and load into txCache const newSpendingTxids: { [txid: string]: boolean } = {}; From fc52514c3163d9c22635bda1b5e24712f66049a0 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 12 Nov 2023 07:59:06 +0000 Subject: [PATCH 69/79] restore incoming tx clearance line, smooth moving avg --- .../incoming-transactions-graph.component.ts | 72 +++++++------------ frontend/src/app/graphs/echarts.ts | 4 +- 2 files changed, 26 insertions(+), 50 deletions(-) diff --git a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts index 1b68a5a99..667ad1e28 100644 --- a/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts +++ b/frontend/src/app/components/incoming-transactions-graph/incoming-transactions-graph.component.ts @@ -63,7 +63,8 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On return; } this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference'); - this.MA = this.calculateMA(this.data.series[0]); + const windowSize = Math.max(10, Math.floor(this.data.series[0].length / 8)); + this.MA = this.calculateMA(this.data.series[0], windowSize); this.mountChart(); } @@ -74,33 +75,22 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On this.isLoading = false; } - /// calculate the moving average of maData - calculateMA(maData): number[][] { + /// calculate the moving average of the provided data based on windowSize + calculateMA(data: number[][], windowSize: number = 100): number[][] { //update const variables that are not changed const ma: number[][] = []; let sum = 0; let i = 0; - const len = maData.length; - - //Adjust window length based on the length of the data - //5% appeared as a good amount from tests - //TODO: make this a text box in the UI - const maWindowLen = Math.ceil(len * 0.05); - - //calculate the center of the moving average window - const center = Math.floor(maWindowLen / 2); //calculate the centered moving average - for (i = center; i < len - center; i++) { - sum = 0; - //build out ma as we loop through the data - ma[i] = []; - ma[i].push(maData[i][0]); - for (let j = i - center; j <= i + center; j++) { - sum += maData[j][1]; + for (i = 0; i < data.length; i++) { + sum += data[i][1]; + if (i >= windowSize) { + sum -= data[i - windowSize][1]; + const midpoint = i - Math.floor(windowSize / 2); + const avg = sum / windowSize; + ma.push([data[midpoint][0], avg]); } - - ma[i].push(sum / maWindowLen); } //return the moving average array @@ -138,36 +128,22 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On } }], } - }, - { - zlevel: 0, - name: 'MA', - data: this.MA, - type: 'line', - smooth: false, - showSymbol: false, - symbol: 'none', - lineStyle: { - width: 1, - color: "white", - }, - markLine: { - silent: true, + }); + if (this.template !== 'widget') { + seriesGraph.push({ + zlevel: 0, + name: 'MA', + data: this.MA, + type: 'line', + smooth: false, + showSymbol: false, symbol: 'none', lineStyle: { - color: '#fff', - opacity: 1, width: 2, - }, - data: [{ - yAxis: 1667, - label: { - show: false, - color: '#ffffff', - } - }], - } - }); + color: "white", + } + }); + } this.mempoolStatsChartOption = { grid: { diff --git a/frontend/src/app/graphs/echarts.ts b/frontend/src/app/graphs/echarts.ts index 588e5e5f1..342867168 100644 --- a/frontend/src/app/graphs/echarts.ts +++ b/frontend/src/app/graphs/echarts.ts @@ -1,7 +1,7 @@ // Import tree-shakeable echarts import * as echarts from 'echarts/core'; import { LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart } from 'echarts/charts'; -import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, DataZoomComponent, VisualMapComponent } from 'echarts/components'; +import { TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, DataZoomComponent, VisualMapComponent, MarkLineComponent } from 'echarts/components'; import { SVGRenderer, CanvasRenderer } from 'echarts/renderers'; // Typescript interfaces import { EChartsOption, TreemapSeriesOption, LineSeriesOption, PieSeriesOption } from 'echarts'; @@ -11,7 +11,7 @@ echarts.use([ SVGRenderer, CanvasRenderer, TitleComponent, TooltipComponent, GridComponent, LegendComponent, GeoComponent, DataZoomComponent, - VisualMapComponent, + VisualMapComponent, MarkLineComponent, LineChart, LinesChart, BarChart, TreemapChart, PieChart, ScatterChart ]); export { echarts, EChartsOption, TreemapSeriesOption, LineSeriesOption, PieSeriesOption }; \ No newline at end of file From 5998b54fec492da40d85779bd790b656890f03a9 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 12 Nov 2023 09:23:37 +0000 Subject: [PATCH 70/79] more logs, reduce request chunk sizes --- backend/src/api/bitcoin/esplora-api.ts | 3 ++- backend/src/api/rbf-cache.ts | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index 8f47921c2..d980ad980 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -181,7 +181,8 @@ class FailoverRouter { .catch((e) => { let fallbackHost = this.fallbackHost; if (e?.response?.status !== 404) { - logger.warn(`esplora request failed ${e?.response?.status || 500} ${host.host}${path}`); + logger.warn(`esplora request failed ${e?.response?.status} ${host.host}${path}`); + logger.warn(e instanceof Error ? e.message : e); fallbackHost = this.addFailure(host); } if (retry && e?.code === 'ECONNREFUSED' && this.multihost) { diff --git a/backend/src/api/rbf-cache.ts b/backend/src/api/rbf-cache.ts index 950e3a4e5..33653a33c 100644 --- a/backend/src/api/rbf-cache.ts +++ b/backend/src/api/rbf-cache.ts @@ -480,14 +480,16 @@ class RbfCache { }; if (config.MEMPOOL.BACKEND === 'esplora') { - const sliceLength = 10000; + const sliceLength = 1000; for (let i = 0; i < Math.ceil(txids.length / sliceLength); i++) { const slice = txids.slice(i * sliceLength, (i + 1) * sliceLength); try { const txs = await bitcoinApi.$getRawTransactions(slice); + logger.debug(`fetched ${slice.length} cached rbf transactions`); processTxs(txs); + logger.debug(`processed ${slice.length} cached rbf transactions`); } catch (err) { - logger.err('failed to fetch some cached rbf transactions'); + logger.err(`failed to fetch or process ${slice.length} cached rbf transactions`); } } } else { From c0a481acbe1f4cb1c4db40c32810819f4eed9071 Mon Sep 17 00:00:00 2001 From: wiz Date: Sun, 12 Nov 2023 19:09:36 +0900 Subject: [PATCH 71/79] Reduce /internal/txs RBF cache query chunk size to 250 --- backend/src/api/rbf-cache.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/rbf-cache.ts b/backend/src/api/rbf-cache.ts index 33653a33c..c573d3291 100644 --- a/backend/src/api/rbf-cache.ts +++ b/backend/src/api/rbf-cache.ts @@ -480,7 +480,7 @@ class RbfCache { }; if (config.MEMPOOL.BACKEND === 'esplora') { - const sliceLength = 1000; + const sliceLength = 250; for (let i = 0; i < Math.ceil(txids.length / sliceLength); i++) { const slice = txids.slice(i * sliceLength, (i + 1) * sliceLength); try { From ea2a7e7505799f6e1122212cf935dda4ea3eeb1d Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 16 Sep 2023 02:10:38 +0000 Subject: [PATCH 72/79] Use sigops from mempool/electrs, fix the nodejs sigop calculation --- backend/src/api/bitcoin/esplora-api.interface.ts | 1 + backend/src/api/transaction-utils.ts | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/backend/src/api/bitcoin/esplora-api.interface.ts b/backend/src/api/bitcoin/esplora-api.interface.ts index 55abe1d34..0a0960e46 100644 --- a/backend/src/api/bitcoin/esplora-api.interface.ts +++ b/backend/src/api/bitcoin/esplora-api.interface.ts @@ -6,6 +6,7 @@ export namespace IEsploraApi { size: number; weight: number; fee: number; + sigops?: number; vin: Vin[]; vout: Vout[]; status: Status; diff --git a/backend/src/api/transaction-utils.ts b/backend/src/api/transaction-utils.ts index ef4a34012..d4f130aa6 100644 --- a/backend/src/api/transaction-utils.ts +++ b/backend/src/api/transaction-utils.ts @@ -116,7 +116,10 @@ class TransactionUtils { public extendMempoolTransaction(transaction: IEsploraApi.Transaction): MempoolTransactionExtended { const vsize = Math.ceil(transaction.weight / 4); const fractionalVsize = (transaction.weight / 4); - const sigops = !Common.isLiquid() ? this.countSigops(transaction) : 0; + let sigops = transaction.sigops; + if (sigops == null) { + sigops = !Common.isLiquid() ? this.countSigops(transaction) : 0; + } // https://github.com/bitcoin/bitcoin/blob/e9262ea32a6e1d364fb7974844fadc36f931f8c6/src/policy/policy.cpp#L295-L298 const adjustedVsize = Math.max(fractionalVsize, sigops * 5); // adjusted vsize = Max(weight, sigops * bytes_per_sigop) / witness_scale_factor const feePerVbytes = (transaction.fee || 0) / fractionalVsize; @@ -155,7 +158,7 @@ class TransactionUtils { sigops += 20 * (script.match(/OP_CHECKMULTISIG/g)?.length || 0); } else { // in redeem scripts and witnesses, worth N if preceded by OP_N, 20 otherwise - const matches = script.matchAll(/(?:OP_(\d+))? OP_CHECKMULTISIG/g); + const matches = script.matchAll(/(?:OP_(?:PUSHNUM_)?(\d+))? OP_CHECKMULTISIG/g); for (const match of matches) { const n = parseInt(match[1]); if (Number.isInteger(n)) { @@ -189,6 +192,12 @@ class TransactionUtils { sigops += this.countScriptSigops(bitcoinjs.script.toASM(Buffer.from(input.witness[input.witness.length - 1], 'hex')), false, true); } break; + + case input.prevout.scriptpubkey_type === 'p2sh': + if (input.inner_redeemscript_asm) { + sigops += this.countScriptSigops(input.inner_redeemscript_asm); + } + break; } } } From 72e19f674ac77421d92569ffab7d0267d7edd7da Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 16 Sep 2023 13:02:07 +0000 Subject: [PATCH 73/79] Clear Liquid sigops --- backend/src/api/transaction-utils.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/backend/src/api/transaction-utils.ts b/backend/src/api/transaction-utils.ts index d4f130aa6..6ff1c10b7 100644 --- a/backend/src/api/transaction-utils.ts +++ b/backend/src/api/transaction-utils.ts @@ -116,10 +116,7 @@ class TransactionUtils { public extendMempoolTransaction(transaction: IEsploraApi.Transaction): MempoolTransactionExtended { const vsize = Math.ceil(transaction.weight / 4); const fractionalVsize = (transaction.weight / 4); - let sigops = transaction.sigops; - if (sigops == null) { - sigops = !Common.isLiquid() ? this.countSigops(transaction) : 0; - } + let sigops = Common.isLiquid() ? 0 : (transaction.sigops != null ? transaction.sigops : this.countSigops(transaction)); // https://github.com/bitcoin/bitcoin/blob/e9262ea32a6e1d364fb7974844fadc36f931f8c6/src/policy/policy.cpp#L295-L298 const adjustedVsize = Math.max(fractionalVsize, sigops * 5); // adjusted vsize = Max(weight, sigops * bytes_per_sigop) / witness_scale_factor const feePerVbytes = (transaction.fee || 0) / fractionalVsize; From a510b4992c14b8e0c086f422b9a4b641da34cfb3 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 16 Sep 2023 13:02:47 +0000 Subject: [PATCH 74/79] Fix condition to add mempool data to loaded cache txs --- backend/src/api/mempool.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/mempool.ts b/backend/src/api/mempool.ts index 73260dc9e..f5e788f98 100644 --- a/backend/src/api/mempool.ts +++ b/backend/src/api/mempool.ts @@ -94,7 +94,7 @@ class Mempool { logger.debug(`Migrating ${Object.keys(this.mempoolCache).length} transactions from disk cache to Redis cache`); } for (const txid of Object.keys(this.mempoolCache)) { - if (!this.mempoolCache[txid].sigops || this.mempoolCache[txid].effectiveFeePerVsize == null) { + if (!this.mempoolCache[txid].adjustedVsize || this.mempoolCache[txid].sigops == null || this.mempoolCache[txid].effectiveFeePerVsize == null) { this.mempoolCache[txid] = transactionUtils.extendMempoolTransaction(this.mempoolCache[txid]); } if (this.mempoolCache[txid].order == null) { From 4ac0a6dad2556749c5542d1c359bc489eee3331a Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 19 Sep 2023 00:18:52 +0000 Subject: [PATCH 75/79] Display sigops on all transactions --- .../transaction/transaction.component.html | 8 ++++---- .../components/transaction/transaction.component.ts | 12 ++++++++++++ frontend/src/app/interfaces/electrs.interface.ts | 1 + 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index 006870864..f062662dc 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -292,9 +292,9 @@ Virtual size - + Adjusted vsize - + Weight @@ -314,9 +314,9 @@ Locktime - + Sigops - + Transaction hex diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 4743e5cd6..5260cd668 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -62,6 +62,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { rbfReplaces: string[]; rbfInfo: RbfTree; cpfpInfo: CpfpInfo | null; + sigops: number | null; + adjustedVsize: number | null; showCpfpDetails = false; fetchCpfp$ = new Subject(); fetchRbfHistory$ = new Subject(); @@ -343,6 +345,10 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { if (tx.fee === undefined) { this.tx.fee = 0; } + if (this.tx.sigops != null) { + this.sigops = this.tx.sigops; + this.adjustedVsize = Math.max(this.tx.weight / 4, this.sigops * 5); + } this.tx.feePerVsize = tx.fee / (tx.weight / 4); this.isLoadingTx = false; this.error = undefined; @@ -543,6 +549,10 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { } this.cpfpInfo = cpfpInfo; + if (this.cpfpInfo.adjustedVsize && this.cpfpInfo.sigops != null) { + this.sigops = this.cpfpInfo.sigops; + this.adjustedVsize = this.cpfpInfo.adjustedVsize; + } this.hasEffectiveFeeRate = hasRelatives || (this.tx.effectiveFeePerVsize && (Math.abs(this.tx.effectiveFeePerVsize - this.tx.feePerVsize) > 0.01)); } @@ -569,6 +579,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.replaced = false; this.transactionTime = -1; this.cpfpInfo = null; + this.adjustedVsize = null; + this.sigops = null; this.hasEffectiveFeeRate = false; this.rbfInfo = null; this.rbfReplaces = []; diff --git a/frontend/src/app/interfaces/electrs.interface.ts b/frontend/src/app/interfaces/electrs.interface.ts index 2d604a9de..58a02ad79 100644 --- a/frontend/src/app/interfaces/electrs.interface.ts +++ b/frontend/src/app/interfaces/electrs.interface.ts @@ -26,6 +26,7 @@ export interface Transaction { _outspends?: Outspend[]; _channels?: TransactionChannels; price?: Price; + sigops?: number; } export interface TransactionChannels { From 29299e622ec8cd7a96703900fcdbf2f6014b5b4d Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 13 Nov 2023 03:42:12 +0000 Subject: [PATCH 76/79] Calculate sigops in /api/v1/tx/:txid for mined txs --- backend/src/api/bitcoin/bitcoin.routes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index 240fb07ce..105b0be14 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -251,7 +251,7 @@ class BitcoinRoutes { private async getTransaction(req: Request, res: Response) { try { - const transaction = await transactionUtils.$getTransactionExtended(req.params.txId, true); + const transaction = await transactionUtils.$getTransactionExtended(req.params.txId, true, false, false, true); res.json(transaction); } catch (e) { let statusCode = 500; From 3639dcc92aef898fd9ede92a8e8aa8a87ea421e9 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 10 Nov 2023 07:45:02 +0000 Subject: [PATCH 77/79] Recover from stale PID file --- backend/src/database.ts | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/backend/src/database.ts b/backend/src/database.ts index c27f28d23..655dad47c 100644 --- a/backend/src/database.ts +++ b/backend/src/database.ts @@ -4,6 +4,7 @@ import config from './config'; import { createPool, Pool, PoolConnection } from 'mysql2/promise'; import logger from './logger'; import { FieldPacket, OkPacket, PoolOptions, ResultSetHeader, RowDataPacket } from 'mysql2/typings/mysql'; +import { execSync } from 'child_process'; class DB { constructor() { @@ -105,18 +106,34 @@ import { FieldPacket, OkPacket, PoolOptions, ResultSetHeader, RowDataPacket } fr public getPidLock(): boolean { const filePath = path.join(config.DATABASE.PID_DIR || __dirname, `/mempool-${config.DATABASE.DATABASE}.pid`); + this.enforcePidLock(filePath); + fs.writeFileSync(filePath, `${process.pid}`); + return true; + } + + private enforcePidLock(filePath: string): void { if (fs.existsSync(filePath)) { - const pid = fs.readFileSync(filePath).toString(); - if (pid !== `${process.pid}`) { - const msg = `Already running on PID ${pid} (or pid file '${filePath}' is stale)`; + const pid = parseInt(fs.readFileSync(filePath, 'utf-8')); + if (pid === process.pid) { + logger.warn('PID file already exists for this process'); + return; + } + + let cmd; + try { + cmd = execSync(`ps -p ${pid} -o args=`); + } catch (e) { + logger.warn(`Stale PID file at ${filePath}, but no process running on that PID ${pid}`); + return; + } + + if (cmd && cmd.toString()?.includes('node')) { + const msg = `Another mempool nodejs process is already running on PID ${pid}`; logger.err(msg); throw new Error(msg); } else { - return true; + logger.warn(`Stale PID file at ${filePath}, but the PID ${pid} does not belong to a running mempool instance`); } - } else { - fs.writeFileSync(filePath, `${process.pid}`); - return true; } } @@ -124,6 +141,7 @@ import { FieldPacket, OkPacket, PoolOptions, ResultSetHeader, RowDataPacket } fr const filePath = path.join(config.DATABASE.PID_DIR || __dirname, `/mempool-${config.DATABASE.DATABASE}.pid`); if (fs.existsSync(filePath)) { const pid = fs.readFileSync(filePath).toString(); + // only release our own pid file if (pid === `${process.pid}`) { fs.unlinkSync(filePath); } From 8c4b488251cc2aae70d142a78715b33e2ad3431c Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 13 Nov 2023 07:33:53 +0000 Subject: [PATCH 78/79] handle SIGHUP exit code --- backend/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index e7e1afa3d..59c92fbf3 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -92,7 +92,7 @@ class Server { logger.notice(`Starting Mempool Server${worker ? ' (worker)' : ''}... (${backendInfo.getShortCommitHash()})`); // Register cleanup listeners for exit events - ['exit', 'SIGINT', 'SIGTERM', 'SIGUSR1', 'SIGUSR2', 'uncaughtException', 'unhandledRejection'].forEach(event => { + ['exit', 'SIGHUP', 'SIGINT', 'SIGTERM', 'SIGUSR1', 'SIGUSR2', 'uncaughtException', 'unhandledRejection'].forEach(event => { process.on(event, () => { this.onExit(event); }); }); From c6a92083a8621e50a7b5410ea9909d4e4233da0a Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 13 Nov 2023 07:53:13 +0000 Subject: [PATCH 79/79] Fix pid parsing on release lock --- backend/src/database.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/database.ts b/backend/src/database.ts index 655dad47c..0638aa319 100644 --- a/backend/src/database.ts +++ b/backend/src/database.ts @@ -140,9 +140,9 @@ import { execSync } from 'child_process'; public releasePidLock(): void { const filePath = path.join(config.DATABASE.PID_DIR || __dirname, `/mempool-${config.DATABASE.DATABASE}.pid`); if (fs.existsSync(filePath)) { - const pid = fs.readFileSync(filePath).toString(); + const pid = parseInt(fs.readFileSync(filePath, 'utf-8')); // only release our own pid file - if (pid === `${process.pid}`) { + if (pid === process.pid) { fs.unlinkSync(filePath); } }