diff --git a/backend/src/api/backend-info.ts b/backend/src/api/backend-info.ts index d4500a837..c9a82def4 100644 --- a/backend/src/api/backend-info.ts +++ b/backend/src/api/backend-info.ts @@ -3,9 +3,12 @@ import path from 'path'; import os from 'os'; import { IBackendInfo } from '../mempool.interfaces'; import config from '../config'; +import bitcoinClient from './bitcoin/bitcoin-client'; +import logger from '../logger'; class BackendInfo { private backendInfo: IBackendInfo; + private timer; constructor() { // This file is created by ./fetch-version.ts during building @@ -26,7 +29,22 @@ class BackendInfo { gitCommit: versionInfo.gitCommit, lightning: config.LIGHTNING.ENABLED, backend: config.MEMPOOL.BACKEND, + coreVersion: '?', }; + + this.timer = setInterval(async () => { + await this.$updateCoreVersion(); + }, 10 * 60 * 1000); // every 10 minutes + this.$updateCoreVersion(); // starting immediately + } + + private async $updateCoreVersion(): Promise { + try { + const networkInfo = await bitcoinClient.getNetworkInfo(); + this.backendInfo.coreVersion = networkInfo.subversion; + } catch (e) { + logger.err(`Exception in $updateCoreVersion. Reason: ${(e instanceof Error ? e.message : e)}`); + } } public getBackendInfo(): IBackendInfo { diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index d7be065fa..fb8ee76b2 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -28,6 +28,8 @@ interface FailoverHost { hybrid?: string, backend?: string, electrs?: string, + ssr?: string, + core?: string, lastUpdated: number, } } @@ -35,7 +37,7 @@ interface FailoverHost { class FailoverRouter { activeHost: FailoverHost; fallbackHost: FailoverHost; - maxSlippage: number = config.ESPLORA.MAX_BEHIND_TIP ?? 2; + maxSlippage: number = config.ESPLORA.MAX_BEHIND_TIP ?? (Common.isLiquid() ? 8 : 2); maxHeight: number = 0; hosts: FailoverHost[]; multihost: boolean; @@ -145,7 +147,8 @@ class FailoverRouter { if (Date.now() - host.hashes.lastUpdated > this.gitHashInterval) { await Promise.all([ this.$updateFrontendGitHash(host), - this.$updateBackendGitHash(host), + this.$updateBackendVersions(host), + this.$updateSSRGitHash(host), config.MEMPOOL.OFFICIAL ? this.$updateHybridGitHash(host) : Promise.resolve(), ]); host.hashes.lastUpdated = Date.now(); @@ -301,18 +304,33 @@ class FailoverRouter { } } - private async $updateBackendGitHash(host: FailoverHost): Promise { + private async $updateBackendVersions(host: FailoverHost): Promise { try { const url = `${host.publicDomain}/api/v1/backend-info`; const response = await this.pollConnection.get(url, { timeout: config.ESPLORA.FALLBACK_TIMEOUT }); if (response.data?.gitCommit) { host.hashes.backend = response.data.gitCommit; } + if (response.data?.coreVersion) { + host.hashes.core = response.data.coreVersion; + } } catch (e) { // failed to get backend build hash - do nothing } } + private async $updateSSRGitHash(host: FailoverHost): Promise { + try { + const url = `${host.publicDomain}/ssr/api/status`; + const response = await this.pollConnection.get(url, { timeout: config.ESPLORA.FALLBACK_TIMEOUT }); + if (response.data?.gitHash) { + host.hashes.ssr = response.data.gitHash; + } + } catch (e) { + // failed to get ssr build hash - do nothing + } + } + // returns the public mempool domain corresponding to an esplora server url // (a bit of a hack to avoid manually specifying frontend & backend URLs for each esplora server) private extractPublicDomain(url: string): string { diff --git a/backend/src/mempool.interfaces.ts b/backend/src/mempool.interfaces.ts index 0dfce6b1f..1f7ffdd1f 100644 --- a/backend/src/mempool.interfaces.ts +++ b/backend/src/mempool.interfaces.ts @@ -504,9 +504,35 @@ export interface IBackendInfo { gitCommit: string; version: string; lightning: boolean; + coreVersion: string; backend: 'esplora' | 'electrum' | 'none'; } +export interface INetworkInfo { + version: number; + subversion: string; + protocolversion: number; + localservices: string; + localrelay: boolean; + timeoffset: number; + networkactive: boolean; + networks: { + name: string; + limited: boolean; + reachable: boolean; + proxy: string; + proxy_randomize_credentials: boolean; + }[]; + relayfee: number; + incrementalfee: number; + localaddresses: { + address: string; + port: number; + score: number; + }[]; + warnings: string; +} + export interface IDifficultyAdjustment { progressPercent: number; difficultyChange: number; diff --git a/frontend/src/app/components/server-health/server-health.component.html b/frontend/src/app/components/server-health/server-health.component.html index e5dfd5038..28893f103 100644 --- a/frontend/src/app/components/server-health/server-health.component.html +++ b/frontend/src/app/components/server-health/server-health.component.html @@ -23,6 +23,8 @@ Front Back Electrs + SSR + Core {{ i + 1 }} @@ -32,9 +34,9 @@ {{ (host.rtt / 1000) | number : '1.1-1' }} {{ host.rtt == null ? '' : 's'}} {{ !host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅') }} {{ host.rtt | number : '1.0-0' }} {{ host.rtt == null ? '' : 'ms'}} {{ !host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅') }} {{ host.latestHeight }} {{ !host.checked ? '⏳' : (host.outOfSync ? '🚫' : (host.latestHeight && host.latestHeight < maxHeight ? '🟧' : '✅')) }} - - - @if (host.hashes?.[type]) { + + + @if (type !== 'core' && host.hashes?.[type]) { {{ host.hashes[type].slice(0, 8) || '?' }} } @else { ? diff --git a/frontend/src/app/components/server-health/server-health.component.ts b/frontend/src/app/components/server-health/server-health.component.ts index 1db4bd91a..7c5ed75ba 100644 --- a/frontend/src/app/components/server-health/server-health.component.ts +++ b/frontend/src/app/components/server-health/server-health.component.ts @@ -23,6 +23,7 @@ export class ServerHealthComponent implements OnInit { hybrid: 'mempool.space', backend: 'mempool', electrs: 'electrs', + ssr: 'mempool.space', }; constructor(