console and mining tab visual improvements
This commit is contained in:
@@ -84,8 +84,9 @@ void App::tryConnect()
|
||||
worker_->post([this, config, daemonStarting, externalDetected]() -> rpc::RPCWorker::MainCb {
|
||||
bool connected = rpc_->connect(config.host, config.port, config.rpcuser, config.rpcpassword);
|
||||
|
||||
return [this, connected, daemonStarting, externalDetected]() {
|
||||
return [this, config, connected, daemonStarting, externalDetected]() {
|
||||
if (connected) {
|
||||
saved_config_ = config; // save for fast-lane connection
|
||||
onConnected();
|
||||
} else {
|
||||
if (daemonStarting) {
|
||||
@@ -194,6 +195,29 @@ void App::onConnected()
|
||||
// Addresses are unknown on fresh connect — force a fetch
|
||||
addresses_dirty_ = true;
|
||||
|
||||
// Start the fast-lane RPC connection (dedicated to 1-second mining polls).
|
||||
// Uses its own curl handle + worker thread so getlocalsolps never blocks
|
||||
// behind the main refresh batch.
|
||||
if (!fast_rpc_) {
|
||||
fast_rpc_ = std::make_unique<rpc::RPCClient>();
|
||||
}
|
||||
if (!fast_worker_) {
|
||||
fast_worker_ = std::make_unique<rpc::RPCWorker>();
|
||||
fast_worker_->start();
|
||||
}
|
||||
// Connect on the fast worker's own thread (non-blocking to main)
|
||||
fast_worker_->post([this]() -> rpc::RPCWorker::MainCb {
|
||||
bool ok = fast_rpc_->connect(saved_config_.host, saved_config_.port,
|
||||
saved_config_.rpcuser, saved_config_.rpcpassword);
|
||||
return [ok]() {
|
||||
if (!ok) {
|
||||
DEBUG_LOGF("[FastLane] Failed to connect secondary RPC client\\n");
|
||||
} else {
|
||||
DEBUG_LOGF("[FastLane] Secondary RPC client connected\\n");
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Initial data refresh
|
||||
refreshData();
|
||||
refreshMarketData();
|
||||
@@ -204,6 +228,14 @@ void App::onDisconnected(const std::string& reason)
|
||||
state_.connected = false;
|
||||
state_.clear();
|
||||
connection_status_ = reason;
|
||||
|
||||
// Tear down the fast-lane connection
|
||||
if (fast_worker_) {
|
||||
fast_worker_->stop();
|
||||
}
|
||||
if (fast_rpc_) {
|
||||
fast_rpc_->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -217,35 +249,424 @@ void App::refreshData()
|
||||
// Prevent overlapping refreshes — skip if one is still running
|
||||
if (refresh_in_progress_.exchange(true)) return;
|
||||
|
||||
refreshBalance();
|
||||
// Capture decision flags on the main thread before posting to worker.
|
||||
const bool doAddresses = addresses_dirty_;
|
||||
const bool doPeers = (current_page_ == ui::NavPage::Peers);
|
||||
const bool doEncrypt = !encryption_state_prefetched_;
|
||||
if (encryption_state_prefetched_) encryption_state_prefetched_ = false;
|
||||
|
||||
// Addresses: only re-fetch when explicitly dirtied (new address, send, etc.)
|
||||
if (addresses_dirty_) {
|
||||
refreshAddresses();
|
||||
}
|
||||
|
||||
refreshTransactions();
|
||||
|
||||
// Mining: handled by the 1-second fast_refresh_timer_ — skip here to
|
||||
// avoid queuing a redundant call every 5 seconds.
|
||||
|
||||
// Peers: only fetch when the Peers tab is visible
|
||||
if (current_page_ == ui::NavPage::Peers) {
|
||||
refreshPeerInfo();
|
||||
}
|
||||
|
||||
// Encryption state: skip if onConnected() already prefetched it
|
||||
if (encryption_state_prefetched_) {
|
||||
encryption_state_prefetched_ = false;
|
||||
} else {
|
||||
refreshWalletEncryptionState();
|
||||
}
|
||||
// P4a: Skip transactions if no new blocks since last full fetch
|
||||
const int currentBlocks = state_.sync.blocks;
|
||||
const bool doTransactions = (last_tx_block_height_ < 0
|
||||
|| currentBlocks != last_tx_block_height_
|
||||
|| state_.transactions.empty());
|
||||
|
||||
// Clear the guard after all tasks are posted (they'll execute sequentially
|
||||
// on the worker thread, so the last one to finish signals completion).
|
||||
// We post a sentinel task that clears the flag after all refresh work.
|
||||
worker_->post([this]() -> rpc::RPCWorker::MainCb {
|
||||
return [this]() {
|
||||
// Snapshot z-addresses for transaction fetch (needed on worker thread)
|
||||
std::vector<std::string> txZAddrs;
|
||||
if (doTransactions) {
|
||||
for (const auto& za : state_.z_addresses) {
|
||||
if (!za.address.empty()) txZAddrs.push_back(za.address);
|
||||
}
|
||||
}
|
||||
|
||||
// P4b: Collect txids that are fully enriched (skip re-enrichment)
|
||||
std::set<std::string> fullyEnriched;
|
||||
if (doTransactions) {
|
||||
for (const auto& tx : state_.transactions) {
|
||||
if (tx.confirmations > 6 && tx.timestamp != 0) {
|
||||
fullyEnriched.insert(tx.txid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Single consolidated worker task — all RPC calls happen back-to-back
|
||||
// on a single thread with no inter-task queue overhead.
|
||||
worker_->post([this, doAddresses, doPeers, doEncrypt, doTransactions,
|
||||
currentBlocks, txZAddrs = std::move(txZAddrs),
|
||||
fullyEnriched = std::move(fullyEnriched)]() -> rpc::RPCWorker::MainCb {
|
||||
// ================================================================
|
||||
// Phase 1: Balance + blockchain info
|
||||
// ================================================================
|
||||
json totalBal, blockInfo;
|
||||
bool balOk = false, blockOk = false;
|
||||
|
||||
try {
|
||||
totalBal = rpc_->call("z_gettotalbalance");
|
||||
balOk = true;
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("Balance error: %s\n", e.what());
|
||||
}
|
||||
try {
|
||||
blockInfo = rpc_->call("getblockchaininfo");
|
||||
blockOk = true;
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("BlockchainInfo error: %s\n", e.what());
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Phase 2: Addresses (only when dirtied)
|
||||
// ================================================================
|
||||
std::vector<AddressInfo> zAddrs, tAddrs;
|
||||
bool addrOk = false;
|
||||
if (doAddresses) {
|
||||
addrOk = true;
|
||||
// z-addresses
|
||||
try {
|
||||
json zList = rpc_->call("z_listaddresses");
|
||||
for (const auto& addr : zList) {
|
||||
AddressInfo info;
|
||||
info.address = addr.get<std::string>();
|
||||
info.type = "shielded";
|
||||
zAddrs.push_back(info);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("z_listaddresses error: %s\n", e.what());
|
||||
}
|
||||
// z-balances via z_listunspent (single call)
|
||||
try {
|
||||
json unspent = rpc_->call("z_listunspent");
|
||||
std::map<std::string, double> zBalances;
|
||||
for (const auto& utxo : unspent) {
|
||||
if (utxo.contains("address") && utxo.contains("amount")) {
|
||||
zBalances[utxo["address"].get<std::string>()] += utxo["amount"].get<double>();
|
||||
}
|
||||
}
|
||||
for (auto& info : zAddrs) {
|
||||
auto it = zBalances.find(info.address);
|
||||
if (it != zBalances.end()) info.balance = it->second;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("z_listunspent unavailable (%s), falling back to z_getbalance\n", e.what());
|
||||
for (auto& info : zAddrs) {
|
||||
try {
|
||||
json bal = rpc_->call("z_getbalance", json::array({info.address}));
|
||||
if (!bal.is_null()) info.balance = bal.get<double>();
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
// t-addresses
|
||||
try {
|
||||
json tList = rpc_->call("getaddressesbyaccount", json::array({""}));
|
||||
for (const auto& addr : tList) {
|
||||
AddressInfo info;
|
||||
info.address = addr.get<std::string>();
|
||||
info.type = "transparent";
|
||||
tAddrs.push_back(info);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("getaddressesbyaccount error: %s\n", e.what());
|
||||
}
|
||||
// t-balances via listunspent
|
||||
try {
|
||||
json utxos = rpc_->call("listunspent");
|
||||
std::map<std::string, double> tBalances;
|
||||
for (const auto& utxo : utxos) {
|
||||
tBalances[utxo["address"].get<std::string>()] += utxo["amount"].get<double>();
|
||||
}
|
||||
for (auto& info : tAddrs) {
|
||||
auto it = tBalances.find(info.address);
|
||||
if (it != tBalances.end()) info.balance = it->second;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("listunspent error: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Phase 3: Transactions (only when new blocks)
|
||||
// ================================================================
|
||||
std::vector<TransactionInfo> txns;
|
||||
bool txOk = false;
|
||||
if (doTransactions) {
|
||||
txOk = true;
|
||||
std::set<std::string> knownTxids;
|
||||
|
||||
// Phase 3a: transparent transactions
|
||||
try {
|
||||
json result = rpc_->call("listtransactions", json::array({"", 9999}));
|
||||
for (const auto& tx : result) {
|
||||
TransactionInfo info;
|
||||
if (tx.contains("txid")) info.txid = tx["txid"].get<std::string>();
|
||||
if (tx.contains("category")) info.type = tx["category"].get<std::string>();
|
||||
if (tx.contains("amount")) info.amount = tx["amount"].get<double>();
|
||||
if (tx.contains("time")) info.timestamp = tx["time"].get<int64_t>();
|
||||
if (tx.contains("confirmations")) info.confirmations = tx["confirmations"].get<int>();
|
||||
if (tx.contains("address")) info.address = tx["address"].get<std::string>();
|
||||
knownTxids.insert(info.txid);
|
||||
txns.push_back(info);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("listtransactions error: %s\n", e.what());
|
||||
}
|
||||
|
||||
// Phase 3b: shielded receives
|
||||
for (const auto& addr : txZAddrs) {
|
||||
try {
|
||||
json zresult = rpc_->call("z_listreceivedbyaddress", json::array({addr, 0}));
|
||||
if (zresult.is_null() || !zresult.is_array()) continue;
|
||||
for (const auto& note : zresult) {
|
||||
std::string txid;
|
||||
if (note.contains("txid")) txid = note["txid"].get<std::string>();
|
||||
if (txid.empty()) continue;
|
||||
if (note.contains("change") && note["change"].get<bool>()) continue;
|
||||
bool dominated = false;
|
||||
for (const auto& existing : txns) {
|
||||
if (existing.txid == txid && existing.type == "receive") {
|
||||
dominated = true; break;
|
||||
}
|
||||
}
|
||||
if (dominated) continue;
|
||||
TransactionInfo info;
|
||||
info.txid = txid;
|
||||
info.type = "receive";
|
||||
info.address = addr;
|
||||
if (note.contains("amount")) info.amount = note["amount"].get<double>();
|
||||
if (note.contains("confirmations")) info.confirmations = note["confirmations"].get<int>();
|
||||
if (note.contains("time")) info.timestamp = note["time"].get<int64_t>();
|
||||
if (note.contains("memoStr")) info.memo = note["memoStr"].get<std::string>();
|
||||
knownTxids.insert(txid);
|
||||
txns.push_back(info);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("z_listreceivedbyaddress error for %s: %s\n",
|
||||
addr.substr(0, 12).c_str(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3c: detect shielded sends via z_viewtransaction
|
||||
for (const std::string& txid : knownTxids) {
|
||||
if (fullyEnriched.count(txid)) continue;
|
||||
try {
|
||||
json vtx = rpc_->call("z_viewtransaction", json::array({txid}));
|
||||
if (vtx.is_null() || !vtx.is_object()) continue;
|
||||
if (vtx.contains("outputs") && vtx["outputs"].is_array()) {
|
||||
for (const auto& output : vtx["outputs"]) {
|
||||
bool outgoing = false;
|
||||
if (output.contains("outgoing"))
|
||||
outgoing = output["outgoing"].get<bool>();
|
||||
if (!outgoing) continue;
|
||||
std::string destAddr;
|
||||
if (output.contains("address"))
|
||||
destAddr = output["address"].get<std::string>();
|
||||
double value = 0.0;
|
||||
if (output.contains("value"))
|
||||
value = output["value"].get<double>();
|
||||
bool alreadyTracked = false;
|
||||
for (const auto& existing : txns) {
|
||||
if (existing.txid == txid && existing.type == "send"
|
||||
&& std::abs(existing.amount + value) < 0.00000001) {
|
||||
alreadyTracked = true; break;
|
||||
}
|
||||
}
|
||||
if (alreadyTracked) continue;
|
||||
TransactionInfo info;
|
||||
info.txid = txid;
|
||||
info.type = "send";
|
||||
info.address = destAddr;
|
||||
info.amount = -value;
|
||||
if (output.contains("memoStr"))
|
||||
info.memo = output["memoStr"].get<std::string>();
|
||||
for (const auto& existing : txns) {
|
||||
if (existing.txid == txid) {
|
||||
info.confirmations = existing.confirmations;
|
||||
info.timestamp = existing.timestamp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (info.timestamp == 0) {
|
||||
try {
|
||||
json rawtx = rpc_->call("gettransaction", json::array({txid}));
|
||||
if (!rawtx.is_null() && rawtx.contains("time"))
|
||||
info.timestamp = rawtx["time"].get<int64_t>();
|
||||
if (!rawtx.is_null() && rawtx.contains("confirmations"))
|
||||
info.confirmations = rawtx["confirmations"].get<int>();
|
||||
} catch (...) {}
|
||||
}
|
||||
if (vtx.contains("spends") && vtx["spends"].is_array()) {
|
||||
for (const auto& spend : vtx["spends"]) {
|
||||
if (spend.contains("address")) {
|
||||
info.from_address = spend["address"].get<std::string>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
txns.push_back(info);
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
(void)e; // z_viewtransaction may not be available for all txids
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(txns.begin(), txns.end(),
|
||||
[](const TransactionInfo& a, const TransactionInfo& b) {
|
||||
return a.timestamp > b.timestamp;
|
||||
});
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Phase 4: Peers (only when tab is active)
|
||||
// ================================================================
|
||||
std::vector<PeerInfo> peers;
|
||||
std::vector<BannedPeer> bannedPeers;
|
||||
bool peerOk = false;
|
||||
if (doPeers) {
|
||||
peerOk = true;
|
||||
try {
|
||||
json result = rpc_->call("getpeerinfo");
|
||||
for (const auto& peer : result) {
|
||||
PeerInfo info;
|
||||
if (peer.contains("id")) info.id = peer["id"].get<int>();
|
||||
if (peer.contains("addr")) info.addr = peer["addr"].get<std::string>();
|
||||
if (peer.contains("subver")) info.subver = peer["subver"].get<std::string>();
|
||||
if (peer.contains("services")) info.services = peer["services"].get<std::string>();
|
||||
if (peer.contains("version")) info.version = peer["version"].get<int>();
|
||||
if (peer.contains("conntime")) info.conntime = peer["conntime"].get<int64_t>();
|
||||
if (peer.contains("banscore")) info.banscore = peer["banscore"].get<int>();
|
||||
if (peer.contains("pingtime")) info.pingtime = peer["pingtime"].get<double>();
|
||||
if (peer.contains("bytessent")) info.bytessent = peer["bytessent"].get<int64_t>();
|
||||
if (peer.contains("bytesrecv")) info.bytesrecv = peer["bytesrecv"].get<int64_t>();
|
||||
if (peer.contains("startingheight")) info.startingheight = peer["startingheight"].get<int>();
|
||||
if (peer.contains("synced_headers")) info.synced_headers = peer["synced_headers"].get<int>();
|
||||
if (peer.contains("synced_blocks")) info.synced_blocks = peer["synced_blocks"].get<int>();
|
||||
if (peer.contains("inbound")) info.inbound = peer["inbound"].get<bool>();
|
||||
if (peer.contains("tls_cipher")) info.tls_cipher = peer["tls_cipher"].get<std::string>();
|
||||
if (peer.contains("tls_verified")) info.tls_verified = peer["tls_verified"].get<bool>();
|
||||
peers.push_back(info);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("getPeerInfo error: %s\n", e.what());
|
||||
}
|
||||
try {
|
||||
json result = rpc_->call("listbanned");
|
||||
for (const auto& ban : result) {
|
||||
BannedPeer info;
|
||||
if (ban.contains("address")) info.address = ban["address"].get<std::string>();
|
||||
if (ban.contains("banned_until")) info.banned_until = ban["banned_until"].get<int64_t>();
|
||||
bannedPeers.push_back(info);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("listBanned error: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Phase 5: Wallet encryption state
|
||||
// ================================================================
|
||||
json walletInfo;
|
||||
bool encryptOk = false;
|
||||
if (doEncrypt) {
|
||||
try {
|
||||
walletInfo = rpc_->call("getwalletinfo");
|
||||
encryptOk = true;
|
||||
} catch (...) {}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Single main-thread callback — apply ALL results at once
|
||||
// ================================================================
|
||||
return [this, totalBal, blockInfo, balOk, blockOk,
|
||||
zAddrs = std::move(zAddrs), tAddrs = std::move(tAddrs), addrOk,
|
||||
txns = std::move(txns), txOk, currentBlocks,
|
||||
peers = std::move(peers), bannedPeers = std::move(bannedPeers), peerOk,
|
||||
walletInfo, encryptOk]() {
|
||||
// --- Balance ---
|
||||
try {
|
||||
if (balOk) {
|
||||
if (totalBal.contains("private"))
|
||||
state_.shielded_balance = std::stod(totalBal["private"].get<std::string>());
|
||||
if (totalBal.contains("transparent"))
|
||||
state_.transparent_balance = std::stod(totalBal["transparent"].get<std::string>());
|
||||
if (totalBal.contains("total"))
|
||||
state_.total_balance = std::stod(totalBal["total"].get<std::string>());
|
||||
state_.last_balance_update = std::time(nullptr);
|
||||
}
|
||||
if (blockOk) {
|
||||
if (blockInfo.contains("blocks"))
|
||||
state_.sync.blocks = blockInfo["blocks"].get<int>();
|
||||
if (blockInfo.contains("headers"))
|
||||
state_.sync.headers = blockInfo["headers"].get<int>();
|
||||
if (blockInfo.contains("verificationprogress"))
|
||||
state_.sync.verification_progress = blockInfo["verificationprogress"].get<double>();
|
||||
state_.sync.syncing = (state_.sync.blocks < state_.sync.headers - 2);
|
||||
if (blockInfo.contains("longestchain"))
|
||||
state_.longestchain = blockInfo["longestchain"].get<int>();
|
||||
if (blockInfo.contains("notarized"))
|
||||
state_.notarized = blockInfo["notarized"].get<int>();
|
||||
}
|
||||
// Auto-shield transparent funds if enabled
|
||||
if (balOk && settings_ && settings_->getAutoShield() &&
|
||||
state_.transparent_balance > 0.0001 && !state_.sync.syncing &&
|
||||
!auto_shield_pending_.exchange(true)) {
|
||||
std::string targetZAddr;
|
||||
for (const auto& addr : state_.addresses) {
|
||||
if (addr.isShielded()) {
|
||||
targetZAddr = addr.address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!targetZAddr.empty() && rpc_) {
|
||||
DEBUG_LOGF("[AutoShield] Shielding %.8f DRGX to %s\n",
|
||||
state_.transparent_balance, targetZAddr.c_str());
|
||||
rpc_->z_shieldCoinbase("*", targetZAddr, 0.0001, 50,
|
||||
[this](const json& result) {
|
||||
if (result.contains("opid")) {
|
||||
DEBUG_LOGF("[AutoShield] Started: %s\n",
|
||||
result["opid"].get<std::string>().c_str());
|
||||
}
|
||||
auto_shield_pending_ = false;
|
||||
},
|
||||
[this](const std::string& err) {
|
||||
DEBUG_LOGF("[AutoShield] Error: %s\n", err.c_str());
|
||||
auto_shield_pending_ = false;
|
||||
});
|
||||
} else {
|
||||
auto_shield_pending_ = false;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("[refreshData] balance callback error: %s\n", e.what());
|
||||
}
|
||||
|
||||
// --- Addresses ---
|
||||
if (addrOk) {
|
||||
state_.z_addresses = std::move(zAddrs);
|
||||
state_.t_addresses = std::move(tAddrs);
|
||||
address_list_dirty_ = true;
|
||||
addresses_dirty_ = false;
|
||||
}
|
||||
|
||||
// --- Transactions ---
|
||||
if (txOk) {
|
||||
state_.transactions = std::move(txns);
|
||||
state_.last_tx_update = std::time(nullptr);
|
||||
last_tx_block_height_ = currentBlocks;
|
||||
}
|
||||
|
||||
// --- Peers ---
|
||||
if (peerOk) {
|
||||
state_.peers = std::move(peers);
|
||||
state_.bannedPeers = std::move(bannedPeers);
|
||||
state_.last_peer_update = std::time(nullptr);
|
||||
}
|
||||
|
||||
// --- Encryption state ---
|
||||
if (encryptOk) {
|
||||
try {
|
||||
if (walletInfo.contains("unlocked_until")) {
|
||||
state_.encrypted = true;
|
||||
int64_t until = walletInfo["unlocked_until"].get<int64_t>();
|
||||
state_.unlocked_until = until;
|
||||
state_.locked = (until == 0);
|
||||
} else {
|
||||
state_.encrypted = false;
|
||||
state_.locked = false;
|
||||
state_.unlocked_until = 0;
|
||||
}
|
||||
state_.encryption_state_known = true;
|
||||
} catch (...) {}
|
||||
}
|
||||
|
||||
refresh_in_progress_.store(false, std::memory_order_release);
|
||||
};
|
||||
});
|
||||
@@ -605,7 +1026,12 @@ void App::refreshTransactions()
|
||||
|
||||
void App::refreshMiningInfo()
|
||||
{
|
||||
if (!worker_ || !rpc_) return;
|
||||
// Use the dedicated fast-lane worker + connection so mining polls
|
||||
// never block behind the main refresh batch. Falls back to the main
|
||||
// worker if the fast lane isn't ready yet (e.g. during initial connect).
|
||||
auto* w = (fast_worker_ && fast_worker_->isRunning()) ? fast_worker_.get() : worker_.get();
|
||||
auto* rpc = (fast_rpc_ && fast_rpc_->isConnected()) ? fast_rpc_.get() : rpc_.get();
|
||||
if (!w || !rpc) return;
|
||||
|
||||
// Prevent worker queue pileup — skip if previous refresh hasn't finished
|
||||
if (mining_refresh_in_progress_.exchange(true)) return;
|
||||
@@ -623,13 +1049,13 @@ void App::refreshMiningInfo()
|
||||
// p2p_port are static for the lifetime of a connection (set in onConnected).
|
||||
bool doSlowRefresh = (mining_slow_counter_++ % 5 == 0);
|
||||
|
||||
worker_->post([this, daemonMemMb, doSlowRefresh]() -> rpc::RPCWorker::MainCb {
|
||||
w->post([this, rpc, daemonMemMb, doSlowRefresh]() -> rpc::RPCWorker::MainCb {
|
||||
json miningInfo, localHashrateJson;
|
||||
bool miningOk = false, hashrateOk = false;
|
||||
|
||||
// Fast path: only getlocalsolps (single RPC call, ~1ms) — returns H/s (RandomX)
|
||||
try {
|
||||
localHashrateJson = rpc_->call("getlocalsolps");
|
||||
localHashrateJson = rpc->call("getlocalsolps");
|
||||
hashrateOk = true;
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("getLocalHashrate error: %s\n", e.what());
|
||||
@@ -638,7 +1064,7 @@ void App::refreshMiningInfo()
|
||||
// Slow path: getmininginfo every ~5s
|
||||
if (doSlowRefresh) {
|
||||
try {
|
||||
miningInfo = rpc_->call("getmininginfo");
|
||||
miningInfo = rpc->call("getmininginfo");
|
||||
miningOk = true;
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("getMiningInfo error: %s\n", e.what());
|
||||
|
||||
Reference in New Issue
Block a user