Compare commits
2
Commits
28b9e0dffb
...
dc4426810f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc4426810f | ||
|
|
915c1b4d23 |
@@ -20,7 +20,7 @@
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
VERSION="1.1.1"
|
||||
VERSION="1.2.0"
|
||||
|
||||
# ── Colours ──────────────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'
|
||||
|
||||
@@ -654,6 +654,7 @@
|
||||
"show_qr_code": "QR-Code anzeigen",
|
||||
"showing_transactions": "Zeige %d–%d von %d Transaktionen (gesamt: %zu)",
|
||||
"simple_background": "Einfacher Hintergrund",
|
||||
"slider_off": "Aus",
|
||||
"start_mining": "Mining starten",
|
||||
"status": "Status",
|
||||
"stop_external": "Externen Daemon stoppen",
|
||||
@@ -670,6 +671,12 @@
|
||||
"time_hours_ago": "vor %d Stunden",
|
||||
"time_minutes_ago": "vor %d Minuten",
|
||||
"time_seconds_ago": "vor %d Sekunden",
|
||||
"timeout_15min": "15 Min",
|
||||
"timeout_1hour": "1 Stunde",
|
||||
"timeout_1min": "1 Min",
|
||||
"timeout_30min": "30 Min",
|
||||
"timeout_5min": "5 Min",
|
||||
"timeout_off": "Aus",
|
||||
"to": "An",
|
||||
"to_upper": "AN",
|
||||
"tools": "WERKZEUGE",
|
||||
|
||||
@@ -654,6 +654,7 @@
|
||||
"show_qr_code": "Mostrar Código QR",
|
||||
"showing_transactions": "Mostrando %d–%d de %d transacciones (total: %zu)",
|
||||
"simple_background": "Fondo simple",
|
||||
"slider_off": "Apagado",
|
||||
"start_mining": "Iniciar Minería",
|
||||
"status": "Estado",
|
||||
"stop_external": "Detener daemon externo",
|
||||
@@ -670,6 +671,12 @@
|
||||
"time_hours_ago": "hace %d horas",
|
||||
"time_minutes_ago": "hace %d minutos",
|
||||
"time_seconds_ago": "hace %d segundos",
|
||||
"timeout_15min": "15 min",
|
||||
"timeout_1hour": "1 hora",
|
||||
"timeout_1min": "1 min",
|
||||
"timeout_30min": "30 min",
|
||||
"timeout_5min": "5 min",
|
||||
"timeout_off": "Apagado",
|
||||
"to": "Para",
|
||||
"to_upper": "PARA",
|
||||
"tools": "HERRAMIENTAS",
|
||||
|
||||
@@ -654,6 +654,7 @@
|
||||
"show_qr_code": "Afficher le code QR",
|
||||
"showing_transactions": "Affichage %d–%d sur %d transactions (total : %zu)",
|
||||
"simple_background": "Arrière-plan simple",
|
||||
"slider_off": "Désactivé",
|
||||
"start_mining": "Démarrer le minage",
|
||||
"status": "Statut",
|
||||
"stop_external": "Arrêter le daemon externe",
|
||||
@@ -670,6 +671,12 @@
|
||||
"time_hours_ago": "il y a %d heures",
|
||||
"time_minutes_ago": "il y a %d minutes",
|
||||
"time_seconds_ago": "il y a %d secondes",
|
||||
"timeout_15min": "15 min",
|
||||
"timeout_1hour": "1 heure",
|
||||
"timeout_1min": "1 min",
|
||||
"timeout_30min": "30 min",
|
||||
"timeout_5min": "5 min",
|
||||
"timeout_off": "Désactivé",
|
||||
"to": "À",
|
||||
"to_upper": "À",
|
||||
"tools": "OUTILS",
|
||||
|
||||
@@ -654,6 +654,7 @@
|
||||
"show_qr_code": "QRコードを表示",
|
||||
"showing_transactions": "%d–%d / %d 件の取引を表示中(合計:%zu)",
|
||||
"simple_background": "シンプル背景",
|
||||
"slider_off": "オフ",
|
||||
"start_mining": "マイニング開始",
|
||||
"status": "ステータス",
|
||||
"stop_external": "外部デーモンを停止",
|
||||
@@ -670,6 +671,12 @@
|
||||
"time_hours_ago": "%d時間前",
|
||||
"time_minutes_ago": "%d分前",
|
||||
"time_seconds_ago": "%d秒前",
|
||||
"timeout_15min": "15分",
|
||||
"timeout_1hour": "1時間",
|
||||
"timeout_1min": "1分",
|
||||
"timeout_30min": "30分",
|
||||
"timeout_5min": "5分",
|
||||
"timeout_off": "オフ",
|
||||
"to": "宛先",
|
||||
"to_upper": "宛先",
|
||||
"tools": "ツール",
|
||||
|
||||
@@ -654,6 +654,7 @@
|
||||
"show_qr_code": "QR 코드 표시",
|
||||
"showing_transactions": "%d–%d / %d건의 거래 표시 중 (총: %zu)",
|
||||
"simple_background": "단순 배경",
|
||||
"slider_off": "끔",
|
||||
"start_mining": "채굴 시작",
|
||||
"status": "상태",
|
||||
"stop_external": "외부 데몬 중지",
|
||||
@@ -670,6 +671,12 @@
|
||||
"time_hours_ago": "%d시간 전",
|
||||
"time_minutes_ago": "%d분 전",
|
||||
"time_seconds_ago": "%d초 전",
|
||||
"timeout_15min": "15분",
|
||||
"timeout_1hour": "1시간",
|
||||
"timeout_1min": "1분",
|
||||
"timeout_30min": "30분",
|
||||
"timeout_5min": "5분",
|
||||
"timeout_off": "끔",
|
||||
"to": "받는 곳",
|
||||
"to_upper": "받는 곳",
|
||||
"tools": "도구",
|
||||
|
||||
@@ -654,6 +654,7 @@
|
||||
"show_qr_code": "Mostrar Código QR",
|
||||
"showing_transactions": "Mostrando %d–%d de %d transações (total: %zu)",
|
||||
"simple_background": "Fundo simples",
|
||||
"slider_off": "Desligado",
|
||||
"start_mining": "Iniciar Mineração",
|
||||
"status": "Status",
|
||||
"stop_external": "Parar daemon externo",
|
||||
@@ -670,6 +671,12 @@
|
||||
"time_hours_ago": "há %d horas",
|
||||
"time_minutes_ago": "há %d minutos",
|
||||
"time_seconds_ago": "há %d segundos",
|
||||
"timeout_15min": "15 min",
|
||||
"timeout_1hour": "1 hora",
|
||||
"timeout_1min": "1 min",
|
||||
"timeout_30min": "30 min",
|
||||
"timeout_5min": "5 min",
|
||||
"timeout_off": "Desligado",
|
||||
"to": "Para",
|
||||
"to_upper": "PARA",
|
||||
"tools": "FERRAMENTAS",
|
||||
|
||||
@@ -654,6 +654,7 @@
|
||||
"show_qr_code": "Показать QR-код",
|
||||
"showing_transactions": "Показано %d–%d из %d транзакций (всего: %zu)",
|
||||
"simple_background": "Простой фон",
|
||||
"slider_off": "Выкл",
|
||||
"start_mining": "Начать майнинг",
|
||||
"status": "Статус",
|
||||
"stop_external": "Остановить внешний daemon",
|
||||
@@ -670,6 +671,12 @@
|
||||
"time_hours_ago": "%d часов назад",
|
||||
"time_minutes_ago": "%d минут назад",
|
||||
"time_seconds_ago": "%d секунд назад",
|
||||
"timeout_15min": "15 мин",
|
||||
"timeout_1hour": "1 час",
|
||||
"timeout_1min": "1 мин",
|
||||
"timeout_30min": "30 мин",
|
||||
"timeout_5min": "5 мин",
|
||||
"timeout_off": "Выкл",
|
||||
"to": "Кому",
|
||||
"to_upper": "КОМУ",
|
||||
"tools": "ИНСТРУМЕНТЫ",
|
||||
|
||||
@@ -654,6 +654,7 @@
|
||||
"show_qr_code": "显示二维码",
|
||||
"showing_transactions": "显示第 %d–%d 笔,共 %d 笔交易(总计:%zu)",
|
||||
"simple_background": "简单背景",
|
||||
"slider_off": "关闭",
|
||||
"start_mining": "开始挖矿",
|
||||
"status": "状态",
|
||||
"stop_external": "停止外部守护进程",
|
||||
@@ -670,6 +671,12 @@
|
||||
"time_hours_ago": "%d 小时前",
|
||||
"time_minutes_ago": "%d 分钟前",
|
||||
"time_seconds_ago": "%d 秒前",
|
||||
"timeout_15min": "15分钟",
|
||||
"timeout_1hour": "1小时",
|
||||
"timeout_1min": "1分钟",
|
||||
"timeout_30min": "30分钟",
|
||||
"timeout_5min": "5分钟",
|
||||
"timeout_off": "关闭",
|
||||
"to": "至",
|
||||
"to_upper": "至",
|
||||
"tools": "工具",
|
||||
|
||||
@@ -8,7 +8,7 @@ set -e
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BUILD_DIR="${SCRIPT_DIR}/build/linux"
|
||||
APPDIR="${BUILD_DIR}/AppDir"
|
||||
VERSION="1.1.1"
|
||||
VERSION="1.2.0"
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
|
||||
@@ -7,7 +7,7 @@ set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BUILD_DIR="${SCRIPT_DIR}/build/linux"
|
||||
VERSION="1.1.1"
|
||||
VERSION="1.2.0"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
|
||||
+60
-22
@@ -610,13 +610,16 @@ void App::update()
|
||||
core_timer_ = 0.0f;
|
||||
refreshCoreData();
|
||||
}
|
||||
if (transaction_timer_ >= active_tx_interval_) {
|
||||
transaction_timer_ = 0.0f;
|
||||
refreshTransactionData();
|
||||
}
|
||||
if (address_timer_ >= active_addr_interval_) {
|
||||
address_timer_ = 0.0f;
|
||||
refreshAddressData();
|
||||
// Skip balance/tx/address refresh during warmup — RPC calls fail with -28
|
||||
if (!state_.warming_up) {
|
||||
if (transaction_timer_ >= active_tx_interval_) {
|
||||
transaction_timer_ = 0.0f;
|
||||
refreshTransactionData();
|
||||
}
|
||||
if (address_timer_ >= active_addr_interval_) {
|
||||
address_timer_ = 0.0f;
|
||||
refreshAddressData();
|
||||
}
|
||||
}
|
||||
if (peer_timer_ >= active_peer_interval_) {
|
||||
peer_timer_ = 0.0f;
|
||||
@@ -1078,16 +1081,17 @@ void App::render()
|
||||
int fgVtxStart = fgDL->VtxBuffer.Size;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Loading overlay — show only while daemon is not yet connected.
|
||||
// Once connected, all tabs render normally; individual tabs show
|
||||
// inline sync-progress indicators when the chain is still syncing.
|
||||
// Pages that remain accessible even without a connection:
|
||||
// Console, Peers, Settings
|
||||
// Loading overlay — show only on tabs that truly need wallet data.
|
||||
// Tabs that work without full daemon connection:
|
||||
// Console, Peers, Settings, Market, Mining
|
||||
// Tabs that need balance/transaction data (show overlay):
|
||||
// Overview, Send, Receive, History
|
||||
// ---------------------------------------------------------------
|
||||
bool pageNeedsDaemon = (current_page_ != ui::NavPage::Console &&
|
||||
current_page_ != ui::NavPage::Peers &&
|
||||
current_page_ != ui::NavPage::Settings);
|
||||
bool daemonReady = state_.connected; // don't gate on sync state
|
||||
bool pageNeedsWalletData = (current_page_ == ui::NavPage::Overview ||
|
||||
current_page_ == ui::NavPage::Send ||
|
||||
current_page_ == ui::NavPage::Receive ||
|
||||
current_page_ == ui::NavPage::History);
|
||||
bool daemonReady = state_.connected && !state_.warming_up;
|
||||
|
||||
// Don't show lock screen while pool mining — xmrig runs independently
|
||||
// of the wallet and locking would block the mining UI needlessly.
|
||||
@@ -1095,7 +1099,12 @@ void App::render()
|
||||
if (state_.isLocked() && !poolMiningActive) {
|
||||
// Lock screen — covers tab content just like the loading overlay
|
||||
renderLockScreen();
|
||||
} else if (pageNeedsDaemon && (!daemonReady || (state_.connected && !state_.encryption_state_known))) {
|
||||
} else if (pageNeedsWalletData && state_.warming_up) {
|
||||
// Daemon is reachable but still initializing — show warmup overlay
|
||||
// only on wallet-data tabs. Other tabs render normally.
|
||||
lock_screen_was_visible_ = false;
|
||||
renderLoadingOverlay(contentH);
|
||||
} else if (pageNeedsWalletData && (!daemonReady || (state_.connected && !state_.encryption_state_known))) {
|
||||
// Track how long we've been waiting for encryption state
|
||||
if (state_.connected && !state_.encryption_state_known) {
|
||||
encryption_check_timer_ += ImGui::GetIO().DeltaTime;
|
||||
@@ -1421,7 +1430,16 @@ void App::renderStatusBar()
|
||||
// Connection status
|
||||
float dotOpacity = S.drawElement("components.status-bar", "connection-dot").opacity;
|
||||
if (dotOpacity < 0.0f) dotOpacity = 1.0f;
|
||||
if (state_.connected) {
|
||||
if (state_.warming_up) {
|
||||
ImGui::PushFont(ui::material::Type().iconSmall());
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.7f, 0.0f, dotOpacity), ICON_MD_CIRCLE);
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine(0, sbIconTextGap);
|
||||
// Show truncated warmup status (e.g. "Activating best chain... (Block 12345)")
|
||||
const char* warmupText = state_.warmup_status.empty()
|
||||
? "Warming up..." : state_.warmup_status.c_str();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.0f, 1.0f), "%s", warmupText);
|
||||
} else if (state_.connected) {
|
||||
ImGui::PushFont(ui::material::Type().iconSmall());
|
||||
ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, dotOpacity), ICON_MD_CIRCLE);
|
||||
ImGui::PopFont();
|
||||
@@ -1467,7 +1485,11 @@ void App::renderStatusBar()
|
||||
static double s_prev_time = 0.0;
|
||||
static double s_blocks_per_sec = 0.0;
|
||||
double now = ImGui::GetTime();
|
||||
if (s_prev_time > 0.0 && state_.sync.blocks > s_prev_blocks) {
|
||||
if (s_prev_time == 0.0) {
|
||||
// First sample — seed baseline, don't compute rate yet
|
||||
s_prev_blocks = state_.sync.blocks;
|
||||
s_prev_time = now;
|
||||
} else if (state_.sync.blocks > s_prev_blocks) {
|
||||
double dt = now - s_prev_time;
|
||||
if (dt > 0.5) {
|
||||
double raw = (state_.sync.blocks - s_prev_blocks) / dt;
|
||||
@@ -1478,9 +1500,10 @@ void App::renderStatusBar()
|
||||
s_prev_blocks = state_.sync.blocks;
|
||||
s_prev_time = now;
|
||||
}
|
||||
} else {
|
||||
s_prev_blocks = state_.sync.blocks;
|
||||
s_prev_time = now;
|
||||
} else if (now - s_prev_time > 10.0) {
|
||||
// No new blocks for 10s — decay the displayed rate
|
||||
s_blocks_per_sec *= 0.5;
|
||||
s_prev_time = now;
|
||||
}
|
||||
|
||||
if (s_blocks_per_sec > 0.1) {
|
||||
@@ -2769,6 +2792,21 @@ void App::renderLoadingOverlay(float contentH)
|
||||
curY += ts.y + gap;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// 2b. Warmup description (subtitle explaining what's happening)
|
||||
// -------------------------------------------------------------------
|
||||
if (state_.warming_up && !state_.warmup_description.empty()) {
|
||||
const char* descText = state_.warmup_description.c_str();
|
||||
ImFont* capFont = Type().caption();
|
||||
if (!capFont) capFont = ImGui::GetFont();
|
||||
// Wrap to barW so long descriptions don't overflow
|
||||
ImVec2 ts = capFont->CalcTextSizeA(capFont->LegacySize, FLT_MAX, barW, descText);
|
||||
dl->AddText(capFont, capFont->LegacySize,
|
||||
ImVec2(wp.x + cx - ts.x * 0.5f, curY),
|
||||
IM_COL32(160, 160, 160, 200), descText, nullptr, barW);
|
||||
curY += ts.y + gap;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// 3. Sync progress bar (if connected and syncing)
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
+127
-14
@@ -49,6 +49,40 @@ namespace dragonx {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// ============================================================================
|
||||
// Warmup Message Translation
|
||||
// Maps raw daemon RPC warmup messages to user-friendly text.
|
||||
// ============================================================================
|
||||
|
||||
struct WarmupText {
|
||||
const char* title;
|
||||
const char* description;
|
||||
};
|
||||
|
||||
static WarmupText translateWarmup(const std::string& raw)
|
||||
{
|
||||
if (raw.find("Loading") != std::string::npos)
|
||||
return {"Loading blockchain data...",
|
||||
"Reading the block database from disk. This may take a few minutes after updates."};
|
||||
if (raw.find("Verifying") != std::string::npos)
|
||||
return {"Verifying blockchain...",
|
||||
"Checking recent blocks to make sure your chain data is valid."};
|
||||
if (raw.find("Activating") != std::string::npos)
|
||||
return {"Processing blocks...",
|
||||
"Applying blocks to build the current chain state."};
|
||||
if (raw.find("Rewinding") != std::string::npos)
|
||||
return {"Reorganizing chain...",
|
||||
"A chain reorganization was detected. Reverting to the correct chain."};
|
||||
if (raw.find("Rescanning") != std::string::npos)
|
||||
return {"Scanning for transactions...",
|
||||
"Searching the blockchain for transactions belonging to your wallet. This can take a while."};
|
||||
if (raw.find("Pruning") != std::string::npos)
|
||||
return {"Optimizing storage...",
|
||||
"Removing old block data to free up disk space."};
|
||||
// Fallback: use the raw message
|
||||
return {raw.c_str(), ""};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Connection Management
|
||||
// ============================================================================
|
||||
@@ -150,12 +184,31 @@ 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;
|
||||
auto wt = translateWarmup(warmupStatus);
|
||||
state_.warmup_status = wt.title;
|
||||
state_.warmup_description = wt.description;
|
||||
// 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 +256,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 +269,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 +426,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 +542,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 +577,60 @@ 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();
|
||||
state_.warmup_description.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
|
||||
auto wt = translateWarmup(errMsg);
|
||||
state_.warmup_status = wt.title;
|
||||
state_.warmup_description = wt.description;
|
||||
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()
|
||||
|
||||
@@ -116,6 +116,26 @@ public:
|
||||
* @brief Get last N lines of daemon output (thread-safe snapshot)
|
||||
*/
|
||||
std::vector<std::string> getRecentLines(int maxLines = 8) const;
|
||||
|
||||
/**
|
||||
* @brief Extract the latest block height from daemon output (thread-safe).
|
||||
* Parses the last "height=N" from UpdateTip lines without copying
|
||||
* the entire output buffer. Returns -1 if no UpdateTip found.
|
||||
*/
|
||||
int getLastBlockHeight() const {
|
||||
std::lock_guard<std::mutex> lk(output_mutex_);
|
||||
// Search backwards from the end for "height="
|
||||
size_t pos = process_output_.rfind("height=");
|
||||
if (pos == std::string::npos) return -1;
|
||||
pos += 7; // skip "height="
|
||||
int h = 0;
|
||||
for (size_t i = pos; i < process_output_.size(); ++i) {
|
||||
char c = process_output_[i];
|
||||
if (c >= '0' && c <= '9') h = h * 10 + (c - '0');
|
||||
else break;
|
||||
}
|
||||
return h > 0 ? h : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether start() detected an existing daemon on the RPC port.
|
||||
|
||||
@@ -180,6 +180,9 @@ struct PoolMiningState {
|
||||
struct WalletState {
|
||||
// Connection
|
||||
bool connected = false;
|
||||
bool warming_up = false; // daemon reachable but in RPC warmup (error -28)
|
||||
std::string warmup_status; // user-friendly title, e.g. "Processing blocks..."
|
||||
std::string warmup_description; // subtitle explaining the stage
|
||||
int daemon_version = 0;
|
||||
std::string daemon_subversion;
|
||||
int protocol_version = 0;
|
||||
@@ -250,6 +253,9 @@ struct WalletState {
|
||||
|
||||
void clear() {
|
||||
connected = false;
|
||||
warming_up = false;
|
||||
warmup_status.clear();
|
||||
warmup_description.clear();
|
||||
daemon_version = 0;
|
||||
daemon_subversion.clear();
|
||||
protocol_version = 0;
|
||||
|
||||
+65
-48
@@ -2736,56 +2736,73 @@ unsigned char res_lang_de_json[] = {
|
||||
0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x3a, 0x20, 0x22, 0x45, 0x69,
|
||||
0x6e, 0x66, 0x61, 0x63, 0x68, 0x65, 0x72, 0x20, 0x48, 0x69, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x67, 0x72, 0x75, 0x6e, 0x64, 0x22, 0x2c, 0x0a, 0x20, 0x20,
|
||||
0x20, 0x20, 0x22, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x69, 0x6e,
|
||||
0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x4d, 0x69, 0x6e, 0x69, 0x6e,
|
||||
0x67, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x6e, 0x22, 0x2c, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22,
|
||||
0x3a, 0x20, 0x22, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x2c, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x65, 0x78,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x3a, 0x20, 0x22, 0x45, 0x78,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x6e, 0x20, 0x44, 0x61, 0x65, 0x6d, 0x6f,
|
||||
0x6e, 0x20, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x6e, 0x22, 0x2c, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x6d, 0x69,
|
||||
0x20, 0x20, 0x22, 0x73, 0x6c, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6f, 0x66,
|
||||
0x66, 0x22, 0x3a, 0x20, 0x22, 0x41, 0x75, 0x73, 0x22, 0x2c, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x69,
|
||||
0x6e, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x4d, 0x69, 0x6e, 0x69,
|
||||
0x6e, 0x67, 0x20, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x6e, 0x22, 0x2c,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74,
|
||||
0x74, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x20, 0x22, 0x54, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x61, 0x6b, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x69, 0x72, 0x64,
|
||||
0x20, 0xc3, 0xbc, 0x62, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x6c,
|
||||
0x74, 0x2e, 0x2e, 0x2e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22,
|
||||
0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x3a, 0x20, 0x22, 0x45,
|
||||
0x72, 0x66, 0x6f, 0x6c, 0x67, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x22, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x3a, 0x20, 0x22,
|
||||
0x5a, 0x75, 0x73, 0x61, 0x6d, 0x6d, 0x65, 0x6e, 0x66, 0x61, 0x73, 0x73,
|
||||
0x75, 0x6e, 0x67, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73,
|
||||
0x79, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x53, 0x79,
|
||||
0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x73, 0x69, 0x65, 0x72, 0x65,
|
||||
0x2e, 0x2e, 0x2e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74,
|
||||
0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x3a,
|
||||
0x20, 0x22, 0x54, 0x2d, 0x41, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x6e,
|
||||
0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x73, 0x74,
|
||||
0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22,
|
||||
0x3a, 0x20, 0x22, 0x54, 0x65, 0x73, 0x74, 0x65, 0x6e, 0x22, 0x2c, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x22, 0x3a,
|
||||
0x20, 0x22, 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x22, 0x2c, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x22, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x5f, 0x65, 0x66,
|
||||
0x66, 0x65, 0x63, 0x74, 0x73, 0x22, 0x3a, 0x20, 0x22, 0x44, 0x65, 0x73,
|
||||
0x69, 0x67, 0x6e, 0x2d, 0x45, 0x66, 0x66, 0x65, 0x6b, 0x74, 0x65, 0x22,
|
||||
0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d, 0x65, 0x5f,
|
||||
0x64, 0x61, 0x79, 0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22, 0x3a, 0x20, 0x22,
|
||||
0x76, 0x6f, 0x72, 0x20, 0x25, 0x64, 0x20, 0x54, 0x61, 0x67, 0x65, 0x6e,
|
||||
0x6e, 0x67, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x6e, 0x22, 0x2c,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
|
||||
0x22, 0x3a, 0x20, 0x22, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x2c,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x65,
|
||||
0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x3a, 0x20, 0x22, 0x45,
|
||||
0x78, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x6e, 0x20, 0x44, 0x61, 0x65, 0x6d,
|
||||
0x6f, 0x6e, 0x20, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x6e, 0x22, 0x2c,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x74, 0x6f, 0x70, 0x5f, 0x6d,
|
||||
0x69, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x4d, 0x69, 0x6e,
|
||||
0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x6e, 0x22,
|
||||
0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x75, 0x62, 0x6d, 0x69,
|
||||
0x74, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3a, 0x20, 0x22, 0x54, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x61, 0x6b, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x69, 0x72,
|
||||
0x64, 0x20, 0xc3, 0xbc, 0x62, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65,
|
||||
0x6c, 0x74, 0x2e, 0x2e, 0x2e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x22, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x3a, 0x20, 0x22,
|
||||
0x45, 0x72, 0x66, 0x6f, 0x6c, 0x67, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x22, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x3a, 0x20,
|
||||
0x22, 0x5a, 0x75, 0x73, 0x61, 0x6d, 0x6d, 0x65, 0x6e, 0x66, 0x61, 0x73,
|
||||
0x73, 0x75, 0x6e, 0x67, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22,
|
||||
0x73, 0x79, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x20, 0x22, 0x53,
|
||||
0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x73, 0x69, 0x65, 0x72,
|
||||
0x65, 0x2e, 0x2e, 0x2e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22,
|
||||
0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22,
|
||||
0x3a, 0x20, 0x22, 0x54, 0x2d, 0x41, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65,
|
||||
0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x73,
|
||||
0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x22, 0x3a, 0x20, 0x22, 0x54, 0x65, 0x73, 0x74, 0x65, 0x6e, 0x22, 0x2c,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x22,
|
||||
0x3a, 0x20, 0x22, 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x22, 0x2c, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x5f, 0x65,
|
||||
0x66, 0x66, 0x65, 0x63, 0x74, 0x73, 0x22, 0x3a, 0x20, 0x22, 0x44, 0x65,
|
||||
0x73, 0x69, 0x67, 0x6e, 0x2d, 0x45, 0x66, 0x66, 0x65, 0x6b, 0x74, 0x65,
|
||||
0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x5f, 0x68, 0x6f, 0x75, 0x72, 0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22, 0x3a,
|
||||
0x20, 0x22, 0x76, 0x6f, 0x72, 0x20, 0x25, 0x64, 0x20, 0x53, 0x74, 0x75,
|
||||
0x6e, 0x64, 0x65, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22,
|
||||
0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73,
|
||||
0x5f, 0x61, 0x67, 0x6f, 0x22, 0x3a, 0x20, 0x22, 0x76, 0x6f, 0x72, 0x20,
|
||||
0x25, 0x64, 0x20, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x6e, 0x22, 0x2c,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73,
|
||||
0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22, 0x3a,
|
||||
0x20, 0x22, 0x76, 0x6f, 0x72, 0x20, 0x25, 0x64, 0x20, 0x53, 0x65, 0x6b,
|
||||
0x5f, 0x64, 0x61, 0x79, 0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22, 0x3a, 0x20,
|
||||
0x22, 0x76, 0x6f, 0x72, 0x20, 0x25, 0x64, 0x20, 0x54, 0x61, 0x67, 0x65,
|
||||
0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22,
|
||||
0x3a, 0x20, 0x22, 0x76, 0x6f, 0x72, 0x20, 0x25, 0x64, 0x20, 0x53, 0x74,
|
||||
0x75, 0x6e, 0x64, 0x65, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x22, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65,
|
||||
0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22, 0x3a, 0x20, 0x22, 0x76, 0x6f, 0x72,
|
||||
0x20, 0x25, 0x64, 0x20, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x6e, 0x22,
|
||||
0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d, 0x65, 0x5f,
|
||||
0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x5f, 0x61, 0x67, 0x6f, 0x22,
|
||||
0x3a, 0x20, 0x22, 0x76, 0x6f, 0x72, 0x20, 0x25, 0x64, 0x20, 0x53, 0x65,
|
||||
0x6b, 0x75, 0x6e, 0x64, 0x65, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20,
|
||||
0x20, 0x22, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x31, 0x35,
|
||||
0x6d, 0x69, 0x6e, 0x22, 0x3a, 0x20, 0x22, 0x31, 0x35, 0x20, 0x4d, 0x69,
|
||||
0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d,
|
||||
0x65, 0x6f, 0x75, 0x74, 0x5f, 0x31, 0x68, 0x6f, 0x75, 0x72, 0x22, 0x3a,
|
||||
0x20, 0x22, 0x31, 0x20, 0x53, 0x74, 0x75, 0x6e, 0x64, 0x65, 0x22, 0x2c,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75,
|
||||
0x74, 0x5f, 0x31, 0x6d, 0x69, 0x6e, 0x22, 0x3a, 0x20, 0x22, 0x31, 0x20,
|
||||
0x4d, 0x69, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x33, 0x30, 0x6d, 0x69, 0x6e,
|
||||
0x22, 0x3a, 0x20, 0x22, 0x33, 0x30, 0x20, 0x4d, 0x69, 0x6e, 0x22, 0x2c,
|
||||
0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75,
|
||||
0x74, 0x5f, 0x35, 0x6d, 0x69, 0x6e, 0x22, 0x3a, 0x20, 0x22, 0x35, 0x20,
|
||||
0x4d, 0x69, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x6f, 0x66, 0x66, 0x22, 0x3a,
|
||||
0x20, 0x22, 0x41, 0x75, 0x73, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
|
||||
0x22, 0x74, 0x6f, 0x22, 0x3a, 0x20, 0x22, 0x41, 0x6e, 0x22, 0x2c, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x6f, 0x5f, 0x75, 0x70, 0x70, 0x65,
|
||||
0x72, 0x22, 0x3a, 0x20, 0x22, 0x41, 0x4e, 0x22, 0x2c, 0x0a, 0x20, 0x20,
|
||||
@@ -3402,4 +3419,4 @@ unsigned char res_lang_de_json[] = {
|
||||
0x73, 0x65, 0x73, 0x22, 0x3a, 0x20, 0x22, 0x5a, 0x2d, 0x41, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x65, 0x6e, 0x22, 0x0a, 0x7d, 0x0a
|
||||
};
|
||||
unsigned int res_lang_de_json_len = 40821;
|
||||
unsigned int res_lang_de_json_len = 41025;
|
||||
|
||||
+681
-664
File diff suppressed because it is too large
Load Diff
+714
-696
File diff suppressed because it is too large
Load Diff
+730
-713
File diff suppressed because it is too large
Load Diff
+637
-620
File diff suppressed because it is too large
Load Diff
+667
-649
File diff suppressed because it is too large
Load Diff
+951
-932
File diff suppressed because it is too large
Load Diff
+584
-566
File diff suppressed because it is too large
Load Diff
+25
-3
@@ -73,6 +73,16 @@ bool RPCClient::connect(const std::string& host, const std::string& port,
|
||||
impl_->url = "http://" + host + ":" + port + "/";
|
||||
VERBOSE_LOGF("Connecting to dragonxd at %s\n", impl_->url.c_str());
|
||||
|
||||
// Clean up previous curl handle/headers to avoid leaks on retries
|
||||
if (impl_->headers) {
|
||||
curl_slist_free_all(impl_->headers);
|
||||
impl_->headers = nullptr;
|
||||
}
|
||||
if (impl_->curl) {
|
||||
curl_easy_cleanup(impl_->curl);
|
||||
impl_->curl = nullptr;
|
||||
}
|
||||
|
||||
// Initialize curl handle
|
||||
impl_->curl = curl_easy_init();
|
||||
if (!impl_->curl) {
|
||||
@@ -81,7 +91,7 @@ bool RPCClient::connect(const std::string& host, const std::string& port,
|
||||
}
|
||||
|
||||
// Set up headers - daemon expects text/plain, not application/json
|
||||
impl_->headers = curl_slist_append(impl_->headers, "Content-Type: text/plain");
|
||||
impl_->headers = curl_slist_append(nullptr, "Content-Type: text/plain");
|
||||
std::string auth_header = "Authorization: Basic " + auth_;
|
||||
impl_->headers = curl_slist_append(impl_->headers, auth_header.c_str());
|
||||
|
||||
@@ -97,6 +107,8 @@ bool RPCClient::connect(const std::string& host, const std::string& port,
|
||||
json result = call("getinfo");
|
||||
if (result.contains("version")) {
|
||||
connected_ = true;
|
||||
warming_up_ = false;
|
||||
warmup_status_.clear();
|
||||
last_connect_error_.clear();
|
||||
DEBUG_LOGF("Connected to dragonxd v%d\n", result["version"].get<int>());
|
||||
return true;
|
||||
@@ -104,7 +116,9 @@ bool RPCClient::connect(const std::string& host, const std::string& port,
|
||||
} catch (const std::exception& e) {
|
||||
last_connect_error_ = e.what();
|
||||
// Daemon warmup messages (Loading block index, Verifying blocks, etc.)
|
||||
// are normal startup progress — don't label them "Connection failed".
|
||||
// are normal startup progress — the daemon is reachable and auth works,
|
||||
// it just hasn't finished initializing yet. Mark as connected+warmup
|
||||
// so the wallet can show the UI instead of a blocking overlay.
|
||||
std::string msg = e.what();
|
||||
bool isWarmup = (msg.find("Loading") != std::string::npos ||
|
||||
msg.find("Verifying") != std::string::npos ||
|
||||
@@ -113,13 +127,19 @@ bool RPCClient::connect(const std::string& host, const std::string& port,
|
||||
msg.find("Rescanning") != std::string::npos ||
|
||||
msg.find("Pruning") != std::string::npos);
|
||||
if (isWarmup) {
|
||||
DEBUG_LOGF("Daemon starting: %s\n", msg.c_str());
|
||||
connected_ = true;
|
||||
warming_up_ = true;
|
||||
warmup_status_ = msg;
|
||||
DEBUG_LOGF("Daemon warming up: %s\n", msg.c_str());
|
||||
return true;
|
||||
} else {
|
||||
DEBUG_LOGF("Connection failed: %s\n", msg.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
connected_ = false;
|
||||
warming_up_ = false;
|
||||
warmup_status_.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -127,6 +147,8 @@ void RPCClient::disconnect()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lk(curl_mutex_);
|
||||
connected_ = false;
|
||||
warming_up_ = false;
|
||||
warmup_status_.clear();
|
||||
if (impl_->curl) {
|
||||
curl_easy_cleanup(impl_->curl);
|
||||
impl_->curl = nullptr;
|
||||
|
||||
@@ -53,6 +53,19 @@ public:
|
||||
*/
|
||||
bool isConnected() const { return connected_; }
|
||||
|
||||
/**
|
||||
* @brief True if the last connect() succeeded but daemon returned a warmup error.
|
||||
* The curl handle is valid and auth succeeded — RPC calls will throw warmup errors
|
||||
* until the daemon finishes initializing.
|
||||
*/
|
||||
bool isWarmingUp() const { return warming_up_; }
|
||||
|
||||
/**
|
||||
* @brief The warmup status message (e.g. "Activating best chain...").
|
||||
* Empty when not in warmup.
|
||||
*/
|
||||
const std::string& getWarmupStatus() const { return warmup_status_; }
|
||||
|
||||
/**
|
||||
* @brief Get the error message from the last failed connect() attempt.
|
||||
*/
|
||||
@@ -182,6 +195,8 @@ private:
|
||||
std::string port_;
|
||||
std::string auth_; // Base64 encoded "user:password"
|
||||
bool connected_ = false;
|
||||
bool warming_up_ = false;
|
||||
std::string warmup_status_;
|
||||
std::string last_connect_error_;
|
||||
mutable std::recursive_mutex curl_mutex_; // serializes all curl handle access
|
||||
|
||||
|
||||
+4
-4
@@ -33,13 +33,13 @@ I18n::I18n()
|
||||
// Cyrillic glyphs are included in the Ubuntu font glyph ranges.
|
||||
registerLanguage("en", "English");
|
||||
registerLanguage("es", "Español");
|
||||
registerLanguage("zh", "Chinese");
|
||||
registerLanguage("ru", "Russian");
|
||||
registerLanguage("zh", "中文");
|
||||
registerLanguage("ru", "Русский");
|
||||
registerLanguage("de", "Deutsch");
|
||||
registerLanguage("fr", "Français");
|
||||
registerLanguage("pt", "Português");
|
||||
registerLanguage("ja", "Japanese");
|
||||
registerLanguage("ko", "Korean");
|
||||
registerLanguage("ja", "日本語");
|
||||
registerLanguage("ko", "한국어");
|
||||
|
||||
// Load default English strings (built-in fallback)
|
||||
loadLanguage("en");
|
||||
|
||||
Reference in New Issue
Block a user