Compare commits
3 Commits
aa24505314
...
9e1673ef7f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e1673ef7f | ||
|
|
44a7b2a8ab | ||
|
|
d0bf878555 |
@@ -1502,6 +1502,9 @@ _BTC_SYNC_CACHE_TTL = 5 # seconds
|
|||||||
_btc_version_cache: tuple[float, dict | None] = (0.0, None)
|
_btc_version_cache: tuple[float, dict | None] = (0.0, None)
|
||||||
_BTC_VERSION_CACHE_TTL = 60 # seconds — version doesn't change at runtime
|
_BTC_VERSION_CACHE_TTL = 60 # seconds — version doesn't change at runtime
|
||||||
|
|
||||||
|
# Cache for ``bitcoind --version`` output (available even before RPC is ready)
|
||||||
|
_btcd_version_cache: tuple[float, str | None] = (0.0, None)
|
||||||
|
|
||||||
|
|
||||||
# ── Generic service version detection (NixOS store path) ─────────
|
# ── Generic service version detection (NixOS store path) ─────────
|
||||||
|
|
||||||
@@ -1616,6 +1619,57 @@ def _get_bitcoin_version_info() -> dict | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_bitcoind_version() -> str | None:
|
||||||
|
"""Run ``bitcoind --version`` and return the raw version string, or None on error.
|
||||||
|
|
||||||
|
Parses the first output line to extract the token after "version ".
|
||||||
|
For example: "Bitcoin Knots daemon version v29.3.knots20260210+bip110-v0.4.1"
|
||||||
|
returns "v29.3.knots20260210+bip110-v0.4.1".
|
||||||
|
|
||||||
|
Works regardless of whether the RPC server is ready (IBD, warmup, etc.).
|
||||||
|
Results are cached for 60 seconds (_BTC_VERSION_CACHE_TTL).
|
||||||
|
"""
|
||||||
|
global _btcd_version_cache
|
||||||
|
now = time.monotonic()
|
||||||
|
cached_at, cached_val = _btcd_version_cache
|
||||||
|
if now - cached_at < _BTC_VERSION_CACHE_TTL:
|
||||||
|
return cached_val
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["bitcoind", "--version"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
if result.returncode == 0 and result.stdout.strip():
|
||||||
|
first_line = result.stdout.splitlines()[0]
|
||||||
|
m = re.search(r"version\s+(v?\S+)", first_line, re.IGNORECASE)
|
||||||
|
if m:
|
||||||
|
ver = m.group(1)
|
||||||
|
_btcd_version_cache = (now, ver)
|
||||||
|
return ver
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
_btcd_version_cache = (now, None)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _format_bitcoin_version(raw_version: str, icon: str = "") -> str:
|
||||||
|
"""Format a raw version string from ``bitcoind --version`` for tile display.
|
||||||
|
|
||||||
|
Strips the ``+bip110-vX.Y.Z`` patch suffix so the base version is shown
|
||||||
|
cleanly (e.g. "v29.3.knots20260210+bip110-v0.4.1" → "v29.3.knots20260210").
|
||||||
|
For the BIP110 tile (icon == "bip110") a " (bip110)" tag is appended.
|
||||||
|
"""
|
||||||
|
# Remove the +bip110... patch suffix that appears in BIP-110 builds
|
||||||
|
display = re.sub(r"\+bip110\S*", "", raw_version)
|
||||||
|
if icon == "bip110" and "(bip110)" not in display.lower():
|
||||||
|
display += " (bip110)"
|
||||||
|
return display
|
||||||
|
|
||||||
|
|
||||||
def _get_bitcoin_sync_info() -> dict | None:
|
def _get_bitcoin_sync_info() -> dict | None:
|
||||||
"""Call bitcoin-cli getblockchaininfo and return parsed JSON, or None on error.
|
"""Call bitcoin-cli getblockchaininfo and return parsed JSON, or None on error.
|
||||||
|
|
||||||
@@ -1668,16 +1722,15 @@ async def api_bitcoin_sync():
|
|||||||
async def api_bitcoin_version():
|
async def api_bitcoin_version():
|
||||||
"""Return the version string of the active bitcoind implementation."""
|
"""Return the version string of the active bitcoind implementation."""
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
info = await loop.run_in_executor(None, _get_bitcoin_version_info)
|
raw_ver = await loop.run_in_executor(None, _get_bitcoind_version)
|
||||||
if info is None:
|
if raw_ver is None:
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=503,
|
status_code=503,
|
||||||
content={"error": "bitcoin-cli unavailable or bitcoind not running"},
|
content={"error": "bitcoind --version failed or bitcoind not on PATH"},
|
||||||
)
|
)
|
||||||
subversion = info.get("subversion", "")
|
|
||||||
return {
|
return {
|
||||||
"version": _parse_bitcoin_subversion(subversion),
|
"version": _format_bitcoin_version(raw_ver),
|
||||||
"subversion": subversion,
|
"raw_version": raw_ver,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1802,12 +1855,9 @@ async def api_services():
|
|||||||
service_data["sync_blocks"] = sync_blocks
|
service_data["sync_blocks"] = sync_blocks
|
||||||
service_data["sync_headers"] = sync_headers
|
service_data["sync_headers"] = sync_headers
|
||||||
if unit == "bitcoind.service" and enabled:
|
if unit == "bitcoind.service" and enabled:
|
||||||
ver_info = await loop.run_in_executor(None, _get_bitcoin_version_info)
|
raw_ver = await loop.run_in_executor(None, _get_bitcoind_version)
|
||||||
if ver_info is not None:
|
if raw_ver is not None:
|
||||||
subversion = ver_info.get("subversion", "")
|
btc_ver = _format_bitcoin_version(raw_ver, icon=icon)
|
||||||
btc_ver = _parse_bitcoin_subversion(subversion)
|
|
||||||
if icon == "bip110" and "(bip110)" not in btc_ver.lower():
|
|
||||||
btc_ver += " (bip110)"
|
|
||||||
service_data["bitcoin_version"] = btc_ver # backwards compat
|
service_data["bitcoin_version"] = btc_ver # backwards compat
|
||||||
service_data["version"] = btc_ver
|
service_data["version"] = btc_ver
|
||||||
elif unit != "bitcoind.service":
|
elif unit != "bitcoind.service":
|
||||||
@@ -2088,12 +2138,9 @@ async def api_service_detail(unit: str, icon: str | None = None):
|
|||||||
service_detail["sync_headers"] = sync_headers
|
service_detail["sync_headers"] = sync_headers
|
||||||
if unit == "bitcoind.service" and enabled:
|
if unit == "bitcoind.service" and enabled:
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
ver_info = await loop.run_in_executor(None, _get_bitcoin_version_info)
|
raw_ver = await loop.run_in_executor(None, _get_bitcoind_version)
|
||||||
if ver_info is not None:
|
if raw_ver is not None:
|
||||||
subversion = ver_info.get("subversion", "")
|
btc_ver = _format_bitcoin_version(raw_ver, icon=icon)
|
||||||
btc_ver = _parse_bitcoin_subversion(subversion)
|
|
||||||
if icon == "bip110" and "(bip110)" not in btc_ver.lower():
|
|
||||||
btc_ver += " (bip110)"
|
|
||||||
service_detail["bitcoin_version"] = btc_ver # backwards compat
|
service_detail["bitcoin_version"] = btc_ver # backwards compat
|
||||||
service_detail["version"] = btc_ver
|
service_detail["version"] = btc_ver
|
||||||
elif unit != "bitcoind.service":
|
elif unit != "bitcoind.service":
|
||||||
|
|||||||
Reference in New Issue
Block a user