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/85] 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/85] 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/85] 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/85] 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/85] 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 f295392dcab8c2b8c7beef54503c8cb5790e9578 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 15 Aug 2023 20:54:03 +0900 Subject: [PATCH 06/85] Record purging rate in statistics --- backend/src/api/database-migration.ts | 7 ++++++- backend/src/api/statistics/statistics-api.ts | 10 ++++++++-- backend/src/api/statistics/statistics.ts | 4 ++++ backend/src/mempool.interfaces.ts | 2 ++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/backend/src/api/database-migration.ts b/backend/src/api/database-migration.ts index b7dc39493..89ef7a7be 100644 --- a/backend/src/api/database-migration.ts +++ b/backend/src/api/database-migration.ts @@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository'; import { RowDataPacket } from 'mysql2'; class DatabaseMigration { - private static currentVersion = 65; + private static currentVersion = 66; private queryTimeout = 3600_000; private statisticsAddedIndexed = false; private uniqueLogs: string[] = []; @@ -553,6 +553,11 @@ class DatabaseMigration { await this.$executeQuery('ALTER TABLE `blocks_audits` ADD accelerated_txs JSON DEFAULT "[]"'); await this.updateToSchemaVersion(65); } + + if (databaseSchemaVersion < 66) { + await this.$executeQuery('ALTER TABLE `statistics` ADD min_fee FLOAT UNSIGNED DEFAULT NULL'); + await this.updateToSchemaVersion(66); + } } /** diff --git a/backend/src/api/statistics/statistics-api.ts b/backend/src/api/statistics/statistics-api.ts index 9df12d704..d76b77a37 100644 --- a/backend/src/api/statistics/statistics-api.ts +++ b/backend/src/api/statistics/statistics-api.ts @@ -15,6 +15,7 @@ class StatisticsApi { mempool_byte_weight, fee_data, total_fee, + min_fee, vsize_1, vsize_2, vsize_3, @@ -54,7 +55,7 @@ class StatisticsApi { vsize_1800, vsize_2000 ) - VALUES (NOW(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + VALUES (NOW(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)`; const [result]: any = await DB.query(query); return result.insertId; @@ -73,6 +74,7 @@ class StatisticsApi { mempool_byte_weight, fee_data, total_fee, + min_fee, vsize_1, vsize_2, vsize_3, @@ -112,7 +114,7 @@ class StatisticsApi { vsize_1800, vsize_2000 ) - VALUES (${statistics.added}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, + VALUES (${statistics.added}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; const params: (string | number)[] = [ @@ -122,6 +124,7 @@ class StatisticsApi { statistics.mempool_byte_weight, statistics.fee_data, statistics.total_fee, + statistics.min_fee, statistics.vsize_1, statistics.vsize_2, statistics.vsize_3, @@ -172,6 +175,7 @@ class StatisticsApi { return `SELECT UNIX_TIMESTAMP(added) as added, CAST(avg(vbytes_per_second) as DOUBLE) as vbytes_per_second, + CAST(avg(min_fee) as DOUBLE) as min_fee, CAST(avg(vsize_1) as DOUBLE) as vsize_1, CAST(avg(vsize_2) as DOUBLE) as vsize_2, CAST(avg(vsize_3) as DOUBLE) as vsize_3, @@ -220,6 +224,7 @@ class StatisticsApi { return `SELECT UNIX_TIMESTAMP(added) as added, CAST(avg(vbytes_per_second) as DOUBLE) as vbytes_per_second, + CAST(avg(min_fee) as DOUBLE) as min_fee, vsize_1, vsize_2, vsize_3, @@ -404,6 +409,7 @@ class StatisticsApi { vbytes_per_second: s.vbytes_per_second, mempool_byte_weight: s.mempool_byte_weight, total_fee: s.total_fee, + min_fee: s.min_fee, vsizes: [ s.vsize_1, s.vsize_2, diff --git a/backend/src/api/statistics/statistics.ts b/backend/src/api/statistics/statistics.ts index 27554f36d..494777aad 100644 --- a/backend/src/api/statistics/statistics.ts +++ b/backend/src/api/statistics/statistics.ts @@ -89,6 +89,9 @@ class Statistics { } }); + // get minFee and convert to sats/vb + const minFee = memPool.getMempoolInfo().mempoolminfee * 100000; + try { const insertId = await statisticsApi.$create({ added: 'NOW()', @@ -98,6 +101,7 @@ class Statistics { mempool_byte_weight: totalWeight, total_fee: totalFee, fee_data: '', + min_fee: minFee, vsize_1: weightVsizeFees['1'] || 0, vsize_2: weightVsizeFees['2'] || 0, vsize_3: weightVsizeFees['3'] || 0, diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index c08846191..db04ded43 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -299,6 +299,7 @@ export interface Statistic { total_fee: number; mempool_byte_weight: number; fee_data: string; + min_fee: number; vsize_1: number; vsize_2: number; @@ -345,6 +346,7 @@ export interface OptimizedStatistic { vbytes_per_second: number; total_fee: number; mempool_byte_weight: number; + min_fee: number; vsizes: number[]; } From c1f0eac8f8d3aa7db9a8cc93ed7ee4ee24ff5603 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 14 Sep 2023 22:57:37 +0000 Subject: [PATCH 07/85] 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 08/85] 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 09/85] 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 10/85] 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 11/85] 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 12/85] 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 13/85] 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 14/85] 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 7dad00523ff53a9ad49730c3421b880bdaecc979 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 28 Aug 2023 19:20:58 +0900 Subject: [PATCH 15/85] Implement pid file & checks --- backend/mempool-config.sample.json | 3 ++- backend/src/__tests__/config.test.ts | 1 + backend/src/config.ts | 2 ++ backend/src/database.ts | 29 ++++++++++++++++++++++++++++ backend/src/index.ts | 14 ++++++++++++++ docker/backend/mempool-config.json | 3 ++- docker/backend/start.sh | 2 ++ 7 files changed, 52 insertions(+), 2 deletions(-) diff --git a/backend/mempool-config.sample.json b/backend/mempool-config.sample.json index 00fe95cc5..4bbf6cfad 100644 --- a/backend/mempool-config.sample.json +++ b/backend/mempool-config.sample.json @@ -68,7 +68,8 @@ "DATABASE": "mempool", "USERNAME": "mempool", "PASSWORD": "mempool", - "TIMEOUT": 180000 + "TIMEOUT": 180000, + "PID_DIR": "" }, "SYSLOG": { "ENABLED": true, diff --git a/backend/src/__tests__/config.test.ts b/backend/src/__tests__/config.test.ts index 8097a2465..1a21cd99b 100644 --- a/backend/src/__tests__/config.test.ts +++ b/backend/src/__tests__/config.test.ts @@ -84,6 +84,7 @@ describe('Mempool Backend Config', () => { USERNAME: 'mempool', PASSWORD: 'mempool', TIMEOUT: 180000, + PID_DIR: '' }); expect(config.SYSLOG).toStrictEqual({ diff --git a/backend/src/config.ts b/backend/src/config.ts index ed320d957..9ded762fa 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -93,6 +93,7 @@ interface IConfig { USERNAME: string; PASSWORD: string; TIMEOUT: number; + PID_DIR: string; }; SYSLOG: { ENABLED: boolean; @@ -219,6 +220,7 @@ const defaults: IConfig = { 'USERNAME': 'mempool', 'PASSWORD': 'mempool', 'TIMEOUT': 180000, + 'PID_DIR': '', }, 'SYSLOG': { 'ENABLED': true, diff --git a/backend/src/database.ts b/backend/src/database.ts index 6ad545fda..c27f28d23 100644 --- a/backend/src/database.ts +++ b/backend/src/database.ts @@ -1,3 +1,5 @@ +import * as fs from 'fs'; +import path from 'path'; import config from './config'; import { createPool, Pool, PoolConnection } from 'mysql2/promise'; import logger from './logger'; @@ -101,6 +103,33 @@ import { FieldPacket, OkPacket, PoolOptions, ResultSetHeader, RowDataPacket } fr } } + public getPidLock(): boolean { + const filePath = path.join(config.DATABASE.PID_DIR || __dirname, `/mempool-${config.DATABASE.DATABASE}.pid`); + 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)`; + logger.err(msg); + throw new Error(msg); + } else { + return true; + } + } else { + fs.writeFileSync(filePath, `${process.pid}`); + return true; + } + } + + 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(); + if (pid === `${process.pid}`) { + fs.unlinkSync(filePath); + } + } + } + private async getPool(): Promise { if (this.pool === null) { this.pool = createPool(this.poolConfig); diff --git a/backend/src/index.ts b/backend/src/index.ts index 9d0fa07f5..a8af35d1c 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -91,11 +91,18 @@ class Server { async startServer(worker = false): Promise { logger.notice(`Starting Mempool Server${worker ? ' (worker)' : ''}... (${backendInfo.getShortCommitHash()})`); + // Register cleanup listeners for exit events + ['exit', 'SIGINT', 'SIGTERM', 'SIGUSR1', 'SIGUSR2', 'uncaughtException', 'unhandledRejection'].forEach(event => { + process.on(event, () => { this.onExit(event); }); + }); + if (config.MEMPOOL.BACKEND === 'esplora') { bitcoinApi.startHealthChecks(); } if (config.DATABASE.ENABLED) { + DB.getPidLock(); + await DB.checkDbConnection(); try { if (process.env.npm_config_reindex_blocks === 'true') { // Re-index requests @@ -306,6 +313,13 @@ class Server { this.lastHeapLogTime = now; } } + + onExit(exitEvent): void { + DB.releasePidLock(); + process.exit(0); + } } + + ((): Server => new Server())(); diff --git a/docker/backend/mempool-config.json b/docker/backend/mempool-config.json index aa084133f..5642b36d3 100644 --- a/docker/backend/mempool-config.json +++ b/docker/backend/mempool-config.json @@ -69,7 +69,8 @@ "DATABASE": "__DATABASE_DATABASE__", "USERNAME": "__DATABASE_USERNAME__", "PASSWORD": "__DATABASE_PASSWORD__", - "TIMEOUT": __DATABASE_TIMEOUT__ + "TIMEOUT": __DATABASE_TIMEOUT__, + "PID_DIR": "__PID_DIR__", }, "SYSLOG": { "ENABLED": __SYSLOG_ENABLED__, diff --git a/docker/backend/start.sh b/docker/backend/start.sh index 1bfb06c24..938a5c26f 100755 --- a/docker/backend/start.sh +++ b/docker/backend/start.sh @@ -71,6 +71,7 @@ __DATABASE_DATABASE__=${DATABASE_DATABASE:=mempool} __DATABASE_USERNAME__=${DATABASE_USERNAME:=mempool} __DATABASE_PASSWORD__=${DATABASE_PASSWORD:=mempool} __DATABASE_TIMEOUT__=${DATABASE_TIMEOUT:=180000} +__DATABASE_PID_DIR__=${DATABASE_PID_DIR:=""} # SYSLOG __SYSLOG_ENABLED__=${SYSLOG_ENABLED:=false} @@ -209,6 +210,7 @@ sed -i "s!__DATABASE_DATABASE__!${__DATABASE_DATABASE__}!g" mempool-config.json sed -i "s!__DATABASE_USERNAME__!${__DATABASE_USERNAME__}!g" mempool-config.json sed -i "s!__DATABASE_PASSWORD__!${__DATABASE_PASSWORD__}!g" mempool-config.json sed -i "s!__DATABASE_TIMEOUT__!${__DATABASE_TIMEOUT__}!g" mempool-config.json +sed -i "s!__DATABASE_PID_DIR__!${__DATABASE_PID_DIR__}!g" mempool-config.json sed -i "s!__SYSLOG_ENABLED__!${__SYSLOG_ENABLED__}!g" mempool-config.json sed -i "s!__SYSLOG_HOST__!${__SYSLOG_HOST__}!g" mempool-config.json From 0808640ec0d346a427cf87a07a8dc8317bfff0c1 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 1 Oct 2023 16:38:45 +0100 Subject: [PATCH 16/85] Check database enabled before releasing PID lock file --- backend/src/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index a8af35d1c..e7e1afa3d 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -315,7 +315,9 @@ class Server { } onExit(exitEvent): void { - DB.releasePidLock(); + if (config.DATABASE.ENABLED) { + DB.releasePidLock(); + } process.exit(0); } } From 75c50416f5b0b6fd603cecbfebf6d84bfd99d0bf Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 1 Oct 2023 16:43:21 +0100 Subject: [PATCH 17/85] Fix PID docker config --- backend/src/__fixtures__/mempool-config.template.json | 1 + docker/backend/mempool-config.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/__fixtures__/mempool-config.template.json b/backend/src/__fixtures__/mempool-config.template.json index 1b6c8d411..652536d7a 100644 --- a/backend/src/__fixtures__/mempool-config.template.json +++ b/backend/src/__fixtures__/mempool-config.template.json @@ -69,6 +69,7 @@ "DATABASE": "__DATABASE_DATABASE__", "USERNAME": "__DATABASE_USERNAME__", "PASSWORD": "__DATABASE_PASSWORD__", + "PID_DIR": "__DATABASE_PID_FILE__", "TIMEOUT": 3000 }, "SYSLOG": { diff --git a/docker/backend/mempool-config.json b/docker/backend/mempool-config.json index 5642b36d3..457eccd4a 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": "__PID_DIR__", + "PID_DIR": "__DATABASE_PID_DIR__", }, "SYSLOG": { "ENABLED": __SYSLOG_ENABLED__, From 10be2c8176813bd82ca6f14effeac78ab0143389 Mon Sep 17 00:00:00 2001 From: softsimon Date: Wed, 4 Oct 2023 01:41:49 +0400 Subject: [PATCH 18/85] 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 5b357eb6b5f37c093becad29b5834b0b7da57ad4 Mon Sep 17 00:00:00 2001 From: fubz Date: Fri, 6 Oct 2023 12:48:46 -0400 Subject: [PATCH 19/85] fix: configure crontab for electrs single script --- contributors/fubz.txt | 3 +++ production/install | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 contributors/fubz.txt diff --git a/contributors/fubz.txt b/contributors/fubz.txt new file mode 100644 index 000000000..e799d641d --- /dev/null +++ b/contributors/fubz.txt @@ -0,0 +1,3 @@ +I hereby accept the terms of the Contributor License Agreement in the CONTRIBUTING.md file of the mempool/mempool git repository as of January 25, 2022. + +Signed: fubz diff --git a/production/install b/production/install index 0e11ab31a..b4f6d53c5 100755 --- a/production/install +++ b/production/install @@ -1660,15 +1660,15 @@ case $OS in crontab_bitcoin=() if [ "${BITCOIN_MAINNET_ENABLE}" = ON ];then echo "[*] Installing Electrs Mainnet Cronjob" - crontab_bitcoin+="@reboot sleep 30 ; screen -dmS mainnet /bitcoin/electrs/electrs-start-mainnet\n" + crontab_bitcoin+="@reboot sleep 30 ; screen -dmS mainnet /bitcoin/electrs/start mainnet\n" fi if [ "${BITCOIN_TESTNET_ENABLE}" = ON ];then echo "[*] Installing Electrs Testnet Cronjob" - crontab_bitcoin+="@reboot sleep 70 ; screen -dmS testnet /bitcoin/electrs/electrs-start-testnet\n" + crontab_bitcoin+="@reboot sleep 70 ; screen -dmS testnet /bitcoin/electrs/start testnet\n" fi if [ "${BITCOIN_SIGNET_ENABLE}" = ON ];then echo "[*] Installing Electrs Signet Cronjob" - crontab_bitcoin+="@reboot sleep 90 ; screen -dmS signet /bitcoin/electrs/electrs-start-signet\n" + crontab_bitcoin+="@reboot sleep 90 ; screen -dmS signet /bitcoin/electrs/start signet\n" fi if [ "${BITCOIN_MAINNET_ENABLE}" = ON -o "${BITCOIN_TESTNET_ENABLE}" = ON -o "${BITCOIN_SIGNET_ENABLE}" = ON ];then echo "${crontab_bitcoin}" | crontab -u "${BITCOIN_USER}" - From a35c8be25c6f1f17a93d25215e08e346c6f6c753 Mon Sep 17 00:00:00 2001 From: Felipe Knorr Kuhn Date: Sun, 8 Oct 2023 09:03:22 -0700 Subject: [PATCH 20/85] 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 21/85] 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 22/85] 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 23/85] 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 24/85] 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 76621464f12e272edbc364ee41f2f5a97eb28caf Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 11 Oct 2023 08:09:06 -0400 Subject: [PATCH 25/85] ma vbytes --- .../incoming-transactions-graph.component.ts | 144 +++++++++++++----- 1 file changed, 108 insertions(+), 36 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 219811e9c..3b93cb686 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 @@ -37,6 +37,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On }; windowPreference: string; chartInstance: any = undefined; + MA: number[][] = []; weightMode: boolean = false; rateUnitSub: Subscription; @@ -62,6 +63,7 @@ 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]); this.mountChart(); } @@ -72,7 +74,101 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On this.isLoading = false; } + /// calculate the moving average of maData + calculateMA(maData): 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]; + } + + ma[i].push(sum / maWindowLen); + } + + //return the moving average array + return ma; + } + mountChart(): void { + //create an array for the echart series + //similar to how it is done in mempool-graph.component.ts + const seriesGraph = []; + seriesGraph.push({ + zlevel: 0, + name: 'data', + data: this.data.series[0], + type: 'line', + smooth: false, + showSymbol: false, + symbol: 'none', + lineStyle: { + width: 3, + }, + markLine: { + silent: true, + symbol: 'none', + lineStyle: { + color: '#fff', + opacity: 1, + width: 2, + }, + data: [{ + yAxis: 1667, + label: { + show: false, + color: '#ffffff', + } + }], + } + }, + { + zlevel: 0, + name: 'MA', + data: this.MA, + type: 'line', + smooth: false, + showSymbol: false, + symbol: 'none', + lineStyle: { + width: 1, + color: "white", + }, + markLine: { + silent: true, + symbol: 'none', + lineStyle: { + color: '#fff', + opacity: 1, + width: 2, + }, + data: [{ + yAxis: 1667, + label: { + show: false, + color: '#ffffff', + } + }], + } + }); + this.mempoolStatsChartOption = { grid: { height: this.height, @@ -122,16 +218,20 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On type: 'line', }, formatter: (params: any) => { - const axisValueLabel: string = formatterXAxis(this.locale, this.windowPreference, params[0].axisValue); + const axisValueLabel: string = formatterXAxis(this.locale, this.windowPreference, params[0].axisValue); const colorSpan = (color: string) => ``; let itemFormatted = '
' + axisValueLabel + '
'; params.map((item: any, index: number) => { - if (index < 26) { - itemFormatted += `
-
${colorSpan(item.color)}
-
-
${formatNumber(this.weightMode ? item.value[1] * 4 : item.value[1], this.locale, '1.0-0')} ${this.weightMode ? 'WU' : 'vB'}/s
-
`; + + //Do no include MA in tooltip legend! + if (item.seriesName !== 'MA') { + if (index < 26) { + itemFormatted += `
+
${colorSpan(item.color)}
+
+
${formatNumber(item.value[1], this.locale, '1.0-0')}vB/s
+
`; + } } }); return `
${itemFormatted}
`; @@ -171,35 +271,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges, On } } }, - series: [ - { - zlevel: 0, - data: this.data.series[0], - type: 'line', - smooth: false, - showSymbol: false, - symbol: 'none', - lineStyle: { - width: 3, - }, - markLine: { - silent: true, - symbol: 'none', - lineStyle: { - color: '#fff', - opacity: 1, - width: 2, - }, - data: [{ - yAxis: 1667, - label: { - show: false, - color: '#ffffff', - } - }], - } - }, - ], + series: seriesGraph, visualMap: { show: false, top: 50, 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 26/85] 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 27/85] 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 28/85] 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 29/85] 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 30/85] 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 31/85] 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 32/85] 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 33/85] 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 34/85] 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 35/85] 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 36/85] 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 37/85] 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 38/85] 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 39/85] 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 40/85] 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 41/85] 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 42/85] 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 43/85] 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 44/85] 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 45/85] 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 46/85] 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 47/85] 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 48/85] 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 49/85] 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 50/85] 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 51/85] 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 52/85] 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 53/85] 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 54/85] 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 55/85] 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 56/85] 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 57/85] 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 58/85] 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 59/85] 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 60/85] 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 61/85] 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 62/85] 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 63/85] 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 64/85] 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 65/85] 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 66/85] 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 67/85] 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 68/85] 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 69/85] 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 70/85] 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 71/85] 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 72/85] 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 73/85] 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 74/85] 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 75/85] 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 76/85] 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 77/85] 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 78/85] 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 79/85] 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 80/85] 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 81/85] 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 82/85] 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 83/85] 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 84/85] 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 85/85] 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); } }