perf(net): hold wallet scans at connect, in the background and on tab entry
- Connect gate: after the node starts answering, wallet scans wait until the lock state is known and the node knows its tip (or the wallet has fewer than 500 transactions), at most 20 s. A node already behind latches the sync profile from the connect's getinfo. Meanwhile a list-only pass keeps the address list and the cached history up, and the session's first balance and per-address balances are read once even while catching up, so the wallet shows its last-synced values instead of 0. - Background: minimized, hidden, or covered without focus (3 s debounce, any input cancels) polls only the chain state every 15 s. Wallet scans, the mining poll, price, nudges and auto-lock interaction tracking pause; auto-lock now fires while minimized. Leaving a held profile marks every timer due again. - Tab entry marks the timers due instead of forcing scans; a stale list or a never-loaded history still loads on entry. - The scan entry points own the hard gates, failed enumerations keep the previous lists behind a retry hold, and failed balance calls never overwrite known balances with 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -122,6 +122,9 @@ inherits the GUI's environment.
|
||||
| `FAKE_STUCK_SHUTDOWN` | 0 | the Sep 29 orphan: hold the datadir lock, no listener, ignore SIGTERM, never exit |
|
||||
| `FAKE_WALLET_NOTES` | (off) | a note wallet: `addr:count:amount,...` shielded notes; enables `z_mergetoaddress` + its operations (dragonxd rules: confirmed unlocked notes, a batch locked on return, merged note next block) |
|
||||
| `FAKE_MERGE_SECS` | 6 | how long a `z_mergetoaddress` operation runs before it succeeds |
|
||||
| `FAKE_TIP_UNKNOWN_SECS` | 0 | `longestchain` reads 0 this long after `Done Loading` (a node that hasn't seen an UpdateTip yet) |
|
||||
| `FAKE_LONGEST_AHEAD` / `FAKE_CATCHUP_SECS` | 0 / 0 | the network tip (`longestchain`, `headers`) is N blocks ahead, for that long after `Done Loading` (0 = for good) |
|
||||
| `FAKE_TXCOUNT` | 42 | `getwalletinfo` `txcount` (the GUI's connect gate waits for the tip on wallets of 500+) |
|
||||
| `FAKE_METHODS_ALL` | 0 | `1` = one `call m=<method> p=<params>` event per request (the GUI's RPC cadence; the `methods` lines keep only the top 10 counts) |
|
||||
| `FAKE_HEIGHT`, `FAKE_REBUILD_TOTAL`, `FAKE_NOTES`, `FAKE_VERSION` | 3327145, 62583, 13632, 1030050 | cosmetic numbers |
|
||||
|
||||
|
||||
@@ -123,6 +123,10 @@ struct Config {
|
||||
bool stuckShutdown = false; // the Sep 29 orphan: datadir lock held, no listener, never exits
|
||||
bool mining = false; // internal miner on: `stop` must wait for cs_main first
|
||||
bool rebuildSilent = false; // hold cs_main like a rebuild but log no rebuild markers at all
|
||||
double tipUnknownSecs = 0; // longestchain reads 0 this long after "Done Loading" (no UpdateTip seen yet)
|
||||
long longestAhead = 0; // the network tip is this many blocks ahead: the node is behind...
|
||||
double catchupSecs = 0; // ...for this long after "Done Loading" (0 = for good)
|
||||
long txcount = 42; // getwalletinfo txcount
|
||||
};
|
||||
|
||||
Config g_cfg;
|
||||
@@ -272,6 +276,7 @@ void event(const std::string& name, const std::string& extra = "")
|
||||
std::mutex g_csMain; // the daemon's cs_main
|
||||
std::atomic<long> g_height{0};
|
||||
std::atomic<bool> g_warmup{true};
|
||||
std::atomic<long long> g_loadedAtMs{0}; // when warmup ended ("Done Loading")
|
||||
std::mutex g_warmupMx;
|
||||
std::string g_warmupStatus = "Loading block index...";
|
||||
std::atomic<bool> g_minerOn{false};
|
||||
@@ -602,19 +607,26 @@ Canned cannedReply(const std::string& m, const json& params)
|
||||
return params[0].is_string() ? params[0].get<std::string>() : params[0].dump();
|
||||
};
|
||||
|
||||
// FAKE_TIP_UNKNOWN_SECS / FAKE_LONGEST_AHEAD / FAKE_CATCHUP_SECS: what the node knows of the network tip.
|
||||
const double sinceLoaded = g_loadedAtMs.load() > 0 ? (unixMs() - g_loadedAtMs.load()) / 1000.0 : 0.0;
|
||||
const bool tipUnknown = g_loadedAtMs.load() == 0 || sinceLoaded < g_cfg.tipUnknownSecs;
|
||||
const long ahead = (g_cfg.catchupSecs > 0 && sinceLoaded >= g_cfg.catchupSecs) ? 0 : g_cfg.longestAhead;
|
||||
const long longest = tipUnknown ? 0 : h + ahead;
|
||||
const long headers = h + ahead;
|
||||
|
||||
if (m == "getinfo") {
|
||||
c.result = {{"version", g_cfg.version}, {"protocolversion", 170012}, {"walletversion", 60000},
|
||||
{"balance", 0.0}, {"blocks", h}, {"longestchain", h}, {"notarized", 0},
|
||||
{"balance", 0.0}, {"blocks", h}, {"longestchain", longest}, {"notarized", 0},
|
||||
{"tiptime", now}, {"difficulty", difficulty}, {"keypoololdest", now - 86400},
|
||||
{"keypoolsize", 101}, {"paytxfee", 0.0}, {"timeoffset", 0}, {"connections", 2},
|
||||
{"tls_connections", 2}, {"proxy", ""}, {"testnet", false}, {"relayfee", 0.000001},
|
||||
{"errors", ""}, {"name", "DRAGONX"}, {"p2pport", 21768}, {"rpcport", g_cfg.rpcPort}};
|
||||
} else if (m == "getblockchaininfo") {
|
||||
c.result = {{"chain", "main"}, {"blocks", h}, {"synced", true}, {"headers", h},
|
||||
c.result = {{"chain", "main"}, {"blocks", h}, {"synced", ahead == 0}, {"headers", headers},
|
||||
{"bestblockhash", fakeHash(h)}, {"difficulty", difficulty}, {"verificationprogress", 1.0},
|
||||
{"chainwork", std::string(48, '0') + "1f3a5c7e9b2d4f60"}, {"pruned", false},
|
||||
{"valuePools", json::array()}, {"softforks", json::array()}, {"upgrades", json::object()},
|
||||
{"longestchain", h}, {"notarized", 0}};
|
||||
{"longestchain", longest}, {"notarized", 0}};
|
||||
} else if (m == "getblockcount") {
|
||||
c.result = h;
|
||||
} else if (m == "getbestblockhash") {
|
||||
@@ -631,7 +643,7 @@ Canned cannedReply(const std::string& m, const json& params)
|
||||
{"size", 1765}};
|
||||
} else if (m == "getwalletinfo") {
|
||||
c.result = {{"walletversion", 60000}, {"balance", 0.0}, {"unconfirmed_balance", 0.0},
|
||||
{"immature_balance", 0.0}, {"txcount", 42}, {"keypoololdest", now - 86400},
|
||||
{"immature_balance", 0.0}, {"txcount", g_cfg.txcount}, {"keypoololdest", now - 86400},
|
||||
{"keypoolsize", 101}, {"paytxfee", 0.0}};
|
||||
} else if (m == "z_gettotalbalance") {
|
||||
c.result = {{"transparent", "0.00"}, {"private", "1234.5678"}, {"total", "1234.5678"}};
|
||||
@@ -1348,6 +1360,10 @@ void loadConfig(int argc, char** argv)
|
||||
g_cfg.noiseSecs = std::max(1.0, envDouble("FAKE_NOISE_SECS", g_cfg.noiseSecs));
|
||||
g_cfg.serverTimeoutSecs = std::max(1.0, envDouble("FAKE_SERVER_TIMEOUT_SECS", g_cfg.serverTimeoutSecs));
|
||||
g_cfg.prefill = envLong("FAKE_PREFILL", g_cfg.prefill);
|
||||
g_cfg.tipUnknownSecs = envDouble("FAKE_TIP_UNKNOWN_SECS", g_cfg.tipUnknownSecs);
|
||||
g_cfg.longestAhead = std::max(0L, envLong("FAKE_LONGEST_AHEAD", g_cfg.longestAhead));
|
||||
g_cfg.catchupSecs = envDouble("FAKE_CATCHUP_SECS", g_cfg.catchupSecs);
|
||||
g_cfg.txcount = envLong("FAKE_TXCOUNT", g_cfg.txcount);
|
||||
g_cfg.height = envLong("FAKE_HEIGHT", g_cfg.height);
|
||||
g_cfg.rebuildTotal = std::max(1L, envLong("FAKE_REBUILD_TOTAL", g_cfg.rebuildTotal));
|
||||
g_cfg.notes = envLong("FAKE_NOTES", g_cfg.notes);
|
||||
@@ -1469,6 +1485,7 @@ int main(int argc, char** argv)
|
||||
}
|
||||
if (initOk) {
|
||||
setInitMessage("Full Node Done Loading! :)");
|
||||
g_loadedAtMs = unixMs();
|
||||
g_warmup = false;
|
||||
event("warmup-end");
|
||||
tipThread = std::thread(updateTipLoop);
|
||||
|
||||
Reference in New Issue
Block a user