feat: non-blocking warmup — connect during daemon initialization
Instead of blocking the entire UI with "Activating best chain..." until the daemon finishes warmup, treat warmup responses as a successful connection. The wallet now: - Sets connected=true + warming_up=true when daemon returns RPC -28 - Shows warmup status with block progress in the loading overlay - Polls getinfo every few seconds to detect warmup completion - Allows Console, Peers, Settings tabs during warmup - Shows orange status indicator with warmup message in status bar - Skips balance/tx/address refresh until warmup completes - Triggers full data refresh once daemon is ready Also: fix curl handle/header leak on reconnect, fill in empty externalDetected error branch, bump version to v1.2.0 in build scripts.
This commit is contained in:
@@ -150,12 +150,29 @@ void App::tryConnect()
|
||||
worker_->post([this, config, daemonStarting, externalDetected, attempt]() -> rpc::RPCWorker::MainCb {
|
||||
bool connected = rpc_->connect(config.host, config.port, config.rpcuser, config.rpcpassword);
|
||||
std::string connectErr = rpc_->getLastConnectError();
|
||||
bool warmingUp = rpc_->isWarmingUp();
|
||||
std::string warmupStatus = rpc_->getWarmupStatus();
|
||||
|
||||
return [this, config, connected, daemonStarting, externalDetected, attempt, connectErr]() {
|
||||
return [this, config, connected, warmingUp, warmupStatus, daemonStarting, externalDetected, attempt, connectErr]() {
|
||||
if (connected) {
|
||||
VERBOSE_LOGF("[connect #%d] Connected successfully\n", attempt);
|
||||
VERBOSE_LOGF("[connect #%d] Connected successfully%s\n", attempt,
|
||||
warmingUp ? " (daemon warming up)" : "");
|
||||
saved_config_ = config; // save for fast-lane connection
|
||||
onConnected();
|
||||
if (warmingUp) {
|
||||
// Daemon is reachable and auth works, but RPC calls will
|
||||
// fail until warmup completes. Set the warmup state so
|
||||
// the UI shows status instead of a blocking overlay.
|
||||
state_.warming_up = true;
|
||||
state_.warmup_status = warmupStatus;
|
||||
// Append current block height from daemon output
|
||||
if (embedded_daemon_) {
|
||||
int h = embedded_daemon_->getLastBlockHeight();
|
||||
if (h > 0)
|
||||
state_.warmup_status += " (Block " + std::to_string(h) + ")";
|
||||
}
|
||||
connection_status_ = state_.warmup_status;
|
||||
}
|
||||
} else {
|
||||
// HTTP 401 = authentication failure. The daemon is running
|
||||
// but our rpcuser/rpcpassword don't match. Don't retry
|
||||
@@ -203,18 +220,6 @@ void App::tryConnect()
|
||||
"RPC authentication failed (HTTP 401). "
|
||||
"The rpcuser/rpcpassword in DRAGONX.conf don't match the running daemon. "
|
||||
"Restart the daemon or correct the credentials.");
|
||||
} else if (connectErr.find("Loading") != std::string::npos ||
|
||||
connectErr.find("Verifying") != std::string::npos ||
|
||||
connectErr.find("Activating") != std::string::npos ||
|
||||
connectErr.find("Rewinding") != std::string::npos ||
|
||||
connectErr.find("Rescanning") != std::string::npos ||
|
||||
connectErr.find("Pruning") != std::string::npos) {
|
||||
// Daemon is reachable but still in warmup (Loading block index, etc.)
|
||||
// Check this BEFORE daemonStarting so the actual warmup status is shown.
|
||||
state_.connected = false;
|
||||
connection_status_ = connectErr;
|
||||
VERBOSE_LOGF("[connect #%d] Daemon warmup: %s\n", attempt, connectErr.c_str());
|
||||
core_timer_ = CORE_INTERVAL_DEFAULT - 1.0f;
|
||||
} else if (daemonStarting) {
|
||||
state_.connected = false;
|
||||
// Show the actual RPC error alongside the waiting message so
|
||||
@@ -228,6 +233,15 @@ void App::tryConnect()
|
||||
attempt, connectErr.c_str());
|
||||
core_timer_ = CORE_INTERVAL_DEFAULT - 1.0f;
|
||||
} else if (externalDetected) {
|
||||
state_.connected = false;
|
||||
if (!connectErr.empty()) {
|
||||
connection_status_ = "Connecting to daemon — " + connectErr;
|
||||
} else {
|
||||
connection_status_ = "Connecting to external daemon...";
|
||||
}
|
||||
VERBOSE_LOGF("[connect #%d] External daemon detected but RPC failed (%s), will retry...\n",
|
||||
attempt, connectErr.c_str());
|
||||
core_timer_ = CORE_INTERVAL_DEFAULT - 1.0f;
|
||||
} else {
|
||||
onDisconnected("Connection failed");
|
||||
VERBOSE_LOGF("[connect #%d] RPC connection failed — no daemon starting, no external detected\n", attempt);
|
||||
@@ -376,6 +390,8 @@ void App::onConnected()
|
||||
void App::onDisconnected(const std::string& reason)
|
||||
{
|
||||
state_.connected = false;
|
||||
state_.warming_up = false;
|
||||
state_.warmup_status.clear();
|
||||
state_.clear();
|
||||
connection_status_ = reason;
|
||||
|
||||
@@ -490,6 +506,13 @@ void App::refreshData()
|
||||
{
|
||||
if (!state_.connected || !rpc_ || !worker_) return;
|
||||
|
||||
// During warmup, only poll for warmup completion via refreshCoreData.
|
||||
// Other RPC calls (balance, addresses, transactions) will fail with -28.
|
||||
if (state_.warming_up) {
|
||||
refreshCoreData();
|
||||
return;
|
||||
}
|
||||
|
||||
// Dispatch each category independently — results trickle into the UI
|
||||
// as each completes, rather than waiting for the slowest phase.
|
||||
refreshCoreData();
|
||||
@@ -518,6 +541,57 @@ void App::refreshCoreData()
|
||||
{
|
||||
if (!state_.connected) return;
|
||||
|
||||
// During warmup, poll getinfo to detect when warmup ends.
|
||||
// Most RPC calls (balance, blockchain info) will fail with -28 during warmup.
|
||||
if (state_.warming_up) {
|
||||
if (core_refresh_in_progress_.exchange(true)) return;
|
||||
worker_->post([this]() -> rpc::RPCWorker::MainCb {
|
||||
json info;
|
||||
bool ok = false;
|
||||
std::string errMsg;
|
||||
try {
|
||||
info = rpc_->call("getinfo");
|
||||
ok = true;
|
||||
} catch (const std::exception& e) {
|
||||
errMsg = e.what();
|
||||
}
|
||||
return [this, info, ok, errMsg]() {
|
||||
if (ok) {
|
||||
// Warmup finished — daemon is fully ready
|
||||
state_.warming_up = false;
|
||||
state_.warmup_status.clear();
|
||||
connection_status_ = "Connected";
|
||||
VERBOSE_LOGF("[warmup] Daemon ready, warmup complete\n");
|
||||
// Parse initial info
|
||||
try {
|
||||
if (info.contains("version"))
|
||||
state_.daemon_version = info["version"].get<int>();
|
||||
if (info.contains("blocks"))
|
||||
state_.sync.blocks = info["blocks"].get<int>();
|
||||
if (info.contains("longestchain")) {
|
||||
int lc = info["longestchain"].get<int>();
|
||||
if (lc > 0) state_.longestchain = lc;
|
||||
}
|
||||
} catch (...) {}
|
||||
// Trigger full data refresh now that daemon is ready
|
||||
refreshData();
|
||||
} else {
|
||||
// Still warming up — update status
|
||||
state_.warmup_status = errMsg;
|
||||
if (embedded_daemon_) {
|
||||
int h = embedded_daemon_->getLastBlockHeight();
|
||||
if (h > 0)
|
||||
state_.warmup_status += " (Block " + std::to_string(h) + ")";
|
||||
}
|
||||
connection_status_ = state_.warmup_status;
|
||||
VERBOSE_LOGF("[warmup] Still warming up: %s\n", errMsg.c_str());
|
||||
}
|
||||
core_refresh_in_progress_.store(false, std::memory_order_release);
|
||||
};
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Use fast-lane on Overview for snappier balance updates
|
||||
bool useFast = (current_page_ == ui::NavPage::Overview);
|
||||
auto* w = useFast && fast_worker_ && fast_worker_->isRunning()
|
||||
|
||||
Reference in New Issue
Block a user