Merge branch 'master' into orangesurf/20241217

This commit is contained in:
wiz 2025-04-06 21:59:44 +09:00 committed by GitHub
commit c63bb0b6ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 281 additions and 96 deletions

View file

@ -12,7 +12,7 @@ jobs:
if: "(github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')) || github.event_name == 'push'"
strategy:
matrix:
node: ["20", "21"]
node: ["22.14.0"]
flavor: ["dev", "prod"]
fail-fast: false
runs-on: ubuntu-latest
@ -163,7 +163,7 @@ jobs:
if: "(github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ops') && !contains(github.head_ref, 'ops/')) || github.event_name == 'push'"
strategy:
matrix:
node: ["20", "21"]
node: ["22.14.0"]
flavor: ["dev", "prod"]
fail-fast: false
runs-on: ubuntu-latest
@ -266,7 +266,7 @@ jobs:
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: 20
node-version: 22
cache: "npm"
cache-dependency-path: ${{ matrix.module }}/frontend/package-lock.json

View file

@ -43,7 +43,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
node-version: 22.14.0
registry-url: "https://registry.npmjs.org"
- name: Install (Prod dependencies only)
@ -151,7 +151,7 @@ jobs:
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: 20
node-version: 22.14.0
cache: "npm"
cache-dependency-path: ${{ matrix.module }}/frontend/package-lock.json

2
.nvmrc
View file

@ -1 +1 @@
v20.8.0
v22

View file

@ -1,12 +1,12 @@
{
"name": "mempool-backend",
"version": "3.1.0-dev",
"version": "3.2.0-dev",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "mempool-backend",
"version": "3.1.0-dev",
"version": "3.2.0-dev",
"hasInstallScript": true,
"license": "GNU Affero General Public License v3.0",
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "mempool-backend",
"version": "3.1.0-dev",
"version": "3.2.0-dev",
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
"license": "GNU Affero General Public License v3.0",
"homepage": "https://mempool.space",
@ -68,4 +68,4 @@
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1"
}
}
}

View file

@ -36,7 +36,7 @@ class FailoverRouter {
maxHeight: number = 0;
hosts: FailoverHost[];
multihost: boolean;
gitHashInterval: number = 600000; // 10 minutes
gitHashInterval: number = 60000; // 1 minute
pollInterval: number = 60000; // 1 minute
pollTimer: NodeJS.Timeout | null = null;
pollConnection = axios.create();
@ -111,7 +111,7 @@ class FailoverRouter {
for (const host of this.hosts) {
try {
const result = await (host.socket
? this.pollConnection.get<number>('/blocks/tip/height', { socketPath: host.host, timeout: config.ESPLORA.FALLBACK_TIMEOUT })
? this.pollConnection.get<number>('http://api/blocks/tip/height', { socketPath: host.host, timeout: config.ESPLORA.FALLBACK_TIMEOUT })
: this.pollConnection.get<number>(host.host + '/blocks/tip/height', { timeout: config.ESPLORA.FALLBACK_TIMEOUT })
);
if (result) {
@ -288,7 +288,7 @@ class FailoverRouter {
let url;
if (host.socket) {
axiosConfig = { socketPath: host.host, timeout: config.ESPLORA.REQUEST_TIMEOUT, responseType };
url = path;
url = 'http://api' + path;
} else {
axiosConfig = { timeout: config.ESPLORA.REQUEST_TIMEOUT, responseType };
url = host.host + path;

View file

@ -1391,7 +1391,7 @@ class Blocks {
}
public async $getBlockAuditSummary(hash: string): Promise<BlockAudit | null> {
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK)) {
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) && Common.auditIndexingEnabled()) {
return BlocksAuditsRepository.$getBlockAudit(hash);
} else {
return null;
@ -1399,7 +1399,7 @@ class Blocks {
}
public async $getBlockTxAuditSummary(hash: string, txid: string): Promise<TransactionAudit | null> {
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK)) {
if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) && Common.auditIndexingEnabled()) {
return BlocksAuditsRepository.$getBlockTxAudit(hash, txid);
} else {
return null;

View file

@ -722,6 +722,13 @@ export class Common {
);
}
static auditIndexingEnabled(): boolean {
return (
Common.indexingEnabled() &&
config.MEMPOOL.AUDIT === true
);
}
static gogglesIndexingEnabled(): boolean {
return (
Common.blocksSummariesIndexingEnabled() &&

View file

@ -8,6 +8,7 @@ import mining from './mining/mining';
import transactionUtils from './transaction-utils';
import BlocksRepository from '../repositories/BlocksRepository';
import redisCache from './redis-cache';
import blocks from './blocks';
class PoolsParser {
miningPools: any[] = [];
@ -42,6 +43,8 @@ class PoolsParser {
await this.$insertUnknownPool();
let reindexUnknown = false;
let clearCache = false;
for (const pool of this.miningPools) {
if (!pool.id) {
@ -78,17 +81,20 @@ class PoolsParser {
logger.debug(`Inserting new mining pool ${pool.name}`);
await PoolsRepository.$insertNewMiningPool(pool, slug);
reindexUnknown = true;
clearCache = true;
} else {
if (poolDB.name !== pool.name) {
// Pool has been renamed
const newSlug = pool.name.replace(/[^a-z0-9]/gi, '').toLowerCase();
logger.warn(`Renaming ${poolDB.name} mining pool to ${pool.name}. Slug has been updated. Maybe you want to make a redirection from 'https://mempool.space/mining/pool/${poolDB.slug}' to 'https://mempool.space/mining/pool/${newSlug}`);
await PoolsRepository.$renameMiningPool(poolDB.id, newSlug, pool.name);
clearCache = true;
}
if (poolDB.link !== pool.link) {
// Pool link has changed
logger.debug(`Updating link for ${pool.name} mining pool`);
await PoolsRepository.$updateMiningPoolLink(poolDB.id, pool.link);
clearCache = true;
}
if (JSON.stringify(pool.addresses) !== poolDB.addresses ||
JSON.stringify(pool.regexes) !== poolDB.regexes) {
@ -96,6 +102,7 @@ class PoolsParser {
logger.notice(`Updating addresses and/or coinbase tags for ${pool.name} mining pool.`);
await PoolsRepository.$updateMiningPoolTags(poolDB.id, pool.addresses, pool.regexes);
reindexUnknown = true;
clearCache = true;
await this.$reindexBlocksForPool(poolDB.id);
}
}
@ -111,6 +118,19 @@ class PoolsParser {
}
await this.$reindexBlocksForPool(unknownPool.id);
}
// refresh the in-memory block cache with the reindexed data
if (clearCache) {
for (const block of blocks.getBlocks()) {
const reindexedBlock = await blocks.$indexBlock(block.height);
if (reindexedBlock.id === block.id) {
block.extras.pool = reindexedBlock.extras.pool;
}
}
// update persistent cache with the reindexed data
diskCache.$saveCacheToDisk();
redisCache.$updateBlocks(blocks.getBlocks());
}
}
public matchBlockMiner(scriptsig: string, addresses: string[], pools: PoolTag[]): PoolTag | undefined {

View file

@ -1011,15 +1011,19 @@ class WebsocketHandler {
const blockTransactions = structuredClone(transactions);
this.printLogs();
await statistics.runStatistics();
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED) {
await statistics.runStatistics();
}
const _memPool = memPool.getMempool();
const candidateTxs = await memPool.getMempoolCandidates();
let candidates: GbtCandidates | undefined = (memPool.limitGBT && candidateTxs) ? { txs: candidateTxs, added: [], removed: [] } : undefined;
let transactionIds: string[] = (memPool.limitGBT) ? Object.keys(candidates?.txs || {}) : Object.keys(_memPool);
const accelerations = Object.values(mempool.getAccelerations());
await accelerationRepository.$indexAccelerationsForBlock(block, accelerations, structuredClone(transactions));
if (config.DATABASE.ENABLED) {
const accelerations = Object.values(mempool.getAccelerations());
await accelerationRepository.$indexAccelerationsForBlock(block, accelerations, structuredClone(transactions));
}
const rbfTransactions = Common.findMinedRbfTransactions(transactions, memPool.getSpendMap());
memPool.handleRbfTransactions(rbfTransactions);
@ -1095,7 +1099,9 @@ class WebsocketHandler {
if (config.CORE_RPC.DEBUG_LOG_PATH && block.extras) {
const firstSeen = getRecentFirstSeen(block.id);
if (firstSeen) {
BlocksRepository.$saveFirstSeenTime(block.id, firstSeen);
if (config.DATABASE.ENABLED) {
BlocksRepository.$saveFirstSeenTime(block.id, firstSeen);
}
block.extras.firstSeen = firstSeen;
}
}
@ -1392,7 +1398,9 @@ class WebsocketHandler {
});
}
await statistics.runStatistics();
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED) {
await statistics.runStatistics();
}
}
public handleNewStratumJob(job: StratumJob): void {

View file

@ -156,7 +156,9 @@ class Server {
await poolsUpdater.updatePoolsJson(); // Needs to be done before loading the disk cache because we sometimes wipe it
await syncAssets.syncAssets$();
await mempoolBlocks.updatePools$();
if (config.DATABASE.ENABLED) {
await mempoolBlocks.updatePools$();
}
if (config.MEMPOOL.ENABLED) {
if (config.MEMPOOL.CACHE_ENABLED) {
await diskCache.$loadMempoolCache();

View file

@ -98,7 +98,8 @@ class PoolsUpdater {
logger.info(`Mining pools-v2.json (${githubSha}) import completed`, this.tag);
} catch (e) {
this.lastRun = now - 600; // Try again in 10 minutes
// fast-forward lastRun to 10 minutes before the next scheduled update
this.lastRun = now - Math.max(config.MEMPOOL.POOLS_UPDATE_DELAY - 600, 600);
logger.err(`PoolsUpdater failed. Will try again in 10 minutes. Exception: ${JSON.stringify(e)}`, this.tag);
}
}

View file

@ -8,7 +8,7 @@ WORKDIR /build
RUN apt-get update && \
apt-get install -y curl ca-certificates && \
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
apt-get install -y nodejs build-essential python3 pkg-config && \
apt-get install -y nodejs=22.14.0-1nodesource1 build-essential python3 pkg-config && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
@ -29,7 +29,7 @@ FROM rust:1.84-bookworm AS runtime
RUN apt-get update && \
apt-get install -y curl ca-certificates && \
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
apt-get install -y nodejs && \
apt-get install -y nodejs=22.14.0-1nodesource1 && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

View file

@ -1,4 +1,4 @@
FROM node:22-bookworm-slim AS builder
FROM node:22.14.0-bookworm-slim AS builder
ARG commitHash
ENV DOCKER_COMMIT_HASH=${commitHash}

View file

@ -561,6 +561,43 @@ describe('Mainnet', () => {
});
describe('RBF transactions', () => {
it('RBF page gets updated over websockets', () => {
cy.intercept('/api/v1/replacements', {
statusCode: 200,
body: []
});
cy.intercept('/api/v1/fullrbf/replacements', {
statusCode: 200,
body: []
});
cy.mockMempoolSocketV2();
cy.visit('/rbf');
cy.get('.no-replacements');
cy.get('.tree').should('have.length', 0);
receiveWebSocketMessageFromServer({
params: {
file: {
path: 'rbf_page/rbf_01.json'
}
}
});
cy.get('.tree').should('have.length', 1);
receiveWebSocketMessageFromServer({
params: {
file: {
path: 'rbf_page/rbf_02.json'
}
}
});
cy.get('.tree').should('have.length', 2);
});
it('shows RBF transactions properly (mobile - details)', () => {
cy.intercept('/api/v1/tx/21518a98d1aa9df524865d2f88c578499f524eb1d0c4d3e70312ab863508692f/cached', {
fixture: 'mainnet_tx_cached.json'

View file

@ -0,0 +1,37 @@
{
"rbfLatest": [
{
"tx": {
"txid": "f4bae4f626036250fd00d68490e572f65f66417452003a0f4c4d76f17a9fde68",
"fee": 1185,
"vsize": 223,
"value": 41729,
"rate": 5.313901345291479,
"time": 1743587177,
"rbf": true,
"fullRbf": false,
"mined": true
},
"time": 1743587177,
"fullRbf": true,
"replaces": [
{
"tx": {
"txid": "12945412dfc455e0ed6049dc2ee8737756c8d9e2d9a2eb26f366cd5019a0369f",
"fee": 504,
"vsize": 222,
"value": 42410,
"rate": 2.27027027027027,
"time": 1743586081,
"rbf": true
},
"time": 1743586081,
"interval": 1096,
"fullRbf": false,
"replaces": []
}
],
"mined": true
}
]
}

View file

@ -0,0 +1,68 @@
{
"rbfLatest": [
{
"tx": {
"txid": "d313b479acfbae719afb488a078e0fe0e052a67b9f65f73f7c75d3d95fd36acc",
"fee": 672,
"vsize": 167.25,
"value": 29996328,
"rate": 4.017937219730942,
"time": 1743587365,
"rbf": true,
"fullRbf": false
},
"time": 1743587365,
"fullRbf": false,
"replaces": [
{
"tx": {
"txid": "eb5aa786cabda307cc9642cfb9c41a3b405ac20a391eefbe54be7930bea61865",
"fee": 336,
"vsize": 167.5,
"value": 29996664,
"rate": 2.005970149253731,
"time": 1743586424,
"rbf": true
},
"time": 1743586424,
"interval": 941,
"fullRbf": false,
"replaces": []
}
]
},
{
"tx": {
"txid": "f4bae4f626036250fd00d68490e572f65f66417452003a0f4c4d76f17a9fde68",
"fee": 1185,
"vsize": 223,
"value": 41729,
"rate": 5.313901345291479,
"time": 1743587177,
"rbf": true,
"fullRbf": false,
"mined": true
},
"time": 1743587177,
"fullRbf": true,
"replaces": [
{
"tx": {
"txid": "12945412dfc455e0ed6049dc2ee8737756c8d9e2d9a2eb26f366cd5019a0369f",
"fee": 504,
"vsize": 222,
"value": 42410,
"rate": 2.27027027027027,
"time": 1743586081,
"rbf": true
},
"time": 1743586081,
"interval": 1096,
"fullRbf": false,
"replaces": []
}
],
"mined": true
}
]
}

View file

@ -1,12 +1,12 @@
{
"name": "mempool-frontend",
"version": "3.1.0-dev",
"version": "3.2.0-dev",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "mempool-frontend",
"version": "3.1.0-dev",
"version": "3.2.0-dev",
"license": "GNU Affero General Public License v3.0",
"dependencies": {
"@angular-devkit/build-angular": "^17.3.1",

View file

@ -1,6 +1,6 @@
{
"name": "mempool-frontend",
"version": "3.1.0-dev",
"version": "3.2.0-dev",
"description": "Bitcoin mempool visualizer and blockchain explorer backend",
"license": "GNU Affero General Public License v3.0",
"homepage": "https://mempool.space",

View file

@ -84,74 +84,79 @@ div.scrollable {
text-align: right;
}
}
}
.progress {
background-color: var(--secondary);
.progress {
background-color: var(--secondary);
}
.coinbase {
width: 20%;
@media (max-width: 875px) {
display: none;
}
}
.coinbase {
width: 20%;
@media (max-width: 875px) {
display: none;
}
.health {
@media (max-width: 1150px) {
display: none;
}
}
.height {
width: 10%;
.height {
width: 10%;
}
.timestamp {
@media (max-width: 875px) {
padding-left: 50px;
}
.timestamp {
@media (max-width: 875px) {
padding-left: 50px;
}
@media (max-width: 685px) {
display: none;
}
@media (max-width: 625px) {
display: none;
}
}
.mined {
width: 13%;
@media (max-width: 1100px) {
display: none;
}
.mined {
width: 13%;
}
.txs {
@media (max-width: 938px) {
display: none;
}
.txs {
padding-right: 40px;
@media (max-width: 1100px) {
padding-right: 10px;
}
@media (max-width: 875px) {
padding-right: 20px;
}
@media (max-width: 567px) {
padding-right: 10px;
}
padding-right: 40px;
@media (max-width: 1100px) {
padding-right: 10px;
}
.size {
width: 12%;
@media (max-width: 1000px) {
width: 15%;
}
@media (max-width: 875px) {
width: 20%;
}
@media (max-width: 650px) {
width: 20%;
}
@media (max-width: 450px) {
display: none;
}
@media (max-width: 875px) {
padding-right: 20px;
}
@media (max-width: 567px) {
padding-right: 10px;
}
}
.scriptmessage {
overflow: hidden;
display: inline-block;
text-overflow: ellipsis;
vertical-align: middle;
width: auto;
text-align: left;
.size {
min-width: 80px;
width: 12%;
@media (max-width: 1000px) {
width: 15%;
}
}
.scriptmessage {
max-width: 340px;
overflow: hidden;
display: inline-block;
text-overflow: ellipsis;
vertical-align: middle;
width: auto;
text-align: left;
}
.reward {
@media (max-width: 1035px) {
display: none;
}
}

View file

@ -84,11 +84,11 @@ pkg install -y zsh sudo git screen curl wget neovim rsync nginx openssl openssh-
### Node.js + npm
Build Node.js v20.17.0 and npm v9 from source using `nvm`:
Build Node.js v22.14.0 and npm v9 from source using `nvm`:
```
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | zsh
source $HOME/.zshrc
nvm install v20.17.0 --shared-zlib
nvm install v22.14.0 --shared-zlib
nvm alias default node
```

View file

@ -378,7 +378,7 @@ ELEMENTS_REPO_URL=https://github.com/ElementsProject/elements
ELEMENTS_REPO_NAME=elements
ELEMENTS_REPO_BRANCH=master
#ELEMENTS_LATEST_RELEASE=$(curl -s https://api.github.com/repos/ElementsProject/elements/releases/latest|grep tag_name|head -1|cut -d '"' -f4)
ELEMENTS_LATEST_RELEASE=elements-23.2.6
ELEMENTS_LATEST_RELEASE=elements-23.2.7
echo -n '.'
BITCOIN_ELECTRS_REPO_URL=https://github.com/mempool/electrs
@ -1113,11 +1113,11 @@ echo "[*] Installing Mempool crontab"
osSudo "${ROOT_USER}" crontab -u "${MEMPOOL_USER}" "${MEMPOOL_HOME}/${MEMPOOL_REPO_NAME}/production/mempool.crontab"
echo "[*] Installing nvm.sh from GitHub"
osSudo "${MEMPOOL_USER}" sh -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | zsh'
osSudo "${MEMPOOL_USER}" sh -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | zsh'
echo "[*] Building NodeJS via nvm.sh"
osSudo "${MEMPOOL_USER}" zsh -c 'source ~/.zshrc ; nvm install v20.12.0 --shared-zlib'
osSudo "${MEMPOOL_USER}" zsh -c 'source ~/.zshrc ; nvm alias default 20.12.0'
osSudo "${MEMPOOL_USER}" zsh -c 'source ~/.zshrc ; CC=gcc CXX=g++ nvm install v22.14.0 --shared-zlib'
osSudo "${MEMPOOL_USER}" zsh -c 'source ~/.zshrc ; nvm alias default 22.14.0'
####################
# Tor installation #
@ -1565,7 +1565,7 @@ EOF
osSudo "${UNFURL_USER}" sh -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | zsh'
echo "[*] Building NodeJS via nvm.sh"
osSudo "${UNFURL_USER}" zsh -c 'source ~/.zshrc ; nvm install v20.12.0 --shared-zlib'
osSudo "${UNFURL_USER}" zsh -c 'source ~/.zshrc ; nvm install v22.14.0 --shared-zlib'
;;
esac

View file

@ -1,7 +1,7 @@
#!/usr/bin/env zsh
export NVM_DIR="$HOME/.nvm"
source "$NVM_DIR/nvm.sh"
nvm use v20.12.0
nvm use v22.14.0
# start all mempool backends that exist
for site in mainnet mainnet-lightning testnet testnet-lightning testnet4 signet signet-lightning liquid liquidtestnet;do

View file

@ -1,12 +1,12 @@
{
"name": "mempool-unfurl",
"version": "3.1.0-dev",
"version": "3.2.0-dev",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "mempool-unfurl",
"version": "3.0.0",
"version": "3.2.0-dev",
"dependencies": {
"@types/node": "^16.11.41",
"ejs": "^3.1.10",

View file

@ -1,6 +1,6 @@
{
"name": "mempool-unfurl",
"version": "3.1.0-dev",
"version": "3.2.0-dev",
"description": "Renderer for mempool open graph link preview images",
"repository": {
"type": "git",
@ -32,4 +32,4 @@
"eslint-config-prettier": "^8.5.0",
"prettier": "^2.7.1"
}
}
}