Merge branch 'master' into nymkappa/accelerator-rev-share

This commit is contained in:
nymkappa 2026-01-19 23:58:04 +01:00
commit e429aef07f
No known key found for this signature in database
GPG key ID: A7895A957BA87DBA
10 changed files with 242 additions and 75 deletions

View file

@ -2669,22 +2669,23 @@
}
},
"node_modules/body-parser": {
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"version": "1.20.4",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
"integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"bytes": "~3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.13.0",
"raw-body": "2.5.2",
"destroy": "~1.2.0",
"http-errors": "~2.0.1",
"iconv-lite": "~0.4.24",
"on-finished": "~2.4.1",
"qs": "~6.14.0",
"raw-body": "~2.5.3",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8",
@ -2699,11 +2700,40 @@
"ms": "2.0.0"
}
},
"node_modules/body-parser/node_modules/http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"license": "MIT",
"dependencies": {
"depd": "~2.0.0",
"inherits": "~2.0.4",
"setprototypeof": "~1.2.0",
"statuses": "~2.0.2",
"toidentifier": "~1.0.1"
},
"engines": {
"node": ">= 0.8"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/body-parser/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/body-parser/node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/brace-expansion": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
@ -2812,6 +2842,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
@ -3685,20 +3716,6 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/express/node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -4249,6 +4266,7 @@
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
@ -6023,11 +6041,12 @@
"license": "MIT"
},
"node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.6"
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
@ -6065,19 +6084,49 @@
}
},
"node_modules/raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
"integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
"bytes": "~3.1.2",
"http-errors": "~2.0.1",
"iconv-lite": "~0.4.24",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/raw-body/node_modules/http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"license": "MIT",
"dependencies": {
"depd": "~2.0.0",
"inherits": "~2.0.4",
"setprototypeof": "~1.2.0",
"statuses": "~2.0.2",
"toidentifier": "~1.0.1"
},
"engines": {
"node": ">= 0.8"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/raw-body/node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/react-is": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
@ -7209,7 +7258,11 @@
}
},
"rust-gbt": {
"version": "0.0.1"
"name": "gbt",
"version": "3.0.1",
"engines": {
"node": ">= 12"
}
}
},
"dependencies": {
@ -9077,22 +9130,22 @@
}
},
"body-parser": {
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"version": "1.20.4",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
"integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
"requires": {
"bytes": "3.1.2",
"bytes": "~3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.13.0",
"raw-body": "2.5.2",
"destroy": "~1.2.0",
"http-errors": "~2.0.1",
"iconv-lite": "~0.4.24",
"on-finished": "~2.4.1",
"qs": "~6.14.0",
"raw-body": "~2.5.3",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
"unpipe": "~1.0.0"
},
"dependencies": {
"debug": {
@ -9103,10 +9156,27 @@
"ms": "2.0.0"
}
},
"http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"requires": {
"depd": "~2.0.0",
"inherits": "~2.0.4",
"setprototypeof": "~1.2.0",
"statuses": "~2.0.2",
"toidentifier": "~1.0.1"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="
}
}
},
@ -9795,14 +9865,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
"requires": {
"side-channel": "^1.1.0"
}
}
}
},
@ -11417,11 +11479,11 @@
"dev": true
},
"qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"requires": {
"side-channel": "^1.0.6"
"side-channel": "^1.1.0"
}
},
"queue-microtask": {
@ -11436,14 +11498,33 @@
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
"integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
"requires": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
"bytes": "~3.1.2",
"http-errors": "~2.0.1",
"iconv-lite": "~0.4.24",
"unpipe": "~1.0.0"
},
"dependencies": {
"http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"requires": {
"depd": "~2.0.0",
"inherits": "~2.0.4",
"setprototypeof": "~1.2.0",
"statuses": "~2.0.2",
"toidentifier": "~1.0.1"
}
},
"statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="
}
}
},
"react-is": {

View file

@ -30,6 +30,7 @@ class BackendInfo {
lightning: config.LIGHTNING.ENABLED,
backend: config.MEMPOOL.BACKEND,
coreVersion: '?',
osVersion: `${os.type()} ${os.release()}`,
};
this.timer = setInterval(async () => {

View file

@ -30,6 +30,7 @@ interface FailoverHost {
electrs?: string,
ssr?: string,
core?: string,
os?: string,
lastUpdated: number,
}
}
@ -324,6 +325,9 @@ class FailoverRouter {
if (response.data?.coreVersion) {
host.hashes.core = response.data.coreVersion;
}
if (response.data?.osVersion) {
host.hashes.os = response.data.osVersion;
}
} catch (e) {
// failed to get backend build hash - do nothing
}

View file

@ -505,6 +505,7 @@ export interface IBackendInfo {
version: string;
lightning: boolean;
coreVersion: string;
osVersion: string;
backend: 'esplora' | 'electrum' | 'none';
}

View file

@ -25,6 +25,7 @@
<th class="electrs hash">Electrs</th>
<th class="ssr hash">SSR</th>
<th class="core">Core</th>
<th class="os">OS</th>
</tr>
<tr *ngFor="let host of hosts; let i = index; trackBy: trackByFn">
<td class="rank">{{ i + 1 }}</td>
@ -34,13 +35,13 @@
<td class="rtt only-small">{{ (host.rtt / 1000) | number : '1.1-1' }} {{ host.rtt == null ? '' : 's'}} {{ !host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅') }}</td>
<td class="rtt only-large">{{ host.rtt | number : '1.0-0' }} {{ host.rtt == null ? '' : 'ms'}} {{ !host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅') }}</td>
<td class="height">{{ host.latestHeight }} {{ !host.checked ? '⏳' : (host.outOfSync ? '🚫' : (host.latestHeight && host.latestHeight < maxHeight ? '🟧' : '')) }}</td>
<ng-container *ngFor="let type of ['hybrid', 'frontend', 'backend', 'electrs', 'ssr', 'core']">
<td class="{{type}}" [class.hash]="type !== 'core'" [style.background-color]="type !== 'core' && host.hashes?.[type] ? '#' + host.hashes[type].slice(0, 6) : ''">
@if (type !== 'core' && host.hashes?.[type]) {
<ng-container *ngFor="let type of ['hybrid', 'frontend', 'backend', 'electrs', 'ssr', 'core', 'os']">
<td class="{{type}}" [class.hash]="type !== 'core' && type !== 'os'" [style.background-color]="colors[host.host][type]">
@if (type !== 'core' && type !== 'os' && host.hashes?.[type]) {
<a [style.color]="'white'" href="https://github.com/mempool/{{repoMap[type]}}/commit/{{ host.hashes[type] }}" target="_blank">{{ host.hashes[type].slice(0, 8) || '?' }}</a>
} @else if (host.hashes?.[type]) {
<span [style.color]="'white'" class="only-large" style="font-size: 10px;">{{ host.hashes[type] || '?' }}</span>
<span [style.color]="'white'" class="only-small" style="font-size: 10px;">{{ host.hashes[type]?.slice(9) || '?' }}</span>
<span [style.color]="'white'" class="only-small" style="font-size: 10px;">{{ shortenVersion(host.hashes[type]) || '?' }}</span>
} @else {
<span>?</span>
}

View file

@ -13,10 +13,12 @@
margin: auto;
padding: 1em;
background: var(--box-bg);
overflow-x: auto;
}
.status-table {
width: 100%;
width: max-content;
min-width: 100%;
td, th {
padding: 0.25em;
@ -45,6 +47,7 @@
&.host {
width: 100%;
max-width: 0;
min-width: 130px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

View file

@ -1,6 +1,6 @@
import { Component, OnInit, ChangeDetectionStrategy, SecurityContext, ChangeDetectorRef } from '@angular/core';
import { WebsocketService } from '@app/services/websocket.service';
import { Observable, Subject, map, tap } from 'rxjs';
import { Observable, map, tap } from 'rxjs';
import { StateService } from '@app/services/state.service';
import { HealthCheckHost } from '@interfaces/websocket.interface';
import { DomSanitizer } from '@angular/platform-browser';
@ -17,6 +17,7 @@ export class ServerHealthComponent implements OnInit {
maxHeight: number;
interval: number;
now: number = Date.now();
colors: Record<string, Record<string, string>> = {};
repoMap = {
frontend: 'mempool',
@ -59,6 +60,31 @@ export class ServerHealthComponent implements OnInit {
for (const host of hosts) {
newMaxHeight = Math.max(newMaxHeight, host.latestHeight);
}
const sortedCoreVersions = [...new Set(
hosts.map(h => this.parseVersion(h.hashes?.core)).filter(Boolean)
)].sort((a, b) => this.compareVersions(a, b));
const sortedOsVersions = [...new Set(
hosts.map(h => this.parseOsVersion(h.hashes?.os)).filter(Boolean)
)].sort((a, b) => this.compareVersions(a, b));
this.colors = {};
for (const host of hosts) {
this.colors[host.host] = {};
for (const type of ['hybrid', 'frontend', 'backend', 'electrs', 'ssr', 'core', 'os']) {
if (type === 'core') {
const version = this.parseVersion(host.hashes?.core);
this.colors[host.host][type] = version ? this.getVersionColor(version, sortedCoreVersions) : '';
} else if (type === 'os') {
const version = this.parseOsVersion(host.hashes?.os);
this.colors[host.host][type] = version ? this.getVersionColor(version, sortedOsVersions) : '';
} else {
const hash = host.hashes?.[type];
this.colors[host.host][type] = hash ? '#' + hash.slice(0, 6) : '';
}
}
}
})
);
this.websocketService.want(['mempool-blocks', 'stats', 'blocks', 'tomahawk']);
@ -99,4 +125,50 @@ export class ServerHealthComponent implements OnInit {
return '';
}
}
private parseVersion(subver: string): string | undefined {
if (subver) {
const match = subver.match(/:(\d+\.\d+\.\d+)/);
return match ? match[1] : null;
}
}
private parseOsVersion(osVersion: string): string | undefined {
if (osVersion) {
const match = osVersion.match(/(\d+)\.(\d+)(?:\.(\d+))?/);
return match ? `${match[1]}.${match[2]}.${match[3] ?? 0}` : null;
}
}
public shortenVersion(version: string): string {
return version.match(/\d+\.\d+(?:\.\d+)?/)?.[0] ?? '';
}
private compareVersions(a: string, b: string): number {
const partsA = a.split('.').map(Number);
const partsB = b.split('.').map(Number);
for (let i = 0; i < 3; i++) {
if (partsA[i] !== partsB[i]) {
return partsA[i] - partsB[i];
}
}
return 0;
}
private getVersionColor(version: string, sortedVersions: string[]): string {
if (!version || sortedVersions.length === 0) {
return '';
}
const index = sortedVersions.indexOf(version);
if (index === -1) {
return '';
}
if (sortedVersions.length === 1) {
return 'hsl(120, 70%, 35%)';
}
const hue = (index / (sortedVersions.length - 1)) * 120;
return `hsl(${hue}, 70%, 35%)`;
}
}

View file

@ -151,7 +151,11 @@ export interface HealthCheckHost {
frontend?: string;
backend?: string;
electrs?: string;
}
hybrid?: string;
ssr?: string;
core?: string;
os?: string;
};
}
export interface StratumJob {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Before After
Before After

View file

@ -1,7 +1,7 @@
#!/usr/bin/env zsh
delay=0.15
hostname=$(hostname)
services_host=""
#services_host=""
protocol="https"
base_domain="mempool.space"
list_refresh_interval=60