feat: electrs standalone install with bitcoin dependency + progress UI
- Add electrs to marketplace as standalone installable app - Add dependency check: refuse install if no bitcoin node is running - Use container DNS (bitcoin-knots:8332) on archy-net instead of host IP - Auto-create bitcoin.conf with txindex + RPC on bitcoin-knots install - Auto-build and start electrs-ui container post-install - Show index size and estimated progress during initial sync - Add /electrs-status and /health nginx proxy routes - Remove Tailwind CDN from electrs-ui, use inline styles Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
825d082003
commit
a5757d27f1
@@ -9,6 +9,9 @@ use std::time::Duration;
|
||||
const ELECTRS_HOST: &str = "127.0.0.1";
|
||||
const ELECTRS_PORT: u16 = 50001;
|
||||
const BITCOIN_RPC_URL: &str = "http://127.0.0.1:8332/";
|
||||
const ELECTRS_DATA_DIR: &str = "/var/lib/archipelago/mempool-electrs";
|
||||
// Approximate final index size in bytes for mainnet with --lightmode (~35GB)
|
||||
const ESTIMATED_FULL_INDEX_BYTES: f64 = 35_000_000_000.0;
|
||||
|
||||
/// Build Bitcoin RPC Basic auth header from env vars.
|
||||
/// Falls back to cookie auth file if env vars are not set.
|
||||
@@ -27,6 +30,35 @@ pub struct ElectrsSyncStatus {
|
||||
pub progress_pct: f64,
|
||||
pub status: String,
|
||||
pub error: Option<String>,
|
||||
/// Index data size in human-readable format (e.g. "11.2 GB")
|
||||
pub index_size: Option<String>,
|
||||
}
|
||||
|
||||
/// Get the total size of a directory in bytes.
|
||||
fn dir_size_bytes(path: &str) -> u64 {
|
||||
let mut total: u64 = 0;
|
||||
if let Ok(entries) = std::fs::read_dir(path) {
|
||||
for entry in entries.flatten() {
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
total += dir_size_bytes(&path.to_string_lossy());
|
||||
} else if let Ok(meta) = entry.metadata() {
|
||||
total += meta.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
total
|
||||
}
|
||||
|
||||
/// Format bytes as human-readable string.
|
||||
fn format_bytes(bytes: u64) -> String {
|
||||
if bytes >= 1_000_000_000 {
|
||||
format!("{:.1} GB", bytes as f64 / 1_000_000_000.0)
|
||||
} else if bytes >= 1_000_000 {
|
||||
format!("{:.1} MB", bytes as f64 / 1_000_000.0)
|
||||
} else {
|
||||
format!("{} KB", bytes / 1_000)
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch electrs indexed height via Electrum protocol (TCP JSON-RPC).
|
||||
@@ -100,6 +132,14 @@ async fn bitcoin_network_height() -> Result<u64> {
|
||||
|
||||
/// Get electrs sync status. Runs blocking electrs call in spawn_blocking.
|
||||
pub async fn get_electrs_sync_status() -> ElectrsSyncStatus {
|
||||
// Get index data size (non-blocking, fast filesystem stat)
|
||||
let data_bytes = dir_size_bytes(ELECTRS_DATA_DIR);
|
||||
let index_size = if data_bytes > 0 {
|
||||
Some(format_bytes(data_bytes))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let network_height = match bitcoin_network_height().await {
|
||||
Ok(h) => h,
|
||||
Err(e) => {
|
||||
@@ -109,6 +149,7 @@ pub async fn get_electrs_sync_status() -> ElectrsSyncStatus {
|
||||
progress_pct: 0.0,
|
||||
status: "error".to_string(),
|
||||
error: Some(format!("Bitcoin RPC: {}", e)),
|
||||
index_size,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -119,19 +160,36 @@ pub async fn get_electrs_sync_status() -> ElectrsSyncStatus {
|
||||
// Electrs doesn't listen on 50001 until indexing completes (can take hours)
|
||||
let err_msg = e.to_string();
|
||||
let (status, error) = if err_msg.contains("connect") || err_msg.contains("Connection refused") {
|
||||
// Estimate progress from data directory size
|
||||
let est_pct = if data_bytes > 0 {
|
||||
((data_bytes as f64 / ESTIMATED_FULL_INDEX_BYTES) * 100.0).min(99.0)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let size_str = index_size.clone().unwrap_or_else(|| "0 MB".to_string());
|
||||
(
|
||||
"indexing".to_string(),
|
||||
Some("Electrs is building the index. Electrum RPC will be available when indexing completes (may take hours).".to_string()),
|
||||
Some(format!(
|
||||
"Building index ({} / ~35 GB estimated). Electrum RPC will be available when complete.",
|
||||
size_str
|
||||
)),
|
||||
)
|
||||
} else {
|
||||
("error".to_string(), Some(format!("Electrs: {}", e)))
|
||||
};
|
||||
// Use estimated progress when indexing
|
||||
let progress_pct = if status == "indexing" && data_bytes > 0 {
|
||||
((data_bytes as f64 / ESTIMATED_FULL_INDEX_BYTES) * 100.0).min(99.0)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
return ElectrsSyncStatus {
|
||||
indexed_height: 0,
|
||||
network_height,
|
||||
progress_pct: 0.0,
|
||||
progress_pct,
|
||||
status,
|
||||
error,
|
||||
index_size,
|
||||
};
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -141,6 +199,7 @@ pub async fn get_electrs_sync_status() -> ElectrsSyncStatus {
|
||||
progress_pct: 0.0,
|
||||
status: "error".to_string(),
|
||||
error: Some(format!("Task: {}", e)),
|
||||
index_size,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -163,5 +222,6 @@ pub async fn get_electrs_sync_status() -> ElectrsSyncStatus {
|
||||
progress_pct,
|
||||
status: status.to_string(),
|
||||
error: None,
|
||||
index_size,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user