fix(bitcoin/lnd): rpcbind=0.0.0.0 in generated bitcoin.conf + RPC readiness gate for dependent installs
Root cause of the fresh-install triple failure (LND needing ~5 install attempts, Bitcoin UI bitcoin-rpc 502, backend getblockchaininfo errors): the legacy conf writer set rpcallowip but never rpcbind, so bitcoind bound RPC to the container's loopback only — every dial over the container network (LND, bitcoin-ui) and the rootless port-forward was refused. LND exited instantly, the 60s post-start poll read that as INSTALL CRASH, and the wallet (and its seed backup) never got created. - write_bitcoin_conf + ensure_bitcoin_rpc_config now emit/ensure rpcbind=0.0.0.0 (host exposure unchanged: publish stays 127.0.0.1) - new install gate: bitcoin-dependent apps (lnd/electrs/electrumx/ mempool-electrs/btcpay) wait up to 3 min for an authenticated getblockchaininfo to answer before their container starts, with INSTALL WAIT progress lines; on timeout the install fails with an actionable message instead of a crash-looping container Verified live on the fresh-ISO framework node: after adding rpcbind and restarting bitcoin-knots, LND connected, created its wallet, baked macaroons, and settled into 'Waiting for chain backend to finish sync'. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
b48ea85961
commit
f2a94548ab
@ -816,6 +816,15 @@ impl RpcHandler {
|
||||
};
|
||||
run_args.push(&effective_image);
|
||||
|
||||
// Bitcoin-dependent apps (LND, electrs, BTCPay…) exit immediately if
|
||||
// bitcoind's RPC isn't answering when they start; the 60s post-start
|
||||
// poll then reads that exit as INSTALL CRASH and the whole install
|
||||
// fails — the "LND took 5 attempts" failure mode on fresh installs.
|
||||
// Gate the container start on the RPC actually responding (IBD is
|
||||
// fine — getblockchaininfo answers during sync) with a generous wait,
|
||||
// and fail with an actionable message instead of a crash-looping app.
|
||||
wait_for_bitcoin_rpc_gate(package_id).await?;
|
||||
|
||||
install_log(&format!(
|
||||
"INSTALL RUN: {} — podman run {} (image: {})",
|
||||
package_id, container_name, effective_image
|
||||
@ -1380,11 +1389,18 @@ impl RpcHandler {
|
||||
// (self-shrunk on restart); duplicating it to stdout pushed every IBD
|
||||
// "UpdateTip" line through conmon into journald (>1 GB/day). Deep
|
||||
// debugging uses /var/lib/archipelago/bitcoin/debug.log.
|
||||
// rpcbind=0.0.0.0 is REQUIRED inside a container: with rpcallowip set
|
||||
// but no rpcbind, bitcoind binds RPC to 127.0.0.1 in the container
|
||||
// netns only — LND / the Bitcoin UI dialing bitcoin-knots:8332 over
|
||||
// the bridge get connection refused (fresh-install LND crash-loop +
|
||||
// bitcoin-rpc 502, seen on the 1.7.99 ISO). The port publish stays
|
||||
// 127.0.0.1-only on the host, so exposure is unchanged.
|
||||
let bitcoin_conf = format!(
|
||||
"\
|
||||
# rpcauth: salted hash only - no plaintext password in config or CLI\n\
|
||||
{}\n\
|
||||
server=1\n\
|
||||
rpcbind=0.0.0.0\n\
|
||||
rpcallowip=0.0.0.0/0\n\
|
||||
listen=1\n\
|
||||
rpcthreads=16\n\
|
||||
@ -2475,6 +2491,82 @@ async fn wait_for_adopted_container(package_id: &str, container_name: &str) -> R
|
||||
))
|
||||
}
|
||||
|
||||
/// One-shot probe: does bitcoind answer an authenticated getblockchaininfo?
|
||||
/// Works during IBD (the call answers with progress while syncing). Goes via
|
||||
/// the host-published RPC port, which fails in exactly the same conditions
|
||||
/// as the container-network path (bitcoind down, still binding, bad rpcbind).
|
||||
async fn bitcoin_rpc_answering() -> bool {
|
||||
let (user, pass) = crate::bitcoin_rpc::bitcoin_rpc_credentials().await;
|
||||
let client = match reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(5))
|
||||
.build()
|
||||
{
|
||||
Ok(c) => c,
|
||||
Err(_) => return false,
|
||||
};
|
||||
let body = serde_json::json!({
|
||||
"jsonrpc": "1.0",
|
||||
"id": "install-gate",
|
||||
"method": "getblockchaininfo",
|
||||
"params": [],
|
||||
});
|
||||
match client
|
||||
.post(crate::constants::BITCOIN_RPC_URL)
|
||||
.basic_auth(&user, Some(&pass))
|
||||
.json(&body)
|
||||
.send()
|
||||
.await
|
||||
{
|
||||
Ok(resp) => resp.status().is_success(),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Hold the install of a bitcoin-dependent app until bitcoind's RPC answers,
|
||||
/// up to 3 minutes. No-op for apps that don't need bitcoin at start.
|
||||
async fn wait_for_bitcoin_rpc_gate(package_id: &str) -> Result<()> {
|
||||
if !matches!(
|
||||
package_id,
|
||||
"lnd" | "electrumx" | "electrs" | "mempool-electrs" | "btcpay-server" | "btcpayserver"
|
||||
) {
|
||||
return Ok(());
|
||||
}
|
||||
let deadline = tokio::time::Instant::now() + Duration::from_secs(180);
|
||||
let mut announced = false;
|
||||
while !bitcoin_rpc_answering().await {
|
||||
if tokio::time::Instant::now() >= deadline {
|
||||
install_log(&format!(
|
||||
"INSTALL FAIL: {} — Bitcoin RPC not answering after 180s; refusing to start a container that would crash-loop",
|
||||
package_id
|
||||
))
|
||||
.await;
|
||||
anyhow::bail!(
|
||||
"Bitcoin's RPC is not responding, and {} needs it to start. \
|
||||
Bitcoin may still be starting up — wait a minute and try again. \
|
||||
If this persists, check the Bitcoin app logs.",
|
||||
package_id
|
||||
);
|
||||
}
|
||||
if !announced {
|
||||
install_log(&format!(
|
||||
"INSTALL WAIT: {} — waiting for Bitcoin RPC to become ready (up to 3 min)",
|
||||
package_id
|
||||
))
|
||||
.await;
|
||||
announced = true;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
}
|
||||
if announced {
|
||||
install_log(&format!(
|
||||
"INSTALL WAIT OK: {} — Bitcoin RPC is answering, starting container",
|
||||
package_id
|
||||
))
|
||||
.await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn ensure_bitcoin_rpc_config() -> Result<bool> {
|
||||
let script = r#"
|
||||
set -eu
|
||||
@ -2502,6 +2594,7 @@ ensure_line() {
|
||||
fi
|
||||
}
|
||||
ensure_line server=1
|
||||
ensure_line rpcbind=0.0.0.0
|
||||
ensure_line rpcallowip=0.0.0.0/0
|
||||
ensure_line listen=1
|
||||
ensure_line rpcthreads=16
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user